blob: 39d282b669c700e939a9fe0d51ae0c37079cac24 [file] [log] [blame]
Shinichiro Hamajib69bf8a2015-06-10 14:52:06 +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
Shinichiro Hamaji04932612015-03-31 23:46:56 +090016
17import (
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +090018 "bytes"
Shinichiro Hamaji5e2c3c72015-04-03 15:04:54 +090019 "path/filepath"
Shinichiro Hamaji04932612015-03-31 23:46:56 +090020 "strings"
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +090021
22 "github.com/golang/glog"
Shinichiro Hamaji04932612015-03-31 23:46:56 +090023)
24
Fumitoshi Ukai9ae81d02015-06-08 09:32:38 +090025var wsbytes = [256]bool{' ': true, '\t': true, '\n': true, '\r': true}
26
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090027// TODO(ukai): use unicode.IsSpace?
28func isWhitespace(ch rune) bool {
Fumitoshi Ukai9ae81d02015-06-08 09:32:38 +090029 if int(ch) >= len(wsbytes) {
30 return false
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090031 }
Fumitoshi Ukai9ae81d02015-06-08 09:32:38 +090032 return wsbytes[ch]
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090033}
34
Shinichiro Hamaji04932612015-03-31 23:46:56 +090035func splitSpaces(s string) []string {
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090036 var r []string
37 tokStart := -1
38 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090039 if isWhitespace(ch) {
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090040 if tokStart >= 0 {
41 r = append(r, s[tokStart:i])
42 tokStart = -1
43 }
44 } else {
45 if tokStart < 0 {
46 tokStart = i
47 }
48 }
Shinichiro Hamaji04932612015-03-31 23:46:56 +090049 }
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090050 if tokStart >= 0 {
51 r = append(r, s[tokStart:])
52 }
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +090053 glog.V(2).Infof("splitSpace(%q)=%q", s, r)
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090054 return r
Shinichiro Hamaji04932612015-03-31 23:46:56 +090055}
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +090056
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +090057func splitSpacesBytes(s []byte) (r [][]byte) {
58 tokStart := -1
59 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090060 if isWhitespace(rune(ch)) {
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +090061 if tokStart >= 0 {
62 r = append(r, s[tokStart:i])
63 tokStart = -1
64 }
65 } else {
66 if tokStart < 0 {
67 tokStart = i
68 }
69 }
70 }
71 if tokStart >= 0 {
72 r = append(r, s[tokStart:])
73 }
Fumitoshi Ukai6450d0f2015-07-10 16:34:06 +090074 glog.V(2).Infof("splitSpace(%q)=%q", s, r)
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +090075 return r
76}
77
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +090078// TODO(ukai): use bufio.Scanner?
79type wordScanner struct {
Fumitoshi Ukai4b3a7dd2015-07-21 13:07:44 +090080 in []byte
81 s int // word starts
82 i int // current pos
83 esc bool // handle \-escape
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +090084}
85
86func newWordScanner(in []byte) *wordScanner {
87 return &wordScanner{
88 in: in,
89 }
90}
91
Fumitoshi Ukaib97be672015-07-02 15:12:48 +090092func (ws *wordScanner) next() bool {
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +090093 for ws.s = ws.i; ws.s < len(ws.in); ws.s++ {
Fumitoshi Ukai9ae81d02015-06-08 09:32:38 +090094 if !wsbytes[ws.in[ws.s]] {
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +090095 break
96 }
97 }
98 if ws.s == len(ws.in) {
99 return false
100 }
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900101 return true
102}
103
104func (ws *wordScanner) Scan() bool {
105 if !ws.next() {
106 return false
107 }
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900108 for ws.i = ws.s; ws.i < len(ws.in); ws.i++ {
Fumitoshi Ukai4b3a7dd2015-07-21 13:07:44 +0900109 if ws.esc && ws.in[ws.i] == '\\' {
110 ws.i++
111 continue
112 }
Fumitoshi Ukai9ae81d02015-06-08 09:32:38 +0900113 if wsbytes[ws.in[ws.i]] {
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900114 break
115 }
116 }
117 return true
118}
119
120func (ws *wordScanner) Bytes() []byte {
121 return ws.in[ws.s:ws.i]
122}
123
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900124func (ws *wordScanner) Remain() []byte {
125 if !ws.next() {
126 return nil
127 }
128 return ws.in[ws.s:]
129}
130
Fumitoshi Ukai0822c412015-04-05 22:51:18 +0900131func matchPattern(pat, str string) bool {
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900132 i := strings.IndexByte(pat, '%')
133 if i < 0 {
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900134 return pat == str
135 }
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900136 return strings.HasPrefix(str, pat[:i]) && strings.HasSuffix(str, pat[i+1:])
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900137}
138
Shinichiro Hamaji05b222d2015-04-11 11:36:37 +0900139func matchPatternBytes(pat, str []byte) bool {
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900140 i := bytes.IndexByte(pat, '%')
141 if i < 0 {
Shinichiro Hamaji05b222d2015-04-11 11:36:37 +0900142 return bytes.Equal(pat, str)
143 }
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900144 return bytes.HasPrefix(str, pat[:i]) && bytes.HasSuffix(str, pat[i+1:])
Shinichiro Hamaji05b222d2015-04-11 11:36:37 +0900145}
146
Fumitoshi Ukai0822c412015-04-05 22:51:18 +0900147func substPattern(pat, repl, str string) string {
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900148 ps := strings.SplitN(pat, "%", 2)
149 if len(ps) != 2 {
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900150 if str == pat {
151 return repl
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900152 }
153 return str
154 }
155 in := str
156 trimed := str
157 if ps[0] != "" {
158 trimed = strings.TrimPrefix(in, ps[0])
159 if trimed == in {
160 return str
161 }
162 }
163 in = trimed
164 if ps[1] != "" {
165 trimed = strings.TrimSuffix(in, ps[1])
166 if trimed == in {
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900167 return str
168 }
169 }
170
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900171 rs := strings.SplitN(repl, "%", 2)
172 if len(rs) != 2 {
173 return repl
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900174 }
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900175 return rs[0] + trimed + rs[1]
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900176}
Shinichiro Hamaji5e2c3c72015-04-03 15:04:54 +0900177
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900178func substPatternBytes(pat, repl, str []byte) (pre, subst, post []byte) {
179 i := bytes.IndexByte(pat, '%')
180 if i < 0 {
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900181 if bytes.Equal(str, pat) {
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900182 return repl, nil, nil
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900183 }
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900184 return str, nil, nil
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900185 }
186 in := str
187 trimed := str
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900188 if i > 0 {
189 trimed = bytes.TrimPrefix(in, pat[:i])
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900190 if bytes.Equal(trimed, in) {
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900191 return str, nil, nil
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900192 }
193 }
194 in = trimed
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900195 if i < len(pat)-1 {
196 trimed = bytes.TrimSuffix(in, pat[i+1:])
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900197 if bytes.Equal(trimed, in) {
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900198 return str, nil, nil
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900199 }
200 }
201
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900202 i = bytes.IndexByte(repl, '%')
203 if i < 0 {
204 return repl, nil, nil
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900205 }
206
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900207 return repl[:i], trimed, repl[i+1:]
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900208}
209
Fumitoshi Ukai0822c412015-04-05 22:51:18 +0900210func substRef(pat, repl, str string) string {
211 if strings.IndexByte(pat, '%') >= 0 && strings.IndexByte(repl, '%') >= 0 {
212 return substPattern(pat, repl, str)
213 }
214 str = strings.TrimSuffix(str, pat)
215 return str + repl
216}
217
Shinichiro Hamaji5e2c3c72015-04-03 15:04:54 +0900218func stripExt(s string) string {
219 suf := filepath.Ext(s)
220 return s[:len(s)-len(suf)]
221}
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900222
223func trimLeftSpace(s string) string {
224 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900225 if !isWhitespace(ch) {
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900226 return s[i:]
227 }
228 }
229 return ""
230}
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900231
232func trimLeftSpaceBytes(s []byte) []byte {
233 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900234 if !isWhitespace(rune(ch)) {
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900235 return s[i:]
236 }
237 }
238 return nil
239}
Shinichiro Hamajidad80532015-04-21 17:55:50 +0900240
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900241func trimRightSpaceBytes(s []byte) []byte {
242 for i := len(s) - 1; i >= 0; i-- {
243 ch := s[i]
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900244 if !isWhitespace(rune(ch)) {
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900245 return s[:i+1]
246 }
247 }
248 return nil
249}
250
251func trimSpaceBytes(s []byte) []byte {
252 s = trimLeftSpaceBytes(s)
253 return trimRightSpaceBytes(s)
254}
255
Shinichiro Hamajidad80532015-04-21 17:55:50 +0900256// Strip leading sequences of './' from file names, so that ./file
257// and file are considered to be the same file.
258// From http://www.gnu.org/software/make/manual/make.html#Features
259func trimLeadingCurdir(s string) string {
260 for strings.HasPrefix(s, "./") {
261 s = s[2:]
262 }
263 return s
264}
Fumitoshi Ukai72508a72015-05-08 13:41:31 +0900265
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900266func contains(list []string, s string) bool {
267 for _, v := range list {
268 if v == s {
269 return true
270 }
271 }
272 return false
273}
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900274
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900275func firstWord(line []byte) ([]byte, []byte) {
276 s := newWordScanner(line)
277 if s.Scan() {
278 w := s.Bytes()
279 return w, s.Remain()
280 }
281 return line, nil
282}
283
Fumitoshi Ukai6addc2f2015-07-22 11:33:35 +0900284type findCharOption int
285
286const (
287 noSkipVar findCharOption = iota
288 skipVar
289)
290
291func findLiteralChar(s []byte, stop1, stop2 byte, op findCharOption) int {
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900292 i := 0
293 for {
294 var ch byte
295 for i < len(s) {
296 ch = s[i]
297 if ch == '\\' {
298 i += 2
299 continue
300 }
Fumitoshi Ukai1394d102015-07-08 16:45:49 +0900301 if ch == stop1 {
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900302 break
303 }
Fumitoshi Ukai1394d102015-07-08 16:45:49 +0900304 if ch == stop2 {
305 break
306 }
Fumitoshi Ukai6addc2f2015-07-22 11:33:35 +0900307 if op == skipVar && ch == '$' {
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900308 break
309 }
310 i++
311 }
312 if i >= len(s) {
313 return -1
314 }
315 if ch == '$' {
316 i++
317 if i == len(s) {
318 return -1
319 }
320 oparen := s[i]
321 cparen := closeParen(oparen)
322 i++
323 if cparen != 0 {
324 pcount := 1
325 SkipParen:
326 for i < len(s) {
327 ch = s[i]
328 switch ch {
329 case oparen:
330 pcount++
331 case cparen:
332 pcount--
333 if pcount == 0 {
334 i++
335 break SkipParen
336 }
337 }
338 i++
339 }
340 }
341 continue
342 }
343 return i
344 }
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900345}
346
347func removeComment(line []byte) ([]byte, bool) {
348 var buf []byte
349 for i := 0; i < len(line); i++ {
350 if line[i] != '#' {
351 continue
352 }
353 b := 1
354 for ; i-b >= 0; b++ {
355 if line[i-b] != '\\' {
356 break
357 }
358 }
359 b++
360 nb := b / 2
361 quoted := b%2 == 1
362 if buf == nil {
363 buf = make([]byte, len(line))
364 copy(buf, line)
365 line = buf
366 }
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900367 line = append(line[:i-b+nb+1], line[i:]...)
368 if !quoted {
369 return line[:i-b+nb+1], true
370 }
371 i = i - nb + 1
372 }
373 return line, false
374}
375
Fumitoshi Ukai95ff7602015-07-07 16:28:10 +0900376// cmdline removes tab at the beginning of lines.
377func cmdline(line string) string {
378 buf := []byte(line)
379 for i := 0; i < len(buf); i++ {
380 if buf[i] == '\n' && i+1 < len(buf) && buf[i+1] == '\t' {
381 copy(buf[i+1:], buf[i+2:])
382 buf = buf[:len(buf)-1]
383 }
384 }
385 return string(buf)
386}
387
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900388// concatline removes backslash newline.
389// TODO: backslash baskslash newline becomes backslash newline.
390func concatline(line []byte) []byte {
391 var buf []byte
392 for i := 0; i < len(line); i++ {
393 if line[i] != '\\' {
394 continue
395 }
396 if i+1 == len(line) {
Fumitoshi Ukai2a2deb32015-07-08 11:46:14 +0900397 if line[i-1] != '\\' {
398 line = line[:i]
399 }
Fumitoshi Ukaib97be672015-07-02 15:12:48 +0900400 break
401 }
402 if line[i+1] == '\n' {
403 if buf == nil {
404 buf = make([]byte, len(line))
405 copy(buf, line)
406 line = buf
407 }
408 oline := trimRightSpaceBytes(line[:i])
409 oline = append(oline, ' ')
410 nextline := trimLeftSpaceBytes(line[i+2:])
411 line = append(oline, nextline...)
412 i = len(oline) - 1
413 continue
414 }
415 if i+2 < len(line) && line[i+1] == '\r' && line[i+2] == '\n' {
416 if buf == nil {
417 buf = make([]byte, len(line))
418 copy(buf, line)
419 line = buf
420 }
421 oline := trimRightSpaceBytes(line[:i])
422 oline = append(oline, ' ')
423 nextline := trimLeftSpaceBytes(line[i+3:])
424 line = append(oline, nextline...)
425 i = len(oline) - 1
426 continue
427 }
428 }
429 return line
430}