blob: 08ed5494f104ed24ceb9b34e1d7065478b69ed9b [file] [log] [blame]
Shinichiro Hamaji04932612015-03-31 23:46:56 +09001package main
2
3import (
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +09004 "bytes"
Shinichiro Hamaji5e2c3c72015-04-03 15:04:54 +09005 "path/filepath"
Shinichiro Hamaji04932612015-03-31 23:46:56 +09006 "strings"
7)
8
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +09009// TODO(ukai): use unicode.IsSpace?
10func isWhitespace(ch rune) bool {
11 switch ch {
12 case ' ', '\t', '\n', '\r':
13 return true
14 }
15 return false
16}
17
Shinichiro Hamaji04932612015-03-31 23:46:56 +090018func splitSpaces(s string) []string {
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090019 var r []string
20 tokStart := -1
21 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090022 if isWhitespace(ch) {
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090023 if tokStart >= 0 {
24 r = append(r, s[tokStart:i])
25 tokStart = -1
26 }
27 } else {
28 if tokStart < 0 {
29 tokStart = i
30 }
31 }
Shinichiro Hamaji04932612015-03-31 23:46:56 +090032 }
Shinichiro Hamaji248e7542015-04-09 18:12:06 +090033 if tokStart >= 0 {
34 r = append(r, s[tokStart:])
35 }
36 Log("splitSpace(%q)=%q", s, r)
37 return r
Shinichiro Hamaji04932612015-03-31 23:46:56 +090038}
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +090039
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +090040func splitSpacesBytes(s []byte) (r [][]byte) {
41 tokStart := -1
42 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090043 if isWhitespace(rune(ch)) {
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +090044 if tokStart >= 0 {
45 r = append(r, s[tokStart:i])
46 tokStart = -1
47 }
48 } else {
49 if tokStart < 0 {
50 tokStart = i
51 }
52 }
53 }
54 if tokStart >= 0 {
55 r = append(r, s[tokStart:])
56 }
57 Log("splitSpace(%q)=%q", s, r)
58 return r
59}
60
Fumitoshi Ukai9da19f62015-05-08 14:39:08 +090061// TODO(ukai): use bufio.Scanner?
62type wordScanner struct {
63 in []byte
64 s int // word starts
65 i int // current pos
66}
67
68func newWordScanner(in []byte) *wordScanner {
69 return &wordScanner{
70 in: in,
71 }
72}
73
74func (ws *wordScanner) Scan() bool {
75 for ws.s = ws.i; ws.s < len(ws.in); ws.s++ {
76 ch := rune(ws.in[ws.s])
77 if !isWhitespace(ch) {
78 break
79 }
80 }
81 if ws.s == len(ws.in) {
82 return false
83 }
84 for ws.i = ws.s; ws.i < len(ws.in); ws.i++ {
85 ch := rune(ws.in[ws.i])
86 if isWhitespace(ch) {
87 break
88 }
89 }
90 return true
91}
92
93func (ws *wordScanner) Bytes() []byte {
94 return ws.in[ws.s:ws.i]
95}
96
Fumitoshi Ukai0822c412015-04-05 22:51:18 +090097func matchPattern(pat, str string) bool {
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +090098 i := strings.IndexByte(pat, '%')
99 if i < 0 {
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900100 return pat == str
101 }
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900102 return strings.HasPrefix(str, pat[:i]) && strings.HasSuffix(str, pat[i+1:])
Shinichiro Hamaji9b7d4622015-04-02 01:37:04 +0900103}
104
Shinichiro Hamaji05b222d2015-04-11 11:36:37 +0900105func matchPatternBytes(pat, str []byte) bool {
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900106 i := bytes.IndexByte(pat, '%')
107 if i < 0 {
Shinichiro Hamaji05b222d2015-04-11 11:36:37 +0900108 return bytes.Equal(pat, str)
109 }
Shinichiro Hamaji3796d1e2015-04-12 01:59:15 +0900110 return bytes.HasPrefix(str, pat[:i]) && bytes.HasSuffix(str, pat[i+1:])
Shinichiro Hamaji05b222d2015-04-11 11:36:37 +0900111}
112
Fumitoshi Ukai0822c412015-04-05 22:51:18 +0900113func substPattern(pat, repl, str string) string {
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900114 ps := strings.SplitN(pat, "%", 2)
115 if len(ps) != 2 {
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900116 if str == pat {
117 return repl
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900118 }
119 return str
120 }
121 in := str
122 trimed := str
123 if ps[0] != "" {
124 trimed = strings.TrimPrefix(in, ps[0])
125 if trimed == in {
126 return str
127 }
128 }
129 in = trimed
130 if ps[1] != "" {
131 trimed = strings.TrimSuffix(in, ps[1])
132 if trimed == in {
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900133 return str
134 }
135 }
136
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900137 rs := strings.SplitN(repl, "%", 2)
138 if len(rs) != 2 {
139 return repl
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900140 }
Fumitoshi Ukaic3fe5f62015-04-02 13:19:07 +0900141 return rs[0] + trimed + rs[1]
Shinichiro Hamajic5686fd2015-04-01 03:30:34 +0900142}
Shinichiro Hamaji5e2c3c72015-04-03 15:04:54 +0900143
Shinichiro Hamaji98e910d2015-04-11 10:38:44 +0900144func substPatternBytes(pat, repl, str []byte) []byte {
145 ps := bytes.SplitN(pat, []byte{'%'}, 2)
146 if len(ps) != 2 {
147 if bytes.Equal(str, pat) {
148 return repl
149 }
150 return str
151 }
152 in := str
153 trimed := str
154 if len(ps[0]) != 0 {
155 trimed = bytes.TrimPrefix(in, ps[0])
156 if bytes.Equal(trimed, in) {
157 return str
158 }
159 }
160 in = trimed
161 if len(ps[1]) != 0 {
162 trimed = bytes.TrimSuffix(in, ps[1])
163 if bytes.Equal(trimed, in) {
164 return str
165 }
166 }
167
168 rs := bytes.SplitN(repl, []byte{'%'}, 2)
169 if len(rs) != 2 {
170 return repl
171 }
172
173 r := make([]byte, 0, len(rs[0])+len(trimed)+len(rs[1])+1)
174 r = append(r, rs[0]...)
175 r = append(r, trimed...)
176 r = append(r, rs[1]...)
177 return r
178}
179
Fumitoshi Ukai0822c412015-04-05 22:51:18 +0900180func substRef(pat, repl, str string) string {
181 if strings.IndexByte(pat, '%') >= 0 && strings.IndexByte(repl, '%') >= 0 {
182 return substPattern(pat, repl, str)
183 }
184 str = strings.TrimSuffix(str, pat)
185 return str + repl
186}
187
Shinichiro Hamaji5e2c3c72015-04-03 15:04:54 +0900188func stripExt(s string) string {
189 suf := filepath.Ext(s)
190 return s[:len(s)-len(suf)]
191}
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900192
193func trimLeftSpace(s string) string {
194 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900195 if !isWhitespace(ch) {
Shinichiro Hamajic88618f2015-04-11 19:58:06 +0900196 return s[i:]
197 }
198 }
199 return ""
200}
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900201
202func trimLeftSpaceBytes(s []byte) []byte {
203 for i, ch := range s {
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900204 if !isWhitespace(rune(ch)) {
Shinichiro Hamajic4c98102015-04-12 03:56:00 +0900205 return s[i:]
206 }
207 }
208 return nil
209}
Shinichiro Hamajidad80532015-04-21 17:55:50 +0900210
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900211func trimRightSpaceBytes(s []byte) []byte {
212 for i := len(s) - 1; i >= 0; i-- {
213 ch := s[i]
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900214 if !isWhitespace(rune(ch)) {
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900215 return s[:i+1]
216 }
217 }
218 return nil
219}
220
221func trimSpaceBytes(s []byte) []byte {
222 s = trimLeftSpaceBytes(s)
223 return trimRightSpaceBytes(s)
224}
225
Shinichiro Hamajidad80532015-04-21 17:55:50 +0900226// Strip leading sequences of './' from file names, so that ./file
227// and file are considered to be the same file.
228// From http://www.gnu.org/software/make/manual/make.html#Features
229func trimLeadingCurdir(s string) string {
230 for strings.HasPrefix(s, "./") {
231 s = s[2:]
232 }
233 return s
234}
Fumitoshi Ukai72508a72015-05-08 13:41:31 +0900235
236func reverse(s string) string {
237 // TODO(ukai): support UTF-8?
238 r := make([]byte, len(s))
239 for i := 0; i < len(s); i++ {
240 r[i] = s[len(s)-1-i]
241 }
242 return string(r)
243}