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