strutil.cc revision 9e6e9301189479514d7b060491ff4f51b6d0b840
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 <stack> 25#include <utility> 26 27#include "log.h" 28 29static bool isSpace(char c) { 30 return (9 <= c && c <= 13) || c == 32; 31} 32 33WordScanner::Iterator& WordScanner::Iterator::operator++() { 34 int len = static_cast<int>(in->size()); 35 for (s = i; s < len; s++) { 36 if (!isSpace((*in)[s])) 37 break; 38 } 39 if (s == len) { 40 in = NULL; 41 s = 0; 42 i = 0; 43 return *this; 44 } 45 for (i = s; i < len; i++) { 46 if (isSpace((*in)[i])) 47 break; 48 } 49 return *this; 50} 51 52StringPiece WordScanner::Iterator::operator*() const { 53 return in->substr(s, i - s); 54} 55 56WordScanner::WordScanner(StringPiece in) 57 : in_(in) { 58} 59 60WordScanner::Iterator WordScanner::begin() const { 61 Iterator iter; 62 iter.in = &in_; 63 iter.s = 0; 64 iter.i = 0; 65 ++iter; 66 return iter; 67} 68 69WordScanner::Iterator WordScanner::end() const { 70 Iterator iter; 71 iter.in = NULL; 72 iter.s = 0; 73 iter.i = 0; 74 return iter; 75} 76 77void WordScanner::Split(vector<StringPiece>* o) { 78 for (StringPiece t : *this) 79 o->push_back(t); 80} 81 82WordWriter::WordWriter(string* o) 83 : out_(o), 84 needs_space_(false) { 85} 86 87void WordWriter::MaybeAddWhitespace() { 88 if (needs_space_) { 89 out_->push_back(' '); 90 } else { 91 needs_space_ = true; 92 } 93} 94 95void WordWriter::Write(StringPiece s) { 96 MaybeAddWhitespace(); 97 AppendString(s, out_); 98} 99 100ScopedTerminator::ScopedTerminator(StringPiece s) 101 : s_(s), c_(s[s.size()]) { 102 const_cast<char*>(s_.data())[s_.size()] = '\0'; 103} 104 105ScopedTerminator::~ScopedTerminator() { 106 const_cast<char*>(s_.data())[s_.size()] = c_; 107} 108 109void AppendString(StringPiece str, string* out) { 110 out->append(str.begin(), str.end()); 111} 112 113bool HasPrefix(StringPiece str, StringPiece prefix) { 114 ssize_t size_diff = str.size() - prefix.size(); 115 return size_diff >= 0 && str.substr(0, prefix.size()) == prefix; 116} 117 118bool HasSuffix(StringPiece str, StringPiece suffix) { 119 ssize_t size_diff = str.size() - suffix.size(); 120 return size_diff >= 0 && str.substr(size_diff) == suffix; 121} 122 123bool HasWord(StringPiece str, StringPiece w) { 124 size_t found = str.find(w); 125 if (found == string::npos) 126 return false; 127 if (found != 0 && !isSpace(str[found-1])) 128 return false; 129 size_t end = found + w.size(); 130 if (end != str.size() && !isSpace(str[end])) 131 return false; 132 return true; 133} 134 135StringPiece TrimSuffix(StringPiece str, StringPiece suffix) { 136 ssize_t size_diff = str.size() - suffix.size(); 137 if (size_diff < 0 || str.substr(size_diff) != suffix) 138 return str; 139 return str.substr(0, size_diff); 140} 141 142Pattern::Pattern(StringPiece pat) 143 : pat_(pat), percent_index_(pat.find('%')) { 144} 145 146bool Pattern::Match(StringPiece str) const { 147 if (percent_index_ == string::npos) 148 return str == pat_; 149 return MatchImpl(str); 150} 151 152bool Pattern::MatchImpl(StringPiece str) const { 153 return (HasPrefix(str, pat_.substr(0, percent_index_)) && 154 HasSuffix(str, pat_.substr(percent_index_ + 1))); 155} 156 157StringPiece Pattern::Stem(StringPiece str) const { 158 if (!Match(str)) 159 return ""; 160 return str.substr(percent_index_, 161 str.size() - (pat_.size() - percent_index_ - 1)); 162} 163 164void Pattern::AppendSubst(StringPiece str, StringPiece subst, 165 string* out) const { 166 if (percent_index_ == string::npos) { 167 if (str == pat_) { 168 AppendString(subst, out); 169 return; 170 } else { 171 AppendString(str, out); 172 return; 173 } 174 } 175 176 if (MatchImpl(str)) { 177 size_t subst_percent_index = subst.find('%'); 178 if (subst_percent_index == string::npos) { 179 AppendString(subst, out); 180 return; 181 } else { 182 AppendString(subst.substr(0, subst_percent_index), out); 183 AppendString(str.substr(percent_index_, 184 str.size() - pat_.size() + 1), out); 185 AppendString(subst.substr(subst_percent_index + 1), out); 186 return; 187 } 188 } 189 AppendString(str, out); 190} 191 192void Pattern::AppendSubstRef(StringPiece str, StringPiece subst, 193 string* out) const { 194 if (percent_index_ != string::npos && subst.find('%') != string::npos) { 195 AppendSubst(str, subst, out); 196 return; 197 } 198 StringPiece s = TrimSuffix(str, pat_); 199 out->append(s.begin(), s.end()); 200 out->append(subst.begin(), subst.end()); 201} 202 203string NoLineBreak(const string& s) { 204 size_t index = s.find('\n'); 205 if (index == string::npos) 206 return s; 207 string r = s; 208 while (index != string::npos) { 209 r = r.substr(0, index) + "\\n" + r.substr(index + 1); 210 index = r.find('\n', index + 2); 211 } 212 return r; 213} 214 215StringPiece TrimLeftSpace(StringPiece s) { 216 size_t i = 0; 217 for (; i < s.size(); i++) { 218 if (isSpace(s[i])) 219 continue; 220 char n = s.get(i+1); 221 if (s[i] == '\\' && (n == '\r' || n == '\n')) { 222 i++; 223 continue; 224 } 225 break; 226 } 227 return s.substr(i, s.size() - i); 228} 229 230StringPiece TrimRightSpace(StringPiece s) { 231 size_t i = 0; 232 for (; i < s.size(); i++) { 233 char c = s[s.size() - 1 - i]; 234 if (isSpace(c)) { 235 if ((c == '\r' || c == '\n') && s.get(s.size() - 2 - i) == '\\') 236 i++; 237 continue; 238 } 239 break; 240 } 241 return s.substr(0, s.size() - i); 242} 243 244StringPiece TrimSpace(StringPiece s) { 245 return TrimRightSpace(TrimLeftSpace(s)); 246} 247 248StringPiece Dirname(StringPiece s) { 249 size_t found = s.rfind('/'); 250 if (found == string::npos) 251 return StringPiece("."); 252 if (found == 0) 253 return StringPiece(""); 254 return s.substr(0, found); 255} 256 257StringPiece Basename(StringPiece s) { 258 size_t found = s.rfind('/'); 259 if (found == string::npos || found == 0) 260 return s; 261 return s.substr(found + 1); 262} 263 264StringPiece GetExt(StringPiece s) { 265 size_t found = s.rfind('.'); 266 if (found == string::npos) 267 return StringPiece(""); 268 return s.substr(found); 269} 270 271StringPiece StripExt(StringPiece s) { 272 size_t slash_index = s.rfind('/'); 273 size_t found = s.rfind('.'); 274 if (found == string::npos || 275 (slash_index != string::npos && found < slash_index)) 276 return s; 277 return s.substr(0, found); 278} 279 280void NormalizePath(string* o) { 281 if (o->empty()) 282 return; 283 size_t start_index = 0; 284 if ((*o)[0] == '/') 285 start_index++; 286 size_t j = start_index; 287 size_t prev_start = start_index; 288 for (size_t i = start_index; i <= o->size(); i++) { 289 char c = (*o)[i]; 290 if (c != '/' && c != 0) { 291 (*o)[j] = c; 292 j++; 293 continue; 294 } 295 296 StringPiece prev_dir = StringPiece(o->data() + prev_start, j - prev_start); 297 if (prev_dir == ".") { 298 j--; 299 } else if (prev_dir == ".." && j != 2 /* .. */) { 300 if (j == 3) { 301 // /.. 302 j = start_index; 303 } else { 304 size_t orig_j = j; 305 j -= 4; 306 j = o->rfind('/', j); 307 if (j == string::npos) { 308 j = start_index; 309 } else { 310 j++; 311 } 312 if (StringPiece(o->data() + j, 3) == "../") { 313 j = orig_j; 314 (*o)[j] = c; 315 j++; 316 } 317 } 318 } else if (!prev_dir.empty()) { 319 if (c) { 320 (*o)[j] = c; 321 j++; 322 } 323 } 324 prev_start = j; 325 } 326 if (j > 1 && (*o)[j-1] == '/') 327 j--; 328 o->resize(j); 329} 330 331void AbsPath(StringPiece s, string* o) { 332 if (s.get(0) == '/') { 333 o->clear(); 334 } else { 335 char buf[PATH_MAX]; 336 if (!getcwd(buf, PATH_MAX)) { 337 fprintf(stderr, "getcwd failed\n"); 338 CHECK(false); 339 } 340 341 CHECK(buf[0] == '/'); 342 *o = buf; 343 *o += '/'; 344 } 345 AppendString(s, o); 346 NormalizePath(o); 347} 348 349template<typename Cond> 350size_t FindOutsideParenImpl(StringPiece s, Cond cond) { 351 bool prev_backslash = false; 352 stack<char> paren_stack; 353 for (size_t i = 0; i < s.size(); i++) { 354 char c = s[i]; 355 if (cond(c) && paren_stack.empty() && !prev_backslash) { 356 return i; 357 } 358 switch (c) { 359 case '(': 360 paren_stack.push(')'); 361 break; 362 case '{': 363 paren_stack.push('}'); 364 break; 365 366 case ')': 367 case '}': 368 if (!paren_stack.empty() && c == paren_stack.top()) { 369 paren_stack.pop(); 370 } 371 break; 372 } 373 prev_backslash = c == '\\' && !prev_backslash; 374 } 375 return string::npos; 376} 377 378size_t FindOutsideParen(StringPiece s, char c) { 379 return FindOutsideParenImpl(s, [&c](char d){return c == d;}); 380} 381 382size_t FindTwoOutsideParen(StringPiece s, char c1, char c2) { 383 return FindOutsideParenImpl(s, [&c1, &c2](char d){ 384 return d == c1 || d == c2; 385 }); 386} 387 388size_t FindThreeOutsideParen(StringPiece s, char c1, char c2, char c3) { 389 return FindOutsideParenImpl(s, [&c1, &c2, &c3](char d){ 390 return d == c1 || d == c2 || d == c3; 391 }); 392} 393 394size_t FindEndOfLine(StringPiece s, size_t e, size_t* lf_cnt) { 395 bool prev_backslash = false; 396 for (; e < s.size(); e++) { 397 char c = s[e]; 398 if (c == '\\') { 399 prev_backslash = !prev_backslash; 400 } else if (c == '\n') { 401 ++*lf_cnt; 402 if (!prev_backslash) { 403 return e; 404 } 405 prev_backslash = false; 406 } else if (c != '\r') { 407 prev_backslash = false; 408 } 409 } 410 return e; 411} 412 413StringPiece TrimLeadingCurdir(StringPiece s) { 414 while (s.substr(0, 2) == "./") 415 s = s.substr(2); 416 return s; 417} 418 419void FormatForCommandSubstitution(string* s) { 420 while ((*s)[s->size()-1] == '\n') 421 s->pop_back(); 422 for (size_t i = 0; i < s->size(); i++) { 423 if ((*s)[i] == '\n') 424 (*s)[i] = ' '; 425 } 426} 427 428string SortWordsInString(StringPiece s) { 429 vector<string> toks; 430 for (StringPiece tok : WordScanner(s)) { 431 toks.push_back(tok.as_string()); 432 } 433 sort(toks.begin(), toks.end()); 434 return JoinStrings(toks, " "); 435} 436 437string ConcatDir(StringPiece b, StringPiece n) { 438 string r; 439 if (!b.empty()) { 440 b.AppendToString(&r); 441 r += '/'; 442 } 443 n.AppendToString(&r); 444 NormalizePath(&r); 445 return r; 446} 447 448string EchoEscape(const string str) { 449 const char *in = str.c_str(); 450 string buf; 451 for (; *in; in++) { 452 switch(*in) { 453 case '\\': 454 buf += "\\\\\\\\"; 455 break; 456 case '\n': 457 buf += "\\n"; 458 break; 459 case '"': 460 buf += "\\\""; 461 break; 462 default: 463 buf += *in; 464 } 465 } 466 return buf; 467} 468