blob: b7a16ac9c3e4828c0caff6ab4eaee20541a9be44 [file] [log] [blame]
Shinichiro Hamaji1d545aa2015-06-23 15:29:13 +09001// Copyright 2015 Google Inc. All rights reserved
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090015package kati
Fumitoshi Ukai4a708512015-06-11 17:15:49 +090016
17import (
Fumitoshi Ukai0547db62015-07-29 16:20:59 +090018 "errors"
Fumitoshi Ukai44ae8cf2015-06-24 16:44:15 +090019 "fmt"
Fumitoshi Ukai0547db62015-07-29 16:20:59 +090020 "io"
Fumitoshi Ukai4a708512015-06-11 17:15:49 +090021 "strings"
Fumitoshi Ukai44ae8cf2015-06-24 16:44:15 +090022 "time"
Fumitoshi Ukai4a708512015-06-11 17:15:49 +090023)
24
25var shBuiltins = []struct {
26 name string
Fumitoshi Ukai55c8fa92015-06-25 15:56:10 +090027 pattern expr
Fumitoshi Ukai4a708512015-06-11 17:15:49 +090028 compact func(*funcShell, []Value) Value
29}{
30 {
31 name: "android:rot13",
32 // in repo/android/build/core/definisions.mk
33 // echo $(1) | tr 'a-zA-Z' 'n-za-mN-ZA-M'
Fumitoshi Ukai55c8fa92015-06-25 15:56:10 +090034 pattern: expr{
Fumitoshi Ukai4a708512015-06-11 17:15:49 +090035 literal("echo "),
36 matchVarref{},
37 literal(" | tr 'a-zA-Z' 'n-za-mN-ZA-M'"),
38 },
39 compact: func(sh *funcShell, matches []Value) Value {
40 return &funcShellAndroidRot13{
41 funcShell: sh,
42 v: matches[0],
43 }
44 },
45 },
46 {
Fumitoshi Ukai44ae8cf2015-06-24 16:44:15 +090047 name: "shell-date",
Fumitoshi Ukai55c8fa92015-06-25 15:56:10 +090048 pattern: expr{
Fumitoshi Ukai44ae8cf2015-06-24 16:44:15 +090049 mustLiteralRE(`date \+(\S+)`),
50 },
51 compact: compactShellDate,
52 },
53 {
54 name: "shell-date-quoted",
Fumitoshi Ukai55c8fa92015-06-25 15:56:10 +090055 pattern: expr{
Fumitoshi Ukai44ae8cf2015-06-24 16:44:15 +090056 mustLiteralRE(`date "\+([^"]+)"`),
57 },
58 compact: compactShellDate,
59 },
Fumitoshi Ukai4a708512015-06-11 17:15:49 +090060}
61
62type funcShellAndroidRot13 struct {
63 *funcShell
64 v Value
65}
66
67func rot13(buf []byte) {
68 for i, b := range buf {
69 // tr 'a-zA-Z' 'n-za-mN-ZA-M'
70 if b >= 'a' && b <= 'z' {
71 b += 'n' - 'a'
72 if b > 'z' {
73 b -= 'z' - 'a' + 1
74 }
75 } else if b >= 'A' && b <= 'Z' {
76 b += 'N' - 'A'
77 if b > 'Z' {
78 b -= 'Z' - 'A' + 1
79 }
80 }
81 buf[i] = b
82 }
83}
84
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +090085func (f *funcShellAndroidRot13) Eval(w evalWriter, ev *Evaluator) error {
Fumitoshi Ukaia4a02252015-07-09 14:25:18 +090086 abuf := newEbuf()
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090087 fargs, err := ev.args(abuf, f.v)
88 if err != nil {
89 return err
90 }
Fumitoshi Ukai4a708512015-06-11 17:15:49 +090091 rot13(fargs[0])
92 w.Write(fargs[0])
Fumitoshi Ukaia4a02252015-07-09 14:25:18 +090093 abuf.release()
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090094 return nil
Fumitoshi Ukai4a708512015-06-11 17:15:49 +090095}
96
Fumitoshi Ukai44ae8cf2015-06-24 16:44:15 +090097var (
Fumitoshi Ukai65c72332015-06-26 21:32:50 +090098 // ShellDateTimestamp is an timestamp used for $(shell date).
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +090099 ShellDateTimestamp time.Time
Fumitoshi Ukai44ae8cf2015-06-24 16:44:15 +0900100 shellDateFormatRef = map[string]string{
101 "%Y": "2006",
102 "%m": "01",
103 "%d": "02",
104 "%H": "15",
105 "%M": "04",
106 "%S": "05",
107 "%b": "Jan",
108 "%k": "15", // XXX
109 }
110)
111
112type funcShellDate struct {
113 *funcShell
114 format string
115}
116
117func compactShellDate(sh *funcShell, v []Value) Value {
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900118 if ShellDateTimestamp.IsZero() {
Fumitoshi Ukai44ae8cf2015-06-24 16:44:15 +0900119 return sh
120 }
121 tf, ok := v[0].(literal)
122 if !ok {
123 return sh
124 }
125 tfstr := string(tf)
126 for k, v := range shellDateFormatRef {
127 tfstr = strings.Replace(tfstr, k, v, -1)
128 }
129 return &funcShellDate{
130 funcShell: sh,
131 format: tfstr,
132 }
133}
134
Fumitoshi Ukaib44b12d2015-07-07 14:19:32 +0900135func (f *funcShellDate) Eval(w evalWriter, ev *Evaluator) error {
Fumitoshi Ukai744bb2b2015-06-25 00:10:52 +0900136 fmt.Fprint(w, ShellDateTimestamp.Format(f.format))
Fumitoshi Ukai65c72332015-06-26 21:32:50 +0900137 return nil
Fumitoshi Ukai44ae8cf2015-06-24 16:44:15 +0900138}
Fumitoshi Ukai0547db62015-07-29 16:20:59 +0900139
140type buildinCommand interface {
141 run(w evalWriter)
142}
143
144var errFindEmulatorDisabled = errors.New("builtin: find emulator disabled")
145
146func parseBuiltinCommand(cmd string) (buildinCommand, error) {
147 if !UseFindEmulator {
148 return nil, errFindEmulatorDisabled
149 }
Fumitoshi Ukaif8e4baf2015-08-10 15:40:51 +0900150 if strings.HasPrefix(trimLeftSpace(cmd), "build/tools/findleaves") {
Fumitoshi Ukai0547db62015-07-29 16:20:59 +0900151 return parseFindleavesCommand(cmd)
152 }
153 return parseFindCommand(cmd)
154}
155
156type shellParser struct {
157 cmd string
158 ungetToken string
159}
160
161func (p *shellParser) token() (string, error) {
162 if p.ungetToken != "" {
163 tok := p.ungetToken
164 p.ungetToken = ""
165 return tok, nil
166 }
167 p.cmd = trimLeftSpace(p.cmd)
168 if len(p.cmd) == 0 {
169 return "", io.EOF
170 }
171 if p.cmd[0] == ';' {
172 tok := p.cmd[0:1]
173 p.cmd = p.cmd[1:]
174 return tok, nil
175 }
176 if p.cmd[0] == '&' {
177 if len(p.cmd) == 1 || p.cmd[1] != '&' {
178 return "", errFindBackground
179 }
180 tok := p.cmd[0:2]
181 p.cmd = p.cmd[2:]
182 return tok, nil
183 }
184 // TODO(ukai): redirect token.
185 i := 0
186 for i < len(p.cmd) {
187 if isWhitespace(rune(p.cmd[i])) || p.cmd[i] == ';' || p.cmd[i] == '&' {
188 break
189 }
190 i++
191 }
192 tok := p.cmd[0:i]
193 p.cmd = p.cmd[i:]
194 c := tok[0]
195 if c == '\'' || c == '"' {
196 if len(tok) < 2 || tok[len(tok)-1] != c {
197 return "", errFindUnbalancedQuote
198 }
199 // todo: unquote?
200 tok = tok[1 : len(tok)-1]
201 }
202 return tok, nil
203}
204
205func (p *shellParser) unget(s string) {
206 if s != "" {
207 p.ungetToken = s
208 }
209}
210
211func (p *shellParser) expect(toks ...string) error {
212 tok, err := p.token()
213 if err != nil {
214 return err
215 }
216 for _, t := range toks {
217 if tok == t {
218 return nil
219 }
220 }
221 return fmt.Errorf("shell: token=%q; want=%q", tok, toks)
222}
223
224func (p *shellParser) expectSeq(toks ...string) error {
225 for _, tok := range toks {
226 err := p.expect(tok)
227 if err != nil {
228 return err
229 }
230 }
231 return nil
232}