blob: d59d5c5b4f6c0ba10feafd2b38a19c75326205eb [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"
21)
22
Fumitoshi Ukai9ae81d02015-06-08 09:32:38 +090023var wsbytes = [256]bool{' ': true, '\t': true, '\n': true, '\r': true}
24
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090025// TODO(ukai): use unicode.IsSpace?
26func isWhitespace(ch rune) bool {
Fumitoshi Ukai9ae81d02015-06-08 09:32:38 +090027 if int(ch) >= len(wsbytes) {
28 return false
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090029 }
Fumitoshi Ukai9ae81d02015-06-08 09:32:38 +090030 return wsbytes[ch]
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090031}
32
Shinichiro Hamaji04932612015-03-31 23:46:56 +090033func splitSpaces(s string) []string {
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090034 var r []string
35 tokStart := -1
36 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090037 if isWhitespace(ch) {
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090038 if tokStart >= 0 {
39 r = append(r, s[tokStart:i])
40 tokStart = -1
41 }
42 } else {
43 if tokStart < 0 {
44 tokStart = i
45 }
46 }
Shinichiro Hamaji04932612015-03-31 23:46:56 +090047 }
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090048 if tokStart >= 0 {
49 r = append(r, s[tokStart:])
50 }
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +090051 logf("splitSpace(%q)=%q", s, r)
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090052 return r
Shinichiro Hamaji04932612015-03-31 23:46:56 +090053}
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +090054
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +090055func splitSpacesBytes(s []byte) (r [][]byte) {
56 tokStart := -1
57 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090058 if isWhitespace(rune(ch)) {
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +090059 if tokStart >= 0 {
60 r = append(r, s[tokStart:i])
61 tokStart = -1
62 }
63 } else {
64 if tokStart < 0 {
65 tokStart = i
66 }
67 }
68 }
69 if tokStart >= 0 {
70 r = append(r, s[tokStart:])
71 }
Fumitoshi Ukai07cf1212015-06-25 17:16:25 +090072 logf("splitSpace(%q)=%q", s, r)
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +090073 return r
74}
75
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +090076// TODO(ukai): use bufio.Scanner?
77type wordScanner struct {
78 in []byte
79 s int // word starts
80 i int // current pos
81}
82
83func newWordScanner(in []byte) *wordScanner {
84 return &wordScanner{
85 in: in,
86 }
87}
88
89func (ws *wordScanner) Scan() bool {
90 for ws.s = ws.i; ws.s < len(ws.in); ws.s++ {
Fumitoshi Ukai9ae81d02015-06-08 09:32:38 +090091 if !wsbytes[ws.in[ws.s]] {
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +090092 break
93 }
94 }
95 if ws.s == len(ws.in) {
96 return false
97 }
98 for ws.i = ws.s; ws.i < len(ws.in); ws.i++ {
Fumitoshi Ukai9ae81d02015-06-08 09:32:38 +090099 if wsbytes[ws.in[ws.i]] {
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +0900100 break
101 }
102 }
103 return true
104}
105
106func (ws *wordScanner) Bytes() []byte {
107 return ws.in[ws.s:ws.i]
108}
109
Fumitoshi Ukai0822c412015-04-05 22:51:18 +0900110func matchPattern(pat, str string) bool {
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900111 i := strings.IndexByte(pat, '%')
112 if i < 0 {
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900113 return pat == str
114 }
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900115 return strings.HasPrefix(str, pat[:i]) && strings.HasSuffix(str, pat[i+1:])
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900116}
117
Shinichiro Hamaji05b222d2015-04-11 11:36:37 +0900118func matchPatternBytes(pat, str []byte) bool {
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900119 i := bytes.IndexByte(pat, '%')
120 if i < 0 {
Shinichiro Hamaji05b222d2015-04-11 11:36:37 +0900121 return bytes.Equal(pat, str)
122 }
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900123 return bytes.HasPrefix(str, pat[:i]) && bytes.HasSuffix(str, pat[i+1:])
Shinichiro Hamaji05b222d2015-04-11 11:36:37 +0900124}
125
Fumitoshi Ukai0822c412015-04-05 22:51:18 +0900126func substPattern(pat, repl, str string) string {
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900127 ps := strings.SplitN(pat, "%", 2)
128 if len(ps) != 2 {
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900129 if str == pat {
130 return repl
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900131 }
132 return str
133 }
134 in := str
135 trimed := str
136 if ps[0] != "" {
137 trimed = strings.TrimPrefix(in, ps[0])
138 if trimed == in {
139 return str
140 }
141 }
142 in = trimed
143 if ps[1] != "" {
144 trimed = strings.TrimSuffix(in, ps[1])
145 if trimed == in {
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900146 return str
147 }
148 }
149
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900150 rs := strings.SplitN(repl, "%", 2)
151 if len(rs) != 2 {
152 return repl
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900153 }
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900154 return rs[0] + trimed + rs[1]
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900155}
Shinichiro Hamaji5e2c3c72015-04-03 15:04:54 +0900156
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900157func substPatternBytes(pat, repl, str []byte) (pre, subst, post []byte) {
158 i := bytes.IndexByte(pat, '%')
159 if i < 0 {
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900160 if bytes.Equal(str, pat) {
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900161 return repl, nil, nil
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900162 }
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900163 return str, nil, nil
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900164 }
165 in := str
166 trimed := str
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900167 if i > 0 {
168 trimed = bytes.TrimPrefix(in, pat[:i])
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900169 if bytes.Equal(trimed, in) {
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900170 return str, nil, nil
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900171 }
172 }
173 in = trimed
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900174 if i < len(pat)-1 {
175 trimed = bytes.TrimSuffix(in, pat[i+1:])
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900176 if bytes.Equal(trimed, in) {
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900177 return str, nil, nil
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900178 }
179 }
180
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900181 i = bytes.IndexByte(repl, '%')
182 if i < 0 {
183 return repl, nil, nil
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900184 }
185
Fumitoshi Ukai77411fb2015-06-19 15:46:21 +0900186 return repl[:i], trimed, repl[i+1:]
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900187}
188
Fumitoshi Ukai0822c412015-04-05 22:51:18 +0900189func substRef(pat, repl, str string) string {
190 if strings.IndexByte(pat, '%') >= 0 && strings.IndexByte(repl, '%') >= 0 {
191 return substPattern(pat, repl, str)
192 }
193 str = strings.TrimSuffix(str, pat)
194 return str + repl
195}
196
Shinichiro Hamaji5e2c3c72015-04-03 15:04:54 +0900197func stripExt(s string) string {
198 suf := filepath.Ext(s)
199 return s[:len(s)-len(suf)]
200}
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900201
202func trimLeftSpace(s string) string {
203 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900204 if !isWhitespace(ch) {
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900205 return s[i:]
206 }
207 }
208 return ""
209}
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900210
211func trimLeftSpaceBytes(s []byte) []byte {
212 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900213 if !isWhitespace(rune(ch)) {
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900214 return s[i:]
215 }
216 }
217 return nil
218}
Shinichiro Hamajidad80532015-04-21 17:55:50 +0900219
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900220func trimRightSpaceBytes(s []byte) []byte {
221 for i := len(s) - 1; i >= 0; i-- {
222 ch := s[i]
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900223 if !isWhitespace(rune(ch)) {
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900224 return s[:i+1]
225 }
226 }
227 return nil
228}
229
230func trimSpaceBytes(s []byte) []byte {
231 s = trimLeftSpaceBytes(s)
232 return trimRightSpaceBytes(s)
233}
234
Shinichiro Hamajidad80532015-04-21 17:55:50 +0900235// Strip leading sequences of './' from file names, so that ./file
236// and file are considered to be the same file.
237// From http://www.gnu.org/software/make/manual/make.html#Features
238func trimLeadingCurdir(s string) string {
239 for strings.HasPrefix(s, "./") {
240 s = s[2:]
241 }
242 return s
243}
Fumitoshi Ukai72508a72015-05-08 13:41:31 +0900244
Fumitoshi Ukai83410132015-06-15 14:50:07 +0900245func contains(list []string, s string) bool {
246 for _, v := range list {
247 if v == s {
248 return true
249 }
250 }
251 return false
252}