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
15 // +build ignore
16
17 #include "strutil.h"
18
19 #include <ctype.h>
20 #include <limits.h>
21 #include <unistd.h>
22
23 #include <algorithm>
24 #include <functional>
25 #include <stack>
26 #include <utility>
27
28 #ifdef __SSE4_2__
29 #include <smmintrin.h>
30 #endif
31
32 #include "log.h"
33
isSpace(char c)34 static bool isSpace(char c) {
35 return (9 <= c && c <= 13) || c == 32;
36 }
37
38 #ifdef __SSE4_2__
SkipUntilSSE42(const char * s,int len,const char * ranges,int ranges_size)39 static int SkipUntilSSE42(const char* s, int len,
40 const char* ranges, int ranges_size) {
41 __m128i ranges16 = _mm_loadu_si128((const __m128i*)ranges);
42 len &= ~15;
43 int i = 0;
44 while (i < len) {
45 __m128i b16 = _mm_loadu_si128((const __m128i*)(s + i));
46 int r = _mm_cmpestri(
47 ranges16, ranges_size, b16, len - i,
48 _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
49 if (r != 16) {
50 return i + r;
51 }
52 i += 16;
53 }
54 return len;
55 }
56 #endif
57
58 template <typename Cond>
SkipUntil(const char * s,int len,const char * ranges UNUSED,int ranges_size UNUSED,Cond cond)59 static int SkipUntil(const char* s, int len,
60 const char* ranges UNUSED, int ranges_size UNUSED,
61 Cond cond) {
62 int i = 0;
63 #ifdef __SSE4_2__
64 i += SkipUntilSSE42(s, len, ranges, ranges_size);
65 #endif
66 for (; i < len; i++) {
67 if (cond(s[i]))
68 break;
69 }
70 return i;
71 }
72
operator ++()73 WordScanner::Iterator& WordScanner::Iterator::operator++() {
74 int len = static_cast<int>(in->size());
75 for (s = i + 1; s < len; s++) {
76 if (!isSpace((*in)[s]))
77 break;
78 }
79 if (s >= len) {
80 in = NULL;
81 s = 0;
82 i = 0;
83 return *this;
84 }
85
86 static const char ranges[] = "\x09\x0d ";
87 // It's intentional we are not using isSpace here. It seems with
88 // lambda the compiler generates better code.
89 i = s + SkipUntil(in->data() + s, len - s, ranges, 4,
90 [](char c) { return (9 <= c && c <= 13) || c == 32; });
91 return *this;
92 }
93
operator *() const94 StringPiece WordScanner::Iterator::operator*() const {
95 return in->substr(s, i - s);
96 }
97
WordScanner(StringPiece in)98 WordScanner::WordScanner(StringPiece in)
99 : in_(in) {
100 }
101
begin() const102 WordScanner::Iterator WordScanner::begin() const {
103 Iterator iter;
104 iter.in = &in_;
105 iter.s = 0;
106 iter.i = -1;
107 ++iter;
108 return iter;
109 }
110
end() const111 WordScanner::Iterator WordScanner::end() const {
112 Iterator iter;
113 iter.in = NULL;
114 iter.s = 0;
115 iter.i = 0;
116 return iter;
117 }
118
Split(vector<StringPiece> * o)119 void WordScanner::Split(vector<StringPiece>* o) {
120 for (StringPiece t : *this)
121 o->push_back(t);
122 }
123
WordWriter(string * o)124 WordWriter::WordWriter(string* o)
125 : out_(o),
126 needs_space_(false) {
127 }
128
MaybeAddWhitespace()129 void WordWriter::MaybeAddWhitespace() {
130 if (needs_space_) {
131 out_->push_back(' ');
132 } else {
133 needs_space_ = true;
134 }
135 }
136
Write(StringPiece s)137 void WordWriter::Write(StringPiece s) {
138 MaybeAddWhitespace();
139 AppendString(s, out_);
140 }
141
ScopedTerminator(StringPiece s)142 ScopedTerminator::ScopedTerminator(StringPiece s)
143 : s_(s), c_(s[s.size()]) {
144 const_cast<char*>(s_.data())[s_.size()] = '\0';
145 }
146
~ScopedTerminator()147 ScopedTerminator::~ScopedTerminator() {
148 const_cast<char*>(s_.data())[s_.size()] = c_;
149 }
150
AppendString(StringPiece str,string * out)151 void AppendString(StringPiece str, string* out) {
152 out->append(str.begin(), str.end());
153 }
154
HasPrefix(StringPiece str,StringPiece prefix)155 bool HasPrefix(StringPiece str, StringPiece prefix) {
156 ssize_t size_diff = str.size() - prefix.size();
157 return size_diff >= 0 && str.substr(0, prefix.size()) == prefix;
158 }
159
HasSuffix(StringPiece str,StringPiece suffix)160 bool HasSuffix(StringPiece str, StringPiece suffix) {
161 ssize_t size_diff = str.size() - suffix.size();
162 return size_diff >= 0 && str.substr(size_diff) == suffix;
163 }
164
HasWord(StringPiece str,StringPiece w)165 bool HasWord(StringPiece str, StringPiece w) {
166 size_t found = str.find(w);
167 if (found == string::npos)
168 return false;
169 if (found != 0 && !isSpace(str[found-1]))
170 return false;
171 size_t end = found + w.size();
172 if (end != str.size() && !isSpace(str[end]))
173 return false;
174 return true;
175 }
176
TrimPrefix(StringPiece str,StringPiece prefix)177 StringPiece TrimPrefix(StringPiece str, StringPiece prefix) {
178 ssize_t size_diff = str.size() - prefix.size();
179 if (size_diff < 0 || str.substr(0, prefix.size()) != prefix)
180 return str;
181 return str.substr(prefix.size());
182 }
183
TrimSuffix(StringPiece str,StringPiece suffix)184 StringPiece TrimSuffix(StringPiece str, StringPiece suffix) {
185 ssize_t size_diff = str.size() - suffix.size();
186 if (size_diff < 0 || str.substr(size_diff) != suffix)
187 return str;
188 return str.substr(0, size_diff);
189 }
190
Pattern(StringPiece pat)191 Pattern::Pattern(StringPiece pat)
192 : pat_(pat), percent_index_(pat.find('%')) {
193 }
194
Match(StringPiece str) const195 bool Pattern::Match(StringPiece str) const {
196 if (percent_index_ == string::npos)
197 return str == pat_;
198 return MatchImpl(str);
199 }
200
MatchImpl(StringPiece str) const201 bool Pattern::MatchImpl(StringPiece str) const {
202 return (HasPrefix(str, pat_.substr(0, percent_index_)) &&
203 HasSuffix(str, pat_.substr(percent_index_ + 1)));
204 }
205
Stem(StringPiece str) const206 StringPiece Pattern::Stem(StringPiece str) const {
207 if (!Match(str))
208 return "";
209 return str.substr(percent_index_,
210 str.size() - (pat_.size() - percent_index_ - 1));
211 }
212
AppendSubst(StringPiece str,StringPiece subst,string * out) const213 void Pattern::AppendSubst(StringPiece str, StringPiece subst,
214 string* out) const {
215 if (percent_index_ == string::npos) {
216 if (str == pat_) {
217 AppendString(subst, out);
218 return;
219 } else {
220 AppendString(str, out);
221 return;
222 }
223 }
224
225 if (MatchImpl(str)) {
226 size_t subst_percent_index = subst.find('%');
227 if (subst_percent_index == string::npos) {
228 AppendString(subst, out);
229 return;
230 } else {
231 AppendString(subst.substr(0, subst_percent_index), out);
232 AppendString(str.substr(percent_index_,
233 str.size() - pat_.size() + 1), out);
234 AppendString(subst.substr(subst_percent_index + 1), out);
235 return;
236 }
237 }
238 AppendString(str, out);
239 }
240
AppendSubstRef(StringPiece str,StringPiece subst,string * out) const241 void Pattern::AppendSubstRef(StringPiece str, StringPiece subst,
242 string* out) const {
243 if (percent_index_ != string::npos && subst.find('%') != string::npos) {
244 AppendSubst(str, subst, out);
245 return;
246 }
247 StringPiece s = TrimSuffix(str, pat_);
248 out->append(s.begin(), s.end());
249 out->append(subst.begin(), subst.end());
250 }
251
NoLineBreak(const string & s)252 string NoLineBreak(const string& s) {
253 size_t index = s.find('\n');
254 if (index == string::npos)
255 return s;
256 string r = s;
257 while (index != string::npos) {
258 r = r.substr(0, index) + "\\n" + r.substr(index + 1);
259 index = r.find('\n', index + 2);
260 }
261 return r;
262 }
263
TrimLeftSpace(StringPiece s)264 StringPiece TrimLeftSpace(StringPiece s) {
265 size_t i = 0;
266 for (; i < s.size(); i++) {
267 if (isSpace(s[i]))
268 continue;
269 char n = s.get(i+1);
270 if (s[i] == '\\' && (n == '\r' || n == '\n')) {
271 i++;
272 continue;
273 }
274 break;
275 }
276 return s.substr(i, s.size() - i);
277 }
278
TrimRightSpace(StringPiece s)279 StringPiece TrimRightSpace(StringPiece s) {
280 size_t i = 0;
281 for (; i < s.size(); i++) {
282 char c = s[s.size() - 1 - i];
283 if (isSpace(c)) {
284 if ((c == '\r' || c == '\n') && s.get(s.size() - 2 - i) == '\\')
285 i++;
286 continue;
287 }
288 break;
289 }
290 return s.substr(0, s.size() - i);
291 }
292
TrimSpace(StringPiece s)293 StringPiece TrimSpace(StringPiece s) {
294 return TrimRightSpace(TrimLeftSpace(s));
295 }
296
Dirname(StringPiece s)297 StringPiece Dirname(StringPiece s) {
298 size_t found = s.rfind('/');
299 if (found == string::npos)
300 return StringPiece(".");
301 if (found == 0)
302 return StringPiece("");
303 return s.substr(0, found);
304 }
305
Basename(StringPiece s)306 StringPiece Basename(StringPiece s) {
307 size_t found = s.rfind('/');
308 if (found == string::npos || found == 0)
309 return s;
310 return s.substr(found + 1);
311 }
312
GetExt(StringPiece s)313 StringPiece GetExt(StringPiece s) {
314 size_t found = s.rfind('.');
315 if (found == string::npos)
316 return StringPiece("");
317 return s.substr(found);
318 }
319
StripExt(StringPiece s)320 StringPiece StripExt(StringPiece s) {
321 size_t slash_index = s.rfind('/');
322 size_t found = s.rfind('.');
323 if (found == string::npos ||
324 (slash_index != string::npos && found < slash_index))
325 return s;
326 return s.substr(0, found);
327 }
328
NormalizePath(string * o)329 void NormalizePath(string* o) {
330 if (o->empty())
331 return;
332 size_t start_index = 0;
333 if ((*o)[0] == '/')
334 start_index++;
335 size_t j = start_index;
336 size_t prev_start = start_index;
337 for (size_t i = start_index; i <= o->size(); i++) {
338 char c = (*o)[i];
339 if (c != '/' && c != 0) {
340 (*o)[j] = c;
341 j++;
342 continue;
343 }
344
345 StringPiece prev_dir = StringPiece(o->data() + prev_start, j - prev_start);
346 if (prev_dir == ".") {
347 j--;
348 } else if (prev_dir == ".." && j != 2 /* .. */) {
349 if (j == 3) {
350 // /..
351 j = start_index;
352 } else {
353 size_t orig_j = j;
354 j -= 4;
355 j = o->rfind('/', j);
356 if (j == string::npos) {
357 j = start_index;
358 } else {
359 j++;
360 }
361 if (StringPiece(o->data() + j, 3) == "../") {
362 j = orig_j;
363 (*o)[j] = c;
364 j++;
365 }
366 }
367 } else if (!prev_dir.empty()) {
368 if (c) {
369 (*o)[j] = c;
370 j++;
371 }
372 }
373 prev_start = j;
374 }
375 if (j > 1 && (*o)[j-1] == '/')
376 j--;
377 o->resize(j);
378 }
379
AbsPath(StringPiece s,string * o)380 void AbsPath(StringPiece s, string* o) {
381 if (s.get(0) == '/') {
382 o->clear();
383 } else {
384 char buf[PATH_MAX];
385 if (!getcwd(buf, PATH_MAX)) {
386 fprintf(stderr, "getcwd failed\n");
387 CHECK(false);
388 }
389
390 CHECK(buf[0] == '/');
391 *o = buf;
392 *o += '/';
393 }
394 AppendString(s, o);
395 NormalizePath(o);
396 }
397
398 template<typename Cond>
FindOutsideParenImpl(StringPiece s,Cond cond)399 size_t FindOutsideParenImpl(StringPiece s, Cond cond) {
400 bool prev_backslash = false;
401 stack<char> paren_stack;
402 for (size_t i = 0; i < s.size(); i++) {
403 char c = s[i];
404 if (cond(c) && paren_stack.empty() && !prev_backslash) {
405 return i;
406 }
407 switch (c) {
408 case '(':
409 paren_stack.push(')');
410 break;
411 case '{':
412 paren_stack.push('}');
413 break;
414
415 case ')':
416 case '}':
417 if (!paren_stack.empty() && c == paren_stack.top()) {
418 paren_stack.pop();
419 }
420 break;
421 }
422 prev_backslash = c == '\\' && !prev_backslash;
423 }
424 return string::npos;
425 }
426
FindOutsideParen(StringPiece s,char c)427 size_t FindOutsideParen(StringPiece s, char c) {
428 return FindOutsideParenImpl(s, [&c](char d){return c == d;});
429 }
430
FindTwoOutsideParen(StringPiece s,char c1,char c2)431 size_t FindTwoOutsideParen(StringPiece s, char c1, char c2) {
432 return FindOutsideParenImpl(s, [&c1, &c2](char d){
433 return d == c1 || d == c2;
434 });
435 }
436
FindThreeOutsideParen(StringPiece s,char c1,char c2,char c3)437 size_t FindThreeOutsideParen(StringPiece s, char c1, char c2, char c3) {
438 return FindOutsideParenImpl(s, [&c1, &c2, &c3](char d){
439 return d == c1 || d == c2 || d == c3;
440 });
441 }
442
FindEndOfLine(StringPiece s,size_t e,size_t * lf_cnt)443 size_t FindEndOfLine(StringPiece s, size_t e, size_t* lf_cnt) {
444 static const char ranges[] = "\0\0\n\n\\\\";
445 while (e < s.size()) {
446 e += SkipUntil(s.data() + e, s.size() - e, ranges, 6,
447 [](char c) { return c == 0 || c == '\n' || c == '\\'; });
448 if (e >= s.size()) {
449 CHECK(s.size() == e);
450 break;
451 }
452 char c = s[e];
453 if (c == '\0')
454 break;
455 if (c == '\\') {
456 if (s[e+1] == '\n') {
457 e += 2;
458 ++*lf_cnt;
459 } else if (s[e+1] == '\r' && s[e+2] == '\n') {
460 e += 3;
461 ++*lf_cnt;
462 } else if (s[e+1] == '\\') {
463 e += 2;
464 } else {
465 e++;
466 }
467 } else if (c == '\n') {
468 ++*lf_cnt;
469 return e;
470 }
471 }
472 return e;
473 }
474
TrimLeadingCurdir(StringPiece s)475 StringPiece TrimLeadingCurdir(StringPiece s) {
476 while (s.substr(0, 2) == "./")
477 s = s.substr(2);
478 return s;
479 }
480
FormatForCommandSubstitution(string * s)481 void FormatForCommandSubstitution(string* s) {
482 while ((*s)[s->size()-1] == '\n')
483 s->pop_back();
484 for (size_t i = 0; i < s->size(); i++) {
485 if ((*s)[i] == '\n')
486 (*s)[i] = ' ';
487 }
488 }
489
SortWordsInString(StringPiece s)490 string SortWordsInString(StringPiece s) {
491 vector<string> toks;
492 for (StringPiece tok : WordScanner(s)) {
493 toks.push_back(tok.as_string());
494 }
495 sort(toks.begin(), toks.end());
496 return JoinStrings(toks, " ");
497 }
498
ConcatDir(StringPiece b,StringPiece n)499 string ConcatDir(StringPiece b, StringPiece n) {
500 string r;
501 if (!b.empty()) {
502 b.AppendToString(&r);
503 r += '/';
504 }
505 n.AppendToString(&r);
506 NormalizePath(&r);
507 return r;
508 }
509
EchoEscape(const string & str)510 string EchoEscape(const string &str) {
511 const char *in = str.c_str();
512 string buf;
513 for (; *in; in++) {
514 switch(*in) {
515 case '\\':
516 buf += "\\\\\\\\";
517 break;
518 case '\n':
519 buf += "\\n";
520 break;
521 case '"':
522 buf += "\\\"";
523 break;
524 default:
525 buf += *in;
526 }
527 }
528 return buf;
529 }
530
NeedsShellEscape(char c)531 static bool NeedsShellEscape(char c) {
532 return c == 0 || c == '"' || c == '$' || c == '\\' || c == '`';
533 }
534
EscapeShell(string * s)535 void EscapeShell(string* s) {
536 static const char ranges[] = "\0\0\"\"$$\\\\``";
537 size_t prev = 0;
538 size_t i = SkipUntil(s->c_str(), s->size(), ranges, 10, NeedsShellEscape);
539 if (i == s->size())
540 return;
541
542 string r;
543 for (; i < s->size();) {
544 StringPiece(*s).substr(prev, i - prev).AppendToString(&r);
545 char c = (*s)[i];
546 r += '\\';
547 if (c == '$') {
548 if ((*s)[i+1] == '$') {
549 r += '$';
550 i++;
551 }
552 }
553 r += c;
554 i++;
555 prev = i;
556 i += SkipUntil(s->c_str() + i, s->size() - i, ranges, 10, NeedsShellEscape);
557 }
558 StringPiece(*s).substr(prev).AppendToString(&r);
559 s->swap(r);
560 }
561