| // Copyright 2003-2009 Google Inc. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // This is a variant of PCRE's pcrecpp.cc, originally written at Google. |
| // The main changes are the addition of the HitLimit method and |
| // compilation as PCRE in namespace re2. |
| |
| #include <errno.h> |
| #include "util/util.h" |
| #include "util/flags.h" |
| #include "util/pcre.h" |
| |
| #define PCREPORT(level) LOG(level) |
| |
| // Default PCRE limits. |
| // Defaults chosen to allow a plausible amount of CPU and |
| // not exceed main thread stacks. Note that other threads |
| // often have smaller stacks, and therefore tightening |
| // regexp_stack_limit may frequently be necessary. |
| DEFINE_int32(regexp_stack_limit, 256<<10, "default PCRE stack limit (bytes)"); |
| DEFINE_int32(regexp_match_limit, 1000000, |
| "default PCRE match limit (function calls)"); |
| |
| namespace re2 { |
| |
| // Maximum number of args we can set |
| static const int kMaxArgs = 16; |
| static const int kVecSize = (1 + kMaxArgs) * 3; // results + PCRE workspace |
| |
| // Approximate size of a recursive invocation of PCRE's |
| // internal "match()" frame. This varies depending on the |
| // compiler and architecture, of course, so the constant is |
| // just a conservative estimate. To find the exact number, |
| // run regexp_unittest with --regexp_stack_limit=0 under |
| // a debugger and look at the frames when it crashes. |
| // The exact frame size was 656 in production on 2008/02/03. |
| static const int kPCREFrameSize = 700; |
| |
| // Special name for missing C++ arguments. |
| PCRE::Arg PCRE::no_more_args((void*)NULL); |
| |
| const PCRE::PartialMatchFunctor PCRE::PartialMatch = { }; |
| const PCRE::FullMatchFunctor PCRE::FullMatch = { } ; |
| const PCRE::ConsumeFunctor PCRE::Consume = { }; |
| const PCRE::FindAndConsumeFunctor PCRE::FindAndConsume = { }; |
| |
| // If a regular expression has no error, its error_ field points here |
| static const string empty_string; |
| |
| void PCRE::Init(const char* pattern, Option options, int match_limit, |
| int stack_limit, bool report_errors) { |
| pattern_ = pattern; |
| options_ = options; |
| match_limit_ = match_limit; |
| stack_limit_ = stack_limit; |
| hit_limit_ = false; |
| error_ = &empty_string; |
| report_errors_ = report_errors; |
| re_full_ = NULL; |
| re_partial_ = NULL; |
| |
| if (options & ~(EnabledCompileOptions | EnabledExecOptions)) { |
| error_ = new string("illegal regexp option"); |
| PCREPORT(ERROR) |
| << "Error compiling '" << pattern << "': illegal regexp option"; |
| } else { |
| re_partial_ = Compile(UNANCHORED); |
| if (re_partial_ != NULL) { |
| re_full_ = Compile(ANCHOR_BOTH); |
| } |
| } |
| } |
| |
| PCRE::PCRE(const char* pattern) { |
| Init(pattern, None, 0, 0, true); |
| } |
| PCRE::PCRE(const char* pattern, Option option) { |
| Init(pattern, option, 0, 0, true); |
| } |
| PCRE::PCRE(const string& pattern) { |
| Init(pattern.c_str(), None, 0, 0, true); |
| } |
| PCRE::PCRE(const string& pattern, Option option) { |
| Init(pattern.c_str(), option, 0, 0, true); |
| } |
| PCRE::PCRE(const string& pattern, const PCRE_Options& re_option) { |
| Init(pattern.c_str(), re_option.option(), re_option.match_limit(), |
| re_option.stack_limit(), re_option.report_errors()); |
| } |
| |
| PCRE::PCRE(const char *pattern, const PCRE_Options& re_option) { |
| Init(pattern, re_option.option(), re_option.match_limit(), |
| re_option.stack_limit(), re_option.report_errors()); |
| } |
| |
| PCRE::~PCRE() { |
| if (re_full_ != NULL) pcre_free(re_full_); |
| if (re_partial_ != NULL) pcre_free(re_partial_); |
| if (error_ != &empty_string) delete error_; |
| } |
| |
| pcre* PCRE::Compile(Anchor anchor) { |
| // Special treatment for anchoring. This is needed because at |
| // runtime pcre only provides an option for anchoring at the |
| // beginning of a string. |
| // |
| // There are three types of anchoring we want: |
| // UNANCHORED Compile the original pattern, and use |
| // a pcre unanchored match. |
| // ANCHOR_START Compile the original pattern, and use |
| // a pcre anchored match. |
| // ANCHOR_BOTH Tack a "\z" to the end of the original pattern |
| // and use a pcre anchored match. |
| |
| const char* error; |
| int eoffset; |
| pcre* re; |
| if (anchor != ANCHOR_BOTH) { |
| re = pcre_compile(pattern_.c_str(), |
| (options_ & EnabledCompileOptions), |
| &error, &eoffset, NULL); |
| } else { |
| // Tack a '\z' at the end of PCRE. Parenthesize it first so that |
| // the '\z' applies to all top-level alternatives in the regexp. |
| string wrapped = "(?:"; // A non-counting grouping operator |
| wrapped += pattern_; |
| wrapped += ")\\z"; |
| re = pcre_compile(wrapped.c_str(), |
| (options_ & EnabledCompileOptions), |
| &error, &eoffset, NULL); |
| } |
| if (re == NULL) { |
| if (error_ == &empty_string) error_ = new string(error); |
| PCREPORT(ERROR) << "Error compiling '" << pattern_ << "': " << error; |
| } |
| return re; |
| } |
| |
| /***** Convenience interfaces *****/ |
| |
| bool PCRE::FullMatchFunctor::operator ()(const StringPiece& text, |
| const PCRE& re, |
| const Arg& a0, |
| const Arg& a1, |
| const Arg& a2, |
| const Arg& a3, |
| const Arg& a4, |
| const Arg& a5, |
| const Arg& a6, |
| const Arg& a7, |
| const Arg& a8, |
| const Arg& a9, |
| const Arg& a10, |
| const Arg& a11, |
| const Arg& a12, |
| const Arg& a13, |
| const Arg& a14, |
| const Arg& a15) const { |
| const Arg* args[kMaxArgs]; |
| int n = 0; |
| if (&a0 == &no_more_args) goto done; args[n++] = &a0; |
| if (&a1 == &no_more_args) goto done; args[n++] = &a1; |
| if (&a2 == &no_more_args) goto done; args[n++] = &a2; |
| if (&a3 == &no_more_args) goto done; args[n++] = &a3; |
| if (&a4 == &no_more_args) goto done; args[n++] = &a4; |
| if (&a5 == &no_more_args) goto done; args[n++] = &a5; |
| if (&a6 == &no_more_args) goto done; args[n++] = &a6; |
| if (&a7 == &no_more_args) goto done; args[n++] = &a7; |
| if (&a8 == &no_more_args) goto done; args[n++] = &a8; |
| if (&a9 == &no_more_args) goto done; args[n++] = &a9; |
| if (&a10 == &no_more_args) goto done; args[n++] = &a10; |
| if (&a11 == &no_more_args) goto done; args[n++] = &a11; |
| if (&a12 == &no_more_args) goto done; args[n++] = &a12; |
| if (&a13 == &no_more_args) goto done; args[n++] = &a13; |
| if (&a14 == &no_more_args) goto done; args[n++] = &a14; |
| if (&a15 == &no_more_args) goto done; args[n++] = &a15; |
| done: |
| |
| int consumed; |
| int vec[kVecSize]; |
| return re.DoMatchImpl(text, ANCHOR_BOTH, &consumed, args, n, vec, kVecSize); |
| } |
| |
| bool PCRE::PartialMatchFunctor::operator ()(const StringPiece& text, |
| const PCRE& re, |
| const Arg& a0, |
| const Arg& a1, |
| const Arg& a2, |
| const Arg& a3, |
| const Arg& a4, |
| const Arg& a5, |
| const Arg& a6, |
| const Arg& a7, |
| const Arg& a8, |
| const Arg& a9, |
| const Arg& a10, |
| const Arg& a11, |
| const Arg& a12, |
| const Arg& a13, |
| const Arg& a14, |
| const Arg& a15) const { |
| const Arg* args[kMaxArgs]; |
| int n = 0; |
| if (&a0 == &no_more_args) goto done; args[n++] = &a0; |
| if (&a1 == &no_more_args) goto done; args[n++] = &a1; |
| if (&a2 == &no_more_args) goto done; args[n++] = &a2; |
| if (&a3 == &no_more_args) goto done; args[n++] = &a3; |
| if (&a4 == &no_more_args) goto done; args[n++] = &a4; |
| if (&a5 == &no_more_args) goto done; args[n++] = &a5; |
| if (&a6 == &no_more_args) goto done; args[n++] = &a6; |
| if (&a7 == &no_more_args) goto done; args[n++] = &a7; |
| if (&a8 == &no_more_args) goto done; args[n++] = &a8; |
| if (&a9 == &no_more_args) goto done; args[n++] = &a9; |
| if (&a10 == &no_more_args) goto done; args[n++] = &a10; |
| if (&a11 == &no_more_args) goto done; args[n++] = &a11; |
| if (&a12 == &no_more_args) goto done; args[n++] = &a12; |
| if (&a13 == &no_more_args) goto done; args[n++] = &a13; |
| if (&a14 == &no_more_args) goto done; args[n++] = &a14; |
| if (&a15 == &no_more_args) goto done; args[n++] = &a15; |
| done: |
| |
| int consumed; |
| int vec[kVecSize]; |
| return re.DoMatchImpl(text, UNANCHORED, &consumed, args, n, vec, kVecSize); |
| } |
| |
| bool PCRE::ConsumeFunctor::operator ()(StringPiece* input, |
| const PCRE& pattern, |
| const Arg& a0, |
| const Arg& a1, |
| const Arg& a2, |
| const Arg& a3, |
| const Arg& a4, |
| const Arg& a5, |
| const Arg& a6, |
| const Arg& a7, |
| const Arg& a8, |
| const Arg& a9, |
| const Arg& a10, |
| const Arg& a11, |
| const Arg& a12, |
| const Arg& a13, |
| const Arg& a14, |
| const Arg& a15) const { |
| const Arg* args[kMaxArgs]; |
| int n = 0; |
| if (&a0 == &no_more_args) goto done; args[n++] = &a0; |
| if (&a1 == &no_more_args) goto done; args[n++] = &a1; |
| if (&a2 == &no_more_args) goto done; args[n++] = &a2; |
| if (&a3 == &no_more_args) goto done; args[n++] = &a3; |
| if (&a4 == &no_more_args) goto done; args[n++] = &a4; |
| if (&a5 == &no_more_args) goto done; args[n++] = &a5; |
| if (&a6 == &no_more_args) goto done; args[n++] = &a6; |
| if (&a7 == &no_more_args) goto done; args[n++] = &a7; |
| if (&a8 == &no_more_args) goto done; args[n++] = &a8; |
| if (&a9 == &no_more_args) goto done; args[n++] = &a9; |
| if (&a10 == &no_more_args) goto done; args[n++] = &a10; |
| if (&a11 == &no_more_args) goto done; args[n++] = &a11; |
| if (&a12 == &no_more_args) goto done; args[n++] = &a12; |
| if (&a13 == &no_more_args) goto done; args[n++] = &a13; |
| if (&a14 == &no_more_args) goto done; args[n++] = &a14; |
| if (&a15 == &no_more_args) goto done; args[n++] = &a15; |
| done: |
| |
| int consumed; |
| int vec[kVecSize]; |
| if (pattern.DoMatchImpl(*input, ANCHOR_START, &consumed, |
| args, n, vec, kVecSize)) { |
| input->remove_prefix(consumed); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| bool PCRE::FindAndConsumeFunctor::operator ()(StringPiece* input, |
| const PCRE& pattern, |
| const Arg& a0, |
| const Arg& a1, |
| const Arg& a2, |
| const Arg& a3, |
| const Arg& a4, |
| const Arg& a5, |
| const Arg& a6, |
| const Arg& a7, |
| const Arg& a8, |
| const Arg& a9, |
| const Arg& a10, |
| const Arg& a11, |
| const Arg& a12, |
| const Arg& a13, |
| const Arg& a14, |
| const Arg& a15) const { |
| const Arg* args[kMaxArgs]; |
| int n = 0; |
| if (&a0 == &no_more_args) goto done; args[n++] = &a0; |
| if (&a1 == &no_more_args) goto done; args[n++] = &a1; |
| if (&a2 == &no_more_args) goto done; args[n++] = &a2; |
| if (&a3 == &no_more_args) goto done; args[n++] = &a3; |
| if (&a4 == &no_more_args) goto done; args[n++] = &a4; |
| if (&a5 == &no_more_args) goto done; args[n++] = &a5; |
| if (&a6 == &no_more_args) goto done; args[n++] = &a6; |
| if (&a7 == &no_more_args) goto done; args[n++] = &a7; |
| if (&a8 == &no_more_args) goto done; args[n++] = &a8; |
| if (&a9 == &no_more_args) goto done; args[n++] = &a9; |
| if (&a10 == &no_more_args) goto done; args[n++] = &a10; |
| if (&a11 == &no_more_args) goto done; args[n++] = &a11; |
| if (&a12 == &no_more_args) goto done; args[n++] = &a12; |
| if (&a13 == &no_more_args) goto done; args[n++] = &a13; |
| if (&a14 == &no_more_args) goto done; args[n++] = &a14; |
| if (&a15 == &no_more_args) goto done; args[n++] = &a15; |
| done: |
| |
| int consumed; |
| int vec[kVecSize]; |
| if (pattern.DoMatchImpl(*input, UNANCHORED, &consumed, |
| args, n, vec, kVecSize)) { |
| input->remove_prefix(consumed); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| bool PCRE::Replace(string *str, |
| const PCRE& pattern, |
| const StringPiece& rewrite) { |
| int vec[kVecSize]; |
| int matches = pattern.TryMatch(*str, 0, UNANCHORED, true, vec, kVecSize); |
| if (matches == 0) |
| return false; |
| |
| string s; |
| if (!pattern.Rewrite(&s, rewrite, *str, vec, matches)) |
| return false; |
| |
| assert(vec[0] >= 0); |
| assert(vec[1] >= 0); |
| str->replace(vec[0], vec[1] - vec[0], s); |
| return true; |
| } |
| |
| int PCRE::GlobalReplace(string *str, |
| const PCRE& pattern, |
| const StringPiece& rewrite) { |
| int count = 0; |
| int vec[kVecSize]; |
| string out; |
| int start = 0; |
| bool last_match_was_empty_string = false; |
| |
| for (; start <= str->length();) { |
| // If the previous match was for the empty string, we shouldn't |
| // just match again: we'll match in the same way and get an |
| // infinite loop. Instead, we do the match in a special way: |
| // anchored -- to force another try at the same position -- |
| // and with a flag saying that this time, ignore empty matches. |
| // If this special match returns, that means there's a non-empty |
| // match at this position as well, and we can continue. If not, |
| // we do what perl does, and just advance by one. |
| // Notice that perl prints '@@@' for this; |
| // perl -le '$_ = "aa"; s/b*|aa/@/g; print' |
| int matches; |
| if (last_match_was_empty_string) { |
| matches = pattern.TryMatch(*str, start, ANCHOR_START, false, |
| vec, kVecSize); |
| if (matches <= 0) { |
| if (start < str->length()) |
| out.push_back((*str)[start]); |
| start++; |
| last_match_was_empty_string = false; |
| continue; |
| } |
| } else { |
| matches = pattern.TryMatch(*str, start, UNANCHORED, true, vec, kVecSize); |
| if (matches <= 0) |
| break; |
| } |
| int matchstart = vec[0], matchend = vec[1]; |
| assert(matchstart >= start); |
| assert(matchend >= matchstart); |
| |
| out.append(*str, start, matchstart - start); |
| pattern.Rewrite(&out, rewrite, *str, vec, matches); |
| start = matchend; |
| count++; |
| last_match_was_empty_string = (matchstart == matchend); |
| } |
| |
| if (count == 0) |
| return 0; |
| |
| if (start < str->length()) |
| out.append(*str, start, str->length() - start); |
| swap(out, *str); |
| return count; |
| } |
| |
| bool PCRE::Extract(const StringPiece &text, |
| const PCRE& pattern, |
| const StringPiece &rewrite, |
| string *out) { |
| int vec[kVecSize]; |
| int matches = pattern.TryMatch(text, 0, UNANCHORED, true, vec, kVecSize); |
| if (matches == 0) |
| return false; |
| out->clear(); |
| return pattern.Rewrite(out, rewrite, text, vec, matches); |
| } |
| |
| string PCRE::QuoteMeta(const StringPiece& unquoted) { |
| string result; |
| result.reserve(unquoted.size() << 1); |
| |
| // Escape any ascii character not in [A-Za-z_0-9]. |
| // |
| // Note that it's legal to escape a character even if it has no |
| // special meaning in a regular expression -- so this function does |
| // that. (This also makes it identical to the perl function of the |
| // same name except for the null-character special case; |
| // see `perldoc -f quotemeta`.) |
| for (int ii = 0; ii < unquoted.length(); ++ii) { |
| // Note that using 'isalnum' here raises the benchmark time from |
| // 32ns to 58ns: |
| if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') && |
| (unquoted[ii] < 'A' || unquoted[ii] > 'Z') && |
| (unquoted[ii] < '0' || unquoted[ii] > '9') && |
| unquoted[ii] != '_' && |
| // If this is the part of a UTF8 or Latin1 character, we need |
| // to copy this byte without escaping. Experimentally this is |
| // what works correctly with the regexp library. |
| !(unquoted[ii] & 128)) { |
| if (unquoted[ii] == '\0') { // Special handling for null chars. |
| // Can't use "\\0" since the next character might be a digit. |
| result += "\\x00"; |
| continue; |
| } |
| result += '\\'; |
| } |
| result += unquoted[ii]; |
| } |
| |
| return result; |
| } |
| |
| /***** Actual matching and rewriting code *****/ |
| |
| bool PCRE::HitLimit() { |
| return hit_limit_; |
| } |
| |
| void PCRE::ClearHitLimit() { |
| hit_limit_ = 0; |
| } |
| |
| int PCRE::TryMatch(const StringPiece& text, |
| int startpos, |
| Anchor anchor, |
| bool empty_ok, |
| int *vec, |
| int vecsize) const { |
| pcre* re = (anchor == ANCHOR_BOTH) ? re_full_ : re_partial_; |
| if (re == NULL) { |
| PCREPORT(ERROR) << "Matching against invalid re: " << *error_; |
| return 0; |
| } |
| |
| int match_limit = match_limit_; |
| if (match_limit <= 0) { |
| match_limit = FLAGS_regexp_match_limit; |
| } |
| |
| int stack_limit = stack_limit_; |
| if (stack_limit <= 0) { |
| stack_limit = FLAGS_regexp_stack_limit; |
| } |
| |
| pcre_extra extra = { 0 }; |
| if (match_limit > 0) { |
| extra.flags |= PCRE_EXTRA_MATCH_LIMIT; |
| extra.match_limit = match_limit; |
| } |
| if (stack_limit > 0) { |
| extra.flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; |
| extra.match_limit_recursion = stack_limit / kPCREFrameSize; |
| } |
| |
| int options = 0; |
| if (anchor != UNANCHORED) |
| options |= PCRE_ANCHORED; |
| if (!empty_ok) |
| options |= PCRE_NOTEMPTY; |
| |
| int rc = pcre_exec(re, // The regular expression object |
| &extra, |
| (text.data() == NULL) ? "" : text.data(), |
| text.size(), |
| startpos, |
| options, |
| vec, |
| vecsize); |
| |
| // Handle errors |
| if (rc == 0) { |
| // pcre_exec() returns 0 as a special case when the number of |
| // capturing subpatterns exceeds the size of the vector. |
| // When this happens, there is a match and the output vector |
| // is filled, but we miss out on the positions of the extra subpatterns. |
| rc = vecsize / 2; |
| } else if (rc < 0) { |
| switch (rc) { |
| case PCRE_ERROR_NOMATCH: |
| return 0; |
| case PCRE_ERROR_MATCHLIMIT: |
| // Writing to hit_limit is not safe if multiple threads |
| // are using the PCRE, but the flag is only intended |
| // for use by unit tests anyway, so we let it go. |
| hit_limit_ = true; |
| PCREPORT(WARNING) << "Exceeded match limit of " << match_limit |
| << " when matching '" << pattern_ << "'" |
| << " against text that is " << text.size() << " bytes."; |
| return 0; |
| case PCRE_ERROR_RECURSIONLIMIT: |
| // See comment about hit_limit above. |
| hit_limit_ = true; |
| PCREPORT(WARNING) << "Exceeded stack limit of " << stack_limit |
| << " when matching '" << pattern_ << "'" |
| << " against text that is " << text.size() << " bytes."; |
| return 0; |
| default: |
| // There are other return codes from pcre.h : |
| // PCRE_ERROR_NULL (-2) |
| // PCRE_ERROR_BADOPTION (-3) |
| // PCRE_ERROR_BADMAGIC (-4) |
| // PCRE_ERROR_UNKNOWN_NODE (-5) |
| // PCRE_ERROR_NOMEMORY (-6) |
| // PCRE_ERROR_NOSUBSTRING (-7) |
| // ... |
| PCREPORT(ERROR) << "Unexpected return code: " << rc |
| << " when matching '" << pattern_ << "'" |
| << ", re=" << re |
| << ", text=" << text |
| << ", vec=" << vec |
| << ", vecsize=" << vecsize; |
| return 0; |
| } |
| } |
| |
| return rc; |
| } |
| |
| bool PCRE::DoMatchImpl(const StringPiece& text, |
| Anchor anchor, |
| int* consumed, |
| const Arg* const* args, |
| int n, |
| int* vec, |
| int vecsize) const { |
| assert((1 + n) * 3 <= vecsize); // results + PCRE workspace |
| int matches = TryMatch(text, 0, anchor, true, vec, vecsize); |
| assert(matches >= 0); // TryMatch never returns negatives |
| if (matches == 0) |
| return false; |
| |
| *consumed = vec[1]; |
| |
| if (n == 0 || args == NULL) { |
| // We are not interested in results |
| return true; |
| } |
| if (NumberOfCapturingGroups() < n) { |
| // PCRE has fewer capturing groups than number of arg pointers passed in |
| return false; |
| } |
| |
| // If we got here, we must have matched the whole pattern. |
| // We do not need (can not do) any more checks on the value of 'matches' here |
| // -- see the comment for TryMatch. |
| for (int i = 0; i < n; i++) { |
| const int start = vec[2*(i+1)]; |
| const int limit = vec[2*(i+1)+1]; |
| if (!args[i]->Parse(text.data() + start, limit-start)) { |
| // TODO: Should we indicate what the error was? |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool PCRE::DoMatch(const StringPiece& text, |
| Anchor anchor, |
| int* consumed, |
| const Arg* const args[], |
| int n) const { |
| assert(n >= 0); |
| size_t const vecsize = (1 + n) * 3; // results + PCRE workspace |
| // (as for kVecSize) |
| int *vec = new int[vecsize]; |
| bool b = DoMatchImpl(text, anchor, consumed, args, n, vec, vecsize); |
| delete[] vec; |
| return b; |
| } |
| |
| bool PCRE::Rewrite(string *out, const StringPiece &rewrite, |
| const StringPiece &text, int *vec, int veclen) const { |
| int number_of_capturing_groups = NumberOfCapturingGroups(); |
| for (const char *s = rewrite.data(), *end = s + rewrite.size(); |
| s < end; s++) { |
| int c = *s; |
| if (c == '\\') { |
| c = *++s; |
| if (isdigit(c)) { |
| int n = (c - '0'); |
| if (n >= veclen) { |
| if (n <= number_of_capturing_groups) { |
| // unmatched optional capturing group. treat |
| // its value as empty string; i.e., nothing to append. |
| } else { |
| PCREPORT(ERROR) << "requested group " << n |
| << " in regexp " << rewrite.data(); |
| return false; |
| } |
| } |
| int start = vec[2 * n]; |
| if (start >= 0) |
| out->append(text.data() + start, vec[2 * n + 1] - start); |
| } else if (c == '\\') { |
| out->push_back('\\'); |
| } else { |
| PCREPORT(ERROR) << "invalid rewrite pattern: " << rewrite.data(); |
| return false; |
| } |
| } else { |
| out->push_back(c); |
| } |
| } |
| return true; |
| } |
| |
| bool PCRE::CheckRewriteString(const StringPiece& rewrite, string* error) const { |
| int max_token = -1; |
| for (const char *s = rewrite.data(), *end = s + rewrite.size(); |
| s < end; s++) { |
| int c = *s; |
| if (c != '\\') { |
| continue; |
| } |
| if (++s == end) { |
| *error = "Rewrite schema error: '\\' not allowed at end."; |
| return false; |
| } |
| c = *s; |
| if (c == '\\') { |
| continue; |
| } |
| if (!isdigit(c)) { |
| *error = "Rewrite schema error: " |
| "'\\' must be followed by a digit or '\\'."; |
| return false; |
| } |
| int n = (c - '0'); |
| if (max_token < n) { |
| max_token = n; |
| } |
| } |
| |
| if (max_token > NumberOfCapturingGroups()) { |
| SStringPrintf(error, "Rewrite schema requests %d matches, " |
| "but the regexp only has %d parenthesized subexpressions.", |
| max_token, NumberOfCapturingGroups()); |
| return false; |
| } |
| return true; |
| } |
| |
| |
| // Return the number of capturing subpatterns, or -1 if the |
| // regexp wasn't valid on construction. |
| int PCRE::NumberOfCapturingGroups() const { |
| if (re_partial_ == NULL) return -1; |
| |
| int result; |
| CHECK(pcre_fullinfo(re_partial_, // The regular expression object |
| NULL, // We did not study the pattern |
| PCRE_INFO_CAPTURECOUNT, |
| &result) == 0); |
| return result; |
| } |
| |
| |
| /***** Parsers for various types *****/ |
| |
| bool PCRE::Arg::parse_null(const char* str, int n, void* dest) { |
| // We fail if somebody asked us to store into a non-NULL void* pointer |
| return (dest == NULL); |
| } |
| |
| bool PCRE::Arg::parse_string(const char* str, int n, void* dest) { |
| if (dest == NULL) return true; |
| reinterpret_cast<string*>(dest)->assign(str, n); |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_stringpiece(const char* str, int n, void* dest) { |
| if (dest == NULL) return true; |
| reinterpret_cast<StringPiece*>(dest)->set(str, n); |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_char(const char* str, int n, void* dest) { |
| if (n != 1) return false; |
| if (dest == NULL) return true; |
| *(reinterpret_cast<char*>(dest)) = str[0]; |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_uchar(const char* str, int n, void* dest) { |
| if (n != 1) return false; |
| if (dest == NULL) return true; |
| *(reinterpret_cast<unsigned char*>(dest)) = str[0]; |
| return true; |
| } |
| |
| // Largest number spec that we are willing to parse |
| static const int kMaxNumberLength = 32; |
| |
| // PCREQUIPCRES "buf" must have length at least kMaxNumberLength+1 |
| // PCREQUIPCRES "n > 0" |
| // Copies "str" into "buf" and null-terminates if necessary. |
| // Returns one of: |
| // a. "str" if no termination is needed |
| // b. "buf" if the string was copied and null-terminated |
| // c. "" if the input was invalid and has no hope of being parsed |
| static const char* TerminateNumber(char* buf, const char* str, int n) { |
| if ((n > 0) && isspace(*str)) { |
| // We are less forgiving than the strtoxxx() routines and do not |
| // allow leading spaces. |
| return ""; |
| } |
| |
| // See if the character right after the input text may potentially |
| // look like a digit. |
| if (isdigit(str[n]) || |
| ((str[n] >= 'a') && (str[n] <= 'f')) || |
| ((str[n] >= 'A') && (str[n] <= 'F'))) { |
| if (n > kMaxNumberLength) return ""; // Input too big to be a valid number |
| memcpy(buf, str, n); |
| buf[n] = '\0'; |
| return buf; |
| } else { |
| // We can parse right out of the supplied string, so return it. |
| return str; |
| } |
| } |
| |
| bool PCRE::Arg::parse_long_radix(const char* str, |
| int n, |
| void* dest, |
| int radix) { |
| if (n == 0) return false; |
| char buf[kMaxNumberLength+1]; |
| str = TerminateNumber(buf, str, n); |
| char* end; |
| errno = 0; |
| long r = strtol(str, &end, radix); |
| if (end != str + n) return false; // Leftover junk |
| if (errno) return false; |
| if (dest == NULL) return true; |
| *(reinterpret_cast<long*>(dest)) = r; |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_ulong_radix(const char* str, |
| int n, |
| void* dest, |
| int radix) { |
| if (n == 0) return false; |
| char buf[kMaxNumberLength+1]; |
| str = TerminateNumber(buf, str, n); |
| if (str[0] == '-') { |
| // strtoul() will silently accept negative numbers and parse |
| // them. This module is more strict and treats them as errors. |
| return false; |
| } |
| |
| char* end; |
| errno = 0; |
| unsigned long r = strtoul(str, &end, radix); |
| if (end != str + n) return false; // Leftover junk |
| if (errno) return false; |
| if (dest == NULL) return true; |
| *(reinterpret_cast<unsigned long*>(dest)) = r; |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_short_radix(const char* str, |
| int n, |
| void* dest, |
| int radix) { |
| long r; |
| if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse |
| if ((short)r != r) return false; // Out of range |
| if (dest == NULL) return true; |
| *(reinterpret_cast<short*>(dest)) = r; |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_ushort_radix(const char* str, |
| int n, |
| void* dest, |
| int radix) { |
| unsigned long r; |
| if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse |
| if ((ushort)r != r) return false; // Out of range |
| if (dest == NULL) return true; |
| *(reinterpret_cast<unsigned short*>(dest)) = r; |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_int_radix(const char* str, |
| int n, |
| void* dest, |
| int radix) { |
| long r; |
| if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse |
| if ((int)r != r) return false; // Out of range |
| if (dest == NULL) return true; |
| *(reinterpret_cast<int*>(dest)) = r; |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_uint_radix(const char* str, |
| int n, |
| void* dest, |
| int radix) { |
| unsigned long r; |
| if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse |
| if ((uint)r != r) return false; // Out of range |
| if (dest == NULL) return true; |
| *(reinterpret_cast<unsigned int*>(dest)) = r; |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_longlong_radix(const char* str, |
| int n, |
| void* dest, |
| int radix) { |
| if (n == 0) return false; |
| char buf[kMaxNumberLength+1]; |
| str = TerminateNumber(buf, str, n); |
| char* end; |
| errno = 0; |
| int64 r = strtoll(str, &end, radix); |
| if (end != str + n) return false; // Leftover junk |
| if (errno) return false; |
| if (dest == NULL) return true; |
| *(reinterpret_cast<int64*>(dest)) = r; |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_ulonglong_radix(const char* str, |
| int n, |
| void* dest, |
| int radix) { |
| if (n == 0) return false; |
| char buf[kMaxNumberLength+1]; |
| str = TerminateNumber(buf, str, n); |
| if (str[0] == '-') { |
| // strtoull() will silently accept negative numbers and parse |
| // them. This module is more strict and treats them as errors. |
| return false; |
| } |
| char* end; |
| errno = 0; |
| uint64 r = strtoull(str, &end, radix); |
| if (end != str + n) return false; // Leftover junk |
| if (errno) return false; |
| if (dest == NULL) return true; |
| *(reinterpret_cast<uint64*>(dest)) = r; |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_double(const char* str, int n, void* dest) { |
| if (n == 0) return false; |
| static const int kMaxLength = 200; |
| char buf[kMaxLength]; |
| if (n >= kMaxLength) return false; |
| memcpy(buf, str, n); |
| buf[n] = '\0'; |
| errno = 0; |
| char* end; |
| double r = strtod(buf, &end); |
| if (end != buf + n) { |
| #ifdef COMPILER_MSVC |
| // Microsoft's strtod() doesn't handle inf and nan, so we have to |
| // handle it explicitly. Speed is not important here because this |
| // code is only called in unit tests. |
| bool pos = true; |
| const char* i = buf; |
| if ('-' == *i) { |
| pos = false; |
| ++i; |
| } else if ('+' == *i) { |
| ++i; |
| } |
| if (0 == stricmp(i, "inf") || 0 == stricmp(i, "infinity")) { |
| r = numeric_limits<double>::infinity(); |
| if (!pos) |
| r = -r; |
| } else if (0 == stricmp(i, "nan")) { |
| r = numeric_limits<double>::quiet_NaN(); |
| } else { |
| return false; |
| } |
| #else |
| return false; // Leftover junk |
| #endif |
| } |
| if (errno) return false; |
| if (dest == NULL) return true; |
| *(reinterpret_cast<double*>(dest)) = r; |
| return true; |
| } |
| |
| bool PCRE::Arg::parse_float(const char* str, int n, void* dest) { |
| double r; |
| if (!parse_double(str, n, &r)) return false; |
| if (dest == NULL) return true; |
| *(reinterpret_cast<float*>(dest)) = static_cast<float>(r); |
| return true; |
| } |
| |
| |
| #define DEFINE_INTEGER_PARSERS(name) \ |
| bool PCRE::Arg::parse_##name(const char* str, int n, void* dest) { \ |
| return parse_##name##_radix(str, n, dest, 10); \ |
| } \ |
| bool PCRE::Arg::parse_##name##_hex(const char* str, int n, void* dest) { \ |
| return parse_##name##_radix(str, n, dest, 16); \ |
| } \ |
| bool PCRE::Arg::parse_##name##_octal(const char* str, int n, void* dest) { \ |
| return parse_##name##_radix(str, n, dest, 8); \ |
| } \ |
| bool PCRE::Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \ |
| return parse_##name##_radix(str, n, dest, 0); \ |
| } |
| |
| DEFINE_INTEGER_PARSERS(short); |
| DEFINE_INTEGER_PARSERS(ushort); |
| DEFINE_INTEGER_PARSERS(int); |
| DEFINE_INTEGER_PARSERS(uint); |
| DEFINE_INTEGER_PARSERS(long); |
| DEFINE_INTEGER_PARSERS(ulong); |
| DEFINE_INTEGER_PARSERS(longlong); |
| DEFINE_INTEGER_PARSERS(ulonglong); |
| |
| #undef DEFINE_INTEGER_PARSERS |
| |
| } // namespace re2 |