Shinichiro Hamaji | b69bf8a | 2015-06-10 14:52:06 +0900 | [diff] [blame] | 1 | // 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 Ukai | 744bb2b | 2015-06-25 00:10:52 +0900 | [diff] [blame] | 15 | package kati |
Shinichiro Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 16 | |
| 17 | import ( |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 18 | "bytes" |
Shinichiro Hamaji | 5e2c3c7 | 2015-04-03 15:04:54 +0900 | [diff] [blame] | 19 | "path/filepath" |
Shinichiro Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 20 | "strings" |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 21 | |
| 22 | "github.com/golang/glog" |
Shinichiro Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 23 | ) |
| 24 | |
Fumitoshi Ukai | 9ae81d0 | 2015-06-08 09:32:38 +0900 | [diff] [blame] | 25 | var wsbytes = [256]bool{' ': true, '\t': true, '\n': true, '\r': true} |
| 26 | |
Fumitoshi Ukai | b06cd9d | 2015-05-07 12:56:12 +0900 | [diff] [blame] | 27 | // TODO(ukai): use unicode.IsSpace? |
| 28 | func isWhitespace(ch rune) bool { |
Fumitoshi Ukai | 9ae81d0 | 2015-06-08 09:32:38 +0900 | [diff] [blame] | 29 | if int(ch) >= len(wsbytes) { |
| 30 | return false |
Fumitoshi Ukai | b06cd9d | 2015-05-07 12:56:12 +0900 | [diff] [blame] | 31 | } |
Fumitoshi Ukai | 9ae81d0 | 2015-06-08 09:32:38 +0900 | [diff] [blame] | 32 | return wsbytes[ch] |
Fumitoshi Ukai | b06cd9d | 2015-05-07 12:56:12 +0900 | [diff] [blame] | 33 | } |
| 34 | |
Shinichiro Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 35 | func splitSpaces(s string) []string { |
Shinichiro Hamaji | 248e754 | 2015-04-09 18:12:06 +0900 | [diff] [blame] | 36 | var r []string |
| 37 | tokStart := -1 |
| 38 | for i, ch := range s { |
Fumitoshi Ukai | b06cd9d | 2015-05-07 12:56:12 +0900 | [diff] [blame] | 39 | if isWhitespace(ch) { |
Shinichiro Hamaji | 248e754 | 2015-04-09 18:12:06 +0900 | [diff] [blame] | 40 | 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 Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 49 | } |
Shinichiro Hamaji | 248e754 | 2015-04-09 18:12:06 +0900 | [diff] [blame] | 50 | if tokStart >= 0 { |
| 51 | r = append(r, s[tokStart:]) |
| 52 | } |
Fumitoshi Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 53 | glog.V(2).Infof("splitSpace(%q)=%q", s, r) |
Shinichiro Hamaji | 248e754 | 2015-04-09 18:12:06 +0900 | [diff] [blame] | 54 | return r |
Shinichiro Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 55 | } |
Shinichiro Hamaji | c5686fd | 2015-04-01 03:30:34 +0900 | [diff] [blame] | 56 | |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 57 | func splitSpacesBytes(s []byte) (r [][]byte) { |
| 58 | tokStart := -1 |
| 59 | for i, ch := range s { |
Fumitoshi Ukai | b06cd9d | 2015-05-07 12:56:12 +0900 | [diff] [blame] | 60 | if isWhitespace(rune(ch)) { |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 61 | 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 Ukai | 6450d0f | 2015-07-10 16:34:06 +0900 | [diff] [blame] | 74 | glog.V(2).Infof("splitSpace(%q)=%q", s, r) |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 75 | return r |
| 76 | } |
| 77 | |
Fumitoshi Ukai | 9da19f6 | 2015-05-08 14:39:08 +0900 | [diff] [blame] | 78 | // TODO(ukai): use bufio.Scanner? |
| 79 | type wordScanner struct { |
Fumitoshi Ukai | 4b3a7dd | 2015-07-21 13:07:44 +0900 | [diff] [blame] | 80 | in []byte |
| 81 | s int // word starts |
| 82 | i int // current pos |
| 83 | esc bool // handle \-escape |
Fumitoshi Ukai | 9da19f6 | 2015-05-08 14:39:08 +0900 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | func newWordScanner(in []byte) *wordScanner { |
| 87 | return &wordScanner{ |
| 88 | in: in, |
| 89 | } |
| 90 | } |
| 91 | |
Fumitoshi Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 92 | func (ws *wordScanner) next() bool { |
Fumitoshi Ukai | 9da19f6 | 2015-05-08 14:39:08 +0900 | [diff] [blame] | 93 | for ws.s = ws.i; ws.s < len(ws.in); ws.s++ { |
Fumitoshi Ukai | 9ae81d0 | 2015-06-08 09:32:38 +0900 | [diff] [blame] | 94 | if !wsbytes[ws.in[ws.s]] { |
Fumitoshi Ukai | 9da19f6 | 2015-05-08 14:39:08 +0900 | [diff] [blame] | 95 | break |
| 96 | } |
| 97 | } |
| 98 | if ws.s == len(ws.in) { |
| 99 | return false |
| 100 | } |
Fumitoshi Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 101 | return true |
| 102 | } |
| 103 | |
| 104 | func (ws *wordScanner) Scan() bool { |
| 105 | if !ws.next() { |
| 106 | return false |
| 107 | } |
Fumitoshi Ukai | 9da19f6 | 2015-05-08 14:39:08 +0900 | [diff] [blame] | 108 | for ws.i = ws.s; ws.i < len(ws.in); ws.i++ { |
Fumitoshi Ukai | 4b3a7dd | 2015-07-21 13:07:44 +0900 | [diff] [blame] | 109 | if ws.esc && ws.in[ws.i] == '\\' { |
| 110 | ws.i++ |
| 111 | continue |
| 112 | } |
Fumitoshi Ukai | 9ae81d0 | 2015-06-08 09:32:38 +0900 | [diff] [blame] | 113 | if wsbytes[ws.in[ws.i]] { |
Fumitoshi Ukai | 9da19f6 | 2015-05-08 14:39:08 +0900 | [diff] [blame] | 114 | break |
| 115 | } |
| 116 | } |
| 117 | return true |
| 118 | } |
| 119 | |
| 120 | func (ws *wordScanner) Bytes() []byte { |
| 121 | return ws.in[ws.s:ws.i] |
| 122 | } |
| 123 | |
Fumitoshi Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 124 | func (ws *wordScanner) Remain() []byte { |
| 125 | if !ws.next() { |
| 126 | return nil |
| 127 | } |
| 128 | return ws.in[ws.s:] |
| 129 | } |
| 130 | |
Fumitoshi Ukai | 0822c41 | 2015-04-05 22:51:18 +0900 | [diff] [blame] | 131 | func matchPattern(pat, str string) bool { |
Shinichiro Hamaji | 3796d1e | 2015-04-12 01:59:15 +0900 | [diff] [blame] | 132 | i := strings.IndexByte(pat, '%') |
| 133 | if i < 0 { |
Shinichiro Hamaji | 9b7d462 | 2015-04-02 01:37:04 +0900 | [diff] [blame] | 134 | return pat == str |
| 135 | } |
Shinichiro Hamaji | 3796d1e | 2015-04-12 01:59:15 +0900 | [diff] [blame] | 136 | return strings.HasPrefix(str, pat[:i]) && strings.HasSuffix(str, pat[i+1:]) |
Shinichiro Hamaji | 9b7d462 | 2015-04-02 01:37:04 +0900 | [diff] [blame] | 137 | } |
| 138 | |
Shinichiro Hamaji | 05b222d | 2015-04-11 11:36:37 +0900 | [diff] [blame] | 139 | func matchPatternBytes(pat, str []byte) bool { |
Shinichiro Hamaji | 3796d1e | 2015-04-12 01:59:15 +0900 | [diff] [blame] | 140 | i := bytes.IndexByte(pat, '%') |
| 141 | if i < 0 { |
Shinichiro Hamaji | 05b222d | 2015-04-11 11:36:37 +0900 | [diff] [blame] | 142 | return bytes.Equal(pat, str) |
| 143 | } |
Shinichiro Hamaji | 3796d1e | 2015-04-12 01:59:15 +0900 | [diff] [blame] | 144 | return bytes.HasPrefix(str, pat[:i]) && bytes.HasSuffix(str, pat[i+1:]) |
Shinichiro Hamaji | 05b222d | 2015-04-11 11:36:37 +0900 | [diff] [blame] | 145 | } |
| 146 | |
Fumitoshi Ukai | 0822c41 | 2015-04-05 22:51:18 +0900 | [diff] [blame] | 147 | func substPattern(pat, repl, str string) string { |
Fumitoshi Ukai | c3fe5f6 | 2015-04-02 13:19:07 +0900 | [diff] [blame] | 148 | ps := strings.SplitN(pat, "%", 2) |
| 149 | if len(ps) != 2 { |
Shinichiro Hamaji | c5686fd | 2015-04-01 03:30:34 +0900 | [diff] [blame] | 150 | if str == pat { |
| 151 | return repl |
Fumitoshi Ukai | c3fe5f6 | 2015-04-02 13:19:07 +0900 | [diff] [blame] | 152 | } |
| 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 Hamaji | c5686fd | 2015-04-01 03:30:34 +0900 | [diff] [blame] | 167 | return str |
| 168 | } |
| 169 | } |
| 170 | |
Fumitoshi Ukai | c3fe5f6 | 2015-04-02 13:19:07 +0900 | [diff] [blame] | 171 | rs := strings.SplitN(repl, "%", 2) |
| 172 | if len(rs) != 2 { |
| 173 | return repl |
Shinichiro Hamaji | c5686fd | 2015-04-01 03:30:34 +0900 | [diff] [blame] | 174 | } |
Fumitoshi Ukai | c3fe5f6 | 2015-04-02 13:19:07 +0900 | [diff] [blame] | 175 | return rs[0] + trimed + rs[1] |
Shinichiro Hamaji | c5686fd | 2015-04-01 03:30:34 +0900 | [diff] [blame] | 176 | } |
Shinichiro Hamaji | 5e2c3c7 | 2015-04-03 15:04:54 +0900 | [diff] [blame] | 177 | |
Fumitoshi Ukai | 77411fb | 2015-06-19 15:46:21 +0900 | [diff] [blame] | 178 | func substPatternBytes(pat, repl, str []byte) (pre, subst, post []byte) { |
| 179 | i := bytes.IndexByte(pat, '%') |
| 180 | if i < 0 { |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 181 | if bytes.Equal(str, pat) { |
Fumitoshi Ukai | 77411fb | 2015-06-19 15:46:21 +0900 | [diff] [blame] | 182 | return repl, nil, nil |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 183 | } |
Fumitoshi Ukai | 77411fb | 2015-06-19 15:46:21 +0900 | [diff] [blame] | 184 | return str, nil, nil |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 185 | } |
| 186 | in := str |
| 187 | trimed := str |
Fumitoshi Ukai | 77411fb | 2015-06-19 15:46:21 +0900 | [diff] [blame] | 188 | if i > 0 { |
| 189 | trimed = bytes.TrimPrefix(in, pat[:i]) |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 190 | if bytes.Equal(trimed, in) { |
Fumitoshi Ukai | 77411fb | 2015-06-19 15:46:21 +0900 | [diff] [blame] | 191 | return str, nil, nil |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 192 | } |
| 193 | } |
| 194 | in = trimed |
Fumitoshi Ukai | 77411fb | 2015-06-19 15:46:21 +0900 | [diff] [blame] | 195 | if i < len(pat)-1 { |
| 196 | trimed = bytes.TrimSuffix(in, pat[i+1:]) |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 197 | if bytes.Equal(trimed, in) { |
Fumitoshi Ukai | 77411fb | 2015-06-19 15:46:21 +0900 | [diff] [blame] | 198 | return str, nil, nil |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 199 | } |
| 200 | } |
| 201 | |
Fumitoshi Ukai | 77411fb | 2015-06-19 15:46:21 +0900 | [diff] [blame] | 202 | i = bytes.IndexByte(repl, '%') |
| 203 | if i < 0 { |
| 204 | return repl, nil, nil |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 205 | } |
| 206 | |
Fumitoshi Ukai | 77411fb | 2015-06-19 15:46:21 +0900 | [diff] [blame] | 207 | return repl[:i], trimed, repl[i+1:] |
Shinichiro Hamaji | 98e910d | 2015-04-11 10:38:44 +0900 | [diff] [blame] | 208 | } |
| 209 | |
Fumitoshi Ukai | 0822c41 | 2015-04-05 22:51:18 +0900 | [diff] [blame] | 210 | func 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 Hamaji | 5e2c3c7 | 2015-04-03 15:04:54 +0900 | [diff] [blame] | 218 | func stripExt(s string) string { |
| 219 | suf := filepath.Ext(s) |
| 220 | return s[:len(s)-len(suf)] |
| 221 | } |
Shinichiro Hamaji | c88618f | 2015-04-11 19:58:06 +0900 | [diff] [blame] | 222 | |
| 223 | func trimLeftSpace(s string) string { |
| 224 | for i, ch := range s { |
Fumitoshi Ukai | b06cd9d | 2015-05-07 12:56:12 +0900 | [diff] [blame] | 225 | if !isWhitespace(ch) { |
Shinichiro Hamaji | c88618f | 2015-04-11 19:58:06 +0900 | [diff] [blame] | 226 | return s[i:] |
| 227 | } |
| 228 | } |
| 229 | return "" |
| 230 | } |
Shinichiro Hamaji | c4c9810 | 2015-04-12 03:56:00 +0900 | [diff] [blame] | 231 | |
| 232 | func trimLeftSpaceBytes(s []byte) []byte { |
| 233 | for i, ch := range s { |
Fumitoshi Ukai | b06cd9d | 2015-05-07 12:56:12 +0900 | [diff] [blame] | 234 | if !isWhitespace(rune(ch)) { |
Shinichiro Hamaji | c4c9810 | 2015-04-12 03:56:00 +0900 | [diff] [blame] | 235 | return s[i:] |
| 236 | } |
| 237 | } |
| 238 | return nil |
| 239 | } |
Shinichiro Hamaji | dad8053 | 2015-04-21 17:55:50 +0900 | [diff] [blame] | 240 | |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame] | 241 | func trimRightSpaceBytes(s []byte) []byte { |
| 242 | for i := len(s) - 1; i >= 0; i-- { |
| 243 | ch := s[i] |
Fumitoshi Ukai | b06cd9d | 2015-05-07 12:56:12 +0900 | [diff] [blame] | 244 | if !isWhitespace(rune(ch)) { |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame] | 245 | return s[:i+1] |
| 246 | } |
| 247 | } |
| 248 | return nil |
| 249 | } |
| 250 | |
| 251 | func trimSpaceBytes(s []byte) []byte { |
| 252 | s = trimLeftSpaceBytes(s) |
| 253 | return trimRightSpaceBytes(s) |
| 254 | } |
| 255 | |
Shinichiro Hamaji | dad8053 | 2015-04-21 17:55:50 +0900 | [diff] [blame] | 256 | // 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 |
| 259 | func trimLeadingCurdir(s string) string { |
| 260 | for strings.HasPrefix(s, "./") { |
| 261 | s = s[2:] |
| 262 | } |
| 263 | return s |
| 264 | } |
Fumitoshi Ukai | 72508a7 | 2015-05-08 13:41:31 +0900 | [diff] [blame] | 265 | |
Fumitoshi Ukai | 8341013 | 2015-06-15 14:50:07 +0900 | [diff] [blame] | 266 | func 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 Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 274 | |
Fumitoshi Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 275 | func 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 Ukai | 6addc2f | 2015-07-22 11:33:35 +0900 | [diff] [blame] | 284 | type findCharOption int |
| 285 | |
| 286 | const ( |
| 287 | noSkipVar findCharOption = iota |
| 288 | skipVar |
| 289 | ) |
| 290 | |
| 291 | func findLiteralChar(s []byte, stop1, stop2 byte, op findCharOption) int { |
Fumitoshi Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 292 | 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 Ukai | 1394d10 | 2015-07-08 16:45:49 +0900 | [diff] [blame] | 301 | if ch == stop1 { |
Fumitoshi Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 302 | break |
| 303 | } |
Fumitoshi Ukai | 1394d10 | 2015-07-08 16:45:49 +0900 | [diff] [blame] | 304 | if ch == stop2 { |
| 305 | break |
| 306 | } |
Fumitoshi Ukai | 6addc2f | 2015-07-22 11:33:35 +0900 | [diff] [blame] | 307 | if op == skipVar && ch == '$' { |
Fumitoshi Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 308 | 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 Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 345 | } |
| 346 | |
| 347 | func 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 Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 367 | 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 Ukai | 95ff760 | 2015-07-07 16:28:10 +0900 | [diff] [blame] | 376 | // cmdline removes tab at the beginning of lines. |
| 377 | func 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 Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 388 | // concatline removes backslash newline. |
| 389 | // TODO: backslash baskslash newline becomes backslash newline. |
| 390 | func 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 Ukai | 2a2deb3 | 2015-07-08 11:46:14 +0900 | [diff] [blame] | 397 | if line[i-1] != '\\' { |
| 398 | line = line[:i] |
| 399 | } |
Fumitoshi Ukai | b97be67 | 2015-07-02 15:12:48 +0900 | [diff] [blame] | 400 | 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 | } |