// Copyright (c) 2010, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Author: Sanjay Ghemawat #include <stdlib.h> #include <stdio.h> #include <ctype.h> #include <limits.h> /* for SHRT_MIN, USHRT_MAX, etc */ #include <string.h> /* for memcpy */ #include <assert.h> #include <errno.h> #include <string> #include <algorithm> #include "pcrecpp_internal.h" #include "pcre2.h" #include "pcrecpp.h" #include "pcre_stringpiece.h" namespace pcrecpp { // If the user doesn't ask for any options, we just use this one static RE_Options default_options; void RE::Init(const string& pat, const RE_Options* options) { pattern_ = pat; if (options == NULL) { options_ = default_options; } else { options_ = *options; } error_ = ""; re_full_ = NULL; re_partial_ = NULL; re_partial_ = Compile(UNANCHORED); if (re_partial_ != NULL) { re_full_ = Compile(ANCHOR_BOTH); } } void RE::Cleanup() { if (re_full_ != NULL) pcre2_code_free(re_full_); if (re_partial_ != NULL) pcre2_code_free(re_partial_); error_ = ""; } RE::~RE() { Cleanup(); } static void format_pcre_error(int error, string & str) { PCRE2_UCHAR8 buffer[256]; auto rc = pcre2_get_error_message(error, buffer, 256); str.assign(reinterpret_cast<string::value_type*>(buffer)); if (rc == PCRE2_ERROR_NOMEMORY) { str.append("..."); } } pcre2_code* RE::Compile(Anchor anchor) { // First, convert RE_Options into pcre options int pcre_options = 0; pcre_options = options_.all_options(); typedef std::unique_ptr<pcre2_compile_context, decltype(pcre2_compile_context_free)*> compile_context_ptr; compile_context_ptr compile_context(NULL, pcre2_compile_context_free); // As of pcre2 the newline mode must be passed through the compile context. // So we only need one if the newline mode is actually set. if (options_.newline_mode()) { compile_context = compile_context_ptr(pcre2_compile_context_create(NULL), pcre2_compile_context_free); if (!compile_context) { error_ = "Unable to allocate memory for pcre2_compile_congext"; return NULL; } if (pcre2_set_newline(compile_context.get(), options_.newline_mode()) == PCRE2_ERROR_BADDATA) { error_ = "REOptions: bad newline mode given"; return NULL; } } // Special treatment for anchoring. This is needed because at // runtime pcre only provides an option for anchoring at the // beginning of a string (unless you use offset). // // 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. int compile_error; PCRE2_SIZE eoffset; pcre2_code* re; if (anchor != ANCHOR_BOTH) { re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>(pattern_.c_str()), pattern_.length(), pcre_options, &compile_error, &eoffset, compile_context.get()); } else { // Tack a '\z' at the end of RE. 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 = pcre2_compile(reinterpret_cast<PCRE2_SPTR>(wrapped.c_str()), wrapped.length(), pcre_options, &compile_error, &eoffset, compile_context.get()); } if (re == NULL) { format_pcre_error(compile_error, error_); } return re; } /***** Matching interfaces *****/ bool RE::Replace(const StringPiece& rewrite, string *str) const { pcre2_match_data_ptr match_data; int matches = TryMatch(*str, 0, UNANCHORED, true, match_data); if (matches == 0) return false; string s; if (!Rewrite(&s, rewrite, *str, match_data)) return false; auto vec = pcre2_get_ovector_pointer(match_data.get()); assert(vec[0] >= 0); assert(vec[1] >= 0); str->replace(vec[0], vec[1] - vec[0], s); return true; } static bool is_multi_char_newline_mode(int value) { switch (value) { case PCRE2_NEWLINE_CR: case PCRE2_NEWLINE_LF: return false; case PCRE2_NEWLINE_CRLF: case PCRE2_NEWLINE_ANY: case PCRE2_NEWLINE_ANYCRLF: return true; default: return false; } } int RE::GlobalReplace(const StringPiece& rewrite, string *str) const { int count = 0; string out; int start = 0; bool last_match_was_empty_string = false; pcre2_match_data_ptr match_data; while (start <= static_cast<int>(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 = TryMatch(*str, start, ANCHOR_START, false, match_data); if (matches <= 0) { int matchend = start + 1; // advance one character. // If the current char is CR and we're in CRLF mode, skip LF too. // Note it's better to call pcre2_pattern_info() than to examine // all_options(), since options_ could have changed between // compile-time and now, but this is simpler and safe enough. // Modified by PH to add ANY and ANYCRLF. if (matchend < static_cast<int>(str->length()) && (*str)[start] == '\r' && (*str)[matchend] == '\n' && is_multi_char_newline_mode(options_.newline_mode())) { matchend++; } // We also need to advance more than one char if we're in utf8 mode. #ifdef SUPPORT_UTF8 if (options_.utf8()) { while (matchend < static_cast<int>(str->length()) && ((*str)[matchend] & 0xc0) == 0x80) matchend++; } #endif if (start < static_cast<int>(str->length())) out.append(*str, start, matchend - start); start = matchend; last_match_was_empty_string = false; continue; } } else { matches = TryMatch(*str, start, UNANCHORED, true, match_data); if (matches <= 0) break; } auto vec = pcre2_get_ovector_pointer(match_data.get()); int matchstart = vec[0], matchend = vec[1]; assert(matchstart >= start); assert(matchend >= matchstart); out.append(*str, start, matchstart - start); Rewrite(&out, rewrite, *str, match_data); start = matchend; count++; last_match_was_empty_string = (matchstart == matchend); } if (count == 0) return 0; if (start < static_cast<int>(str->length())) out.append(*str, start, str->length() - start); swap(out, *str); return count; } bool RE::Extract(const StringPiece& rewrite, const StringPiece& text, string *out) const { pcre2_match_data_ptr match_data; int matches = TryMatch(text, 0, UNANCHORED, true, match_data); if (matches == 0) return false; out->erase(); return Rewrite(out, rewrite, text, match_data); } /*static*/ string RE::QuoteMeta(const StringPiece& unquoted) { string result; // 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; see `perldoc -f quotemeta`.) The one exception is // escaping NUL: rather than doing backslash + NUL, like perl does, // we do '\0', because pcre itself doesn't take embedded NUL chars. for (int ii = 0; ii < unquoted.size(); ++ii) { // Note that using 'isalnum' here raises the benchmark time from // 32ns to 58ns: if (unquoted[ii] == '\0') { result += "\\0"; } else 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)) { result += '\\'; result += unquoted[ii]; } else { result += unquoted[ii]; } } return result; } /***** Actual matching and rewriting code *****/ int RE::TryMatch(const StringPiece& text, int startpos, Anchor anchor, bool empty_ok, pcre2_match_data_ptr & match_data) const { typedef std::unique_ptr<pcre2_match_context, decltype(pcre2_match_context_free)*> match_context_ptr; pcre2_code* re = (anchor == ANCHOR_BOTH) ? re_full_ : re_partial_; if (re == NULL) { //fprintf(stderr, "Matching against invalid re: %s\n", error_->c_str()); return 0; } match_context_ptr match_context = match_context_ptr( pcre2_match_context_create(NULL), pcre2_match_context_free); if (!match_context) return 0; if (options_.match_limit() > 0) { pcre2_set_match_limit(match_context.get(), options_.match_limit()); } if (options_.match_limit_recursion() > 0) { pcre2_set_recursion_limit(match_context.get(), options_.match_limit_recursion()); } match_data = pcre2_match_data_ptr( pcre2_match_data_create_from_pattern(re, NULL), pcre2_match_data_free); if (!match_data) { return 0; } // int options = 0; // Changed by PH as a result of bugzilla #1288 int options = (options_.all_options() & PCRE2_NO_UTF_CHECK); if (anchor != UNANCHORED) options |= PCRE2_ANCHORED; if (!empty_ok) options |= PCRE2_NOTEMPTY; int rc = pcre2_match( re, reinterpret_cast<PCRE2_SPTR>((text.empty()) ? "" : text.data()), text.size(), startpos, options, match_data.get(), match_context.get()); // Handle errors if (rc == PCRE2_ERROR_NOMATCH) { return 0; } if (rc == PCRE2_ERROR_PARTIAL) { // not sure what to do with partial yet return 0; } else if (rc < 0) { // For any other error condition also return 0. return 0; } return rc; // return number of matches found } bool RE::DoMatchImpl(const StringPiece& text, Anchor anchor, int* consumed, const Arg* args, int n) const { pcre2_match_data_ptr match_data; int matches = TryMatch(text, 0, anchor, true, match_data); assert(matches >= 0); // TryMatch never returns negatives if (matches == 0) return false; auto vec = pcre2_get_ovector_pointer(match_data.get()); // allow for NULL if (consumed != NULL) *consumed = vec[1]; if (n == 0 || args == NULL) { // We are not interested in results return true; } if (NumberOfCapturingGroups() < n) { // RE 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 RE::DoMatch(const StringPiece& text, Anchor anchor, int* consumed, Arg const args[], int n) const { assert(n >= 0); bool retval = DoMatchImpl(text, anchor, consumed, args, n); return retval; } bool RE::Rewrite(string *out, const StringPiece &rewrite, const StringPiece &text, pcre2_match_data_ptr const & match_data) const { auto veclen = pcre2_get_ovector_count(match_data.get()); auto vec = pcre2_get_ovector_pointer(match_data.get()); for (const char *s = rewrite.data(), *end = s + rewrite.size(); s < end; s++) { int c = *s; if (c == '\\') { c = *++s; if (isdigit(c)) { decltype(veclen) n = (c - '0'); if (n >= veclen) { //fprintf(stderr, requested group %d in regexp %.*s\n", // n, rewrite.size(), 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 += '\\'; } else { //fprintf(stderr, "invalid rewrite pattern: %.*s\n", // rewrite.size(), rewrite.data()); return false; } } else { *out += c; } } return true; } // Return the number of capturing subpatterns, or -1 if the // regexp wasn't valid on construction. int RE::NumberOfCapturingGroups() const { if (re_partial_ == NULL) return -1; int result; int pcre_retval = pcre2_pattern_info(re_partial_, PCRE2_INFO_CAPTURECOUNT, &result); assert(pcre_retval == 0); return result; } /***** Parsers for various types *****/ bool Arg::parse_null(const char* str, int n, void* dest) { (void)str; (void)n; // We fail if somebody asked us to store into a non-NULL void* pointer return (dest == NULL); } bool 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 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 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 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; // REQUIRES "buf" must have length at least kMaxNumberLength+1 // REQUIRES "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 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 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] == '-') return false; // strtoul() on a negative number?! 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 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 (r < SHRT_MIN || r > SHRT_MAX) return false; // Out of range if (dest == NULL) return true; *(reinterpret_cast<short*>(dest)) = static_cast<short>(r); return true; } bool 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 (r > USHRT_MAX) return false; // Out of range if (dest == NULL) return true; *(reinterpret_cast<unsigned short*>(dest)) = static_cast<unsigned short>(r); return true; } bool 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 (r < INT_MIN || r > INT_MAX) return false; // Out of range if (dest == NULL) return true; *(reinterpret_cast<int*>(dest)) = r; return true; } bool 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 (r > UINT_MAX) return false; // Out of range if (dest == NULL) return true; *(reinterpret_cast<unsigned int*>(dest)) = r; return true; } bool Arg::parse_longlong_radix(const char* str, int n, void* dest, int radix) { #ifndef HAVE_LONG_LONG return false; #else if (n == 0) return false; char buf[kMaxNumberLength+1]; str = TerminateNumber(buf, str, n); char* end; errno = 0; #if defined HAVE_STRTOQ long long r = strtoq(str, &end, radix); #elif defined HAVE_STRTOLL long long r = strtoll(str, &end, radix); #elif defined HAVE__STRTOI64 long long r = _strtoi64(str, &end, radix); #elif defined HAVE_STRTOIMAX long long r = strtoimax(str, &end, radix); #else #error parse_longlong_radix: cannot convert input to a long-long #endif if (end != str + n) return false; // Leftover junk if (errno) return false; if (dest == NULL) return true; *(reinterpret_cast<long long*>(dest)) = r; return true; #endif /* HAVE_LONG_LONG */ } bool Arg::parse_ulonglong_radix(const char* str, int n, void* dest, int radix) { #ifndef HAVE_UNSIGNED_LONG_LONG return false; #else if (n == 0) return false; char buf[kMaxNumberLength+1]; str = TerminateNumber(buf, str, n); if (str[0] == '-') return false; // strtoull() on a negative number?! char* end; errno = 0; #if defined HAVE_STRTOQ unsigned long long r = strtouq(str, &end, radix); #elif defined HAVE_STRTOLL unsigned long long r = strtoull(str, &end, radix); #elif defined HAVE__STRTOI64 unsigned long long r = _strtoui64(str, &end, radix); #elif defined HAVE_STRTOIMAX unsigned long long r = strtoumax(str, &end, radix); #else #error parse_ulonglong_radix: cannot convert input to a long-long #endif if (end != str + n) return false; // Leftover junk if (errno) return false; if (dest == NULL) return true; *(reinterpret_cast<unsigned long long*>(dest)) = r; return true; #endif /* HAVE_UNSIGNED_LONG_LONG */ } bool 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) return false; // Leftover junk if (errno) return false; if (dest == NULL) return true; *(reinterpret_cast<double*>(dest)) = r; return true; } bool 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 Arg::parse_##name(const char* str, int n, void* dest) { \ return parse_##name##_radix(str, n, dest, 10); \ } \ bool Arg::parse_##name##_hex(const char* str, int n, void* dest) { \ return parse_##name##_radix(str, n, dest, 16); \ } \ bool Arg::parse_##name##_octal(const char* str, int n, void* dest) { \ return parse_##name##_radix(str, n, dest, 8); \ } \ bool 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) /* Don't use semicolons after these */ DEFINE_INTEGER_PARSERS(uint) /* statements because they can cause */ DEFINE_INTEGER_PARSERS(long) /* compiler warnings if the checking */ DEFINE_INTEGER_PARSERS(ulong) /* level is turned up high enough. */ DEFINE_INTEGER_PARSERS(longlong) /* */ DEFINE_INTEGER_PARSERS(ulonglong) /* */ #undef DEFINE_INTEGER_PARSERS } // namespace pcrecpp