29 |
// |
// |
30 |
// Author: Sanjay Ghemawat |
// Author: Sanjay Ghemawat |
31 |
|
|
32 |
|
#ifdef HAVE_CONFIG_H |
33 |
|
# include <config.h> |
34 |
|
#endif |
35 |
|
|
36 |
#include <stdlib.h> |
#include <stdlib.h> |
37 |
#include <stdio.h> |
#include <stdio.h> |
38 |
#include <ctype.h> |
#include <ctype.h> |
40 |
#include <assert.h> |
#include <assert.h> |
41 |
#include <errno.h> |
#include <errno.h> |
42 |
#include <string> |
#include <string> |
43 |
#include "config.h" |
#include <algorithm> |
44 |
// We need this to compile the proper dll on windows/msys. This is copied |
// We need this to compile the proper dll on windows/msys. This is copied |
45 |
// from pcre_internal.h. It would probably be better just to include that. |
// from pcre_internal.h. It would probably be better just to include that. |
46 |
#define PCRE_DEFINITION /* Win32 __declspec(export) trigger for .dll */ |
#define PCRE_DEFINITION /* Win32 __declspec(export) trigger for .dll */ |
64 |
// If the user doesn't ask for any options, we just use this one |
// If the user doesn't ask for any options, we just use this one |
65 |
static RE_Options default_options; |
static RE_Options default_options; |
66 |
|
|
67 |
void RE::Init(const char* pat, const RE_Options* options) { |
void RE::Init(const string& pat, const RE_Options* options) { |
68 |
pattern_ = pat; |
pattern_ = pat; |
69 |
if (options == NULL) { |
if (options == NULL) { |
70 |
options_ = default_options; |
options_ = default_options; |
81 |
// conservative in that it may treat some "simple" patterns |
// conservative in that it may treat some "simple" patterns |
82 |
// as "complex" (e.g., if the vertical bar is in a character |
// as "complex" (e.g., if the vertical bar is in a character |
83 |
// class or is escaped). But it seems good enough. |
// class or is escaped). But it seems good enough. |
84 |
if (strchr(pat, '|') == NULL) { |
if (strchr(pat.c_str(), '|') == NULL) { |
85 |
// Simple pattern: we can use position-based checks to perform |
// Simple pattern: we can use position-based checks to perform |
86 |
// fully anchored matches |
// fully anchored matches |
87 |
re_full_ = re_partial_; |
re_full_ = re_partial_; |
92 |
} |
} |
93 |
} |
} |
94 |
|
|
95 |
RE::~RE() { |
void RE::Cleanup() { |
96 |
if (re_full_ != NULL && re_full_ != re_partial_) (*pcre_free)(re_full_); |
if (re_full_ != NULL && re_full_ != re_partial_) (*pcre_free)(re_full_); |
97 |
if (re_partial_ != NULL) (*pcre_free)(re_partial_); |
if (re_partial_ != NULL) (*pcre_free)(re_partial_); |
98 |
if (error_ != &empty_string) delete error_; |
if (error_ != &empty_string) delete error_; |
99 |
} |
} |
100 |
|
|
101 |
|
|
102 |
|
RE::~RE() { |
103 |
|
Cleanup(); |
104 |
|
} |
105 |
|
|
106 |
|
|
107 |
pcre* RE::Compile(Anchor anchor) { |
pcre* RE::Compile(Anchor anchor) { |
108 |
// First, convert RE_Options into pcre options |
// First, convert RE_Options into pcre options |
109 |
int pcre_options = 0; |
int pcre_options = 0; |
110 |
if (options_.utf8()) |
pcre_options = options_.all_options(); |
|
pcre_options |= PCRE_UTF8; |
|
111 |
|
|
112 |
// Special treatment for anchoring. This is needed because at |
// Special treatment for anchoring. This is needed because at |
113 |
// runtime pcre only provides an option for anchoring at the |
// runtime pcre only provides an option for anchoring at the |
341 |
return true; |
return true; |
342 |
} |
} |
343 |
|
|
344 |
|
// Returns PCRE_NEWLINE_CRLF, PCRE_NEWLINE_CR, or PCRE_NEWLINE_LF. |
345 |
|
// Note that PCRE_NEWLINE_CRLF is defined to be P_N_CR | P_N_LF. |
346 |
|
static int NewlineMode(int pcre_options) { |
347 |
|
// TODO: if we can make it threadsafe, cache this var |
348 |
|
int newline_mode = 0; |
349 |
|
/* if (newline_mode) return newline_mode; */ // do this once it's cached |
350 |
|
if (pcre_options & (PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF)) { |
351 |
|
newline_mode = (pcre_options & |
352 |
|
(PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF)); |
353 |
|
} else { |
354 |
|
int newline; |
355 |
|
pcre_config(PCRE_CONFIG_NEWLINE, &newline); |
356 |
|
if (newline == 10) |
357 |
|
newline_mode = PCRE_NEWLINE_LF; |
358 |
|
else if (newline == 13) |
359 |
|
newline_mode = PCRE_NEWLINE_CR; |
360 |
|
else if (newline == 3338) |
361 |
|
newline_mode = PCRE_NEWLINE_CRLF; |
362 |
|
else |
363 |
|
assert("" == "Unexpected return value from pcre_config(NEWLINE)"); |
364 |
|
} |
365 |
|
return newline_mode; |
366 |
|
} |
367 |
|
|
368 |
int RE::GlobalReplace(const StringPiece& rewrite, |
int RE::GlobalReplace(const StringPiece& rewrite, |
369 |
string *str) const { |
string *str) const { |
370 |
int count = 0; |
int count = 0; |
383 |
if (matchstart == matchend && matchstart == lastend) { |
if (matchstart == matchend && matchstart == lastend) { |
384 |
// advance one character if we matched an empty string at the same |
// advance one character if we matched an empty string at the same |
385 |
// place as the last match occurred |
// place as the last match occurred |
386 |
if (start < static_cast<int>(str->length())) |
matchend = start + 1; |
387 |
out.push_back((*str)[start]); |
// If the current char is CR and we're in CRLF mode, skip LF too. |
388 |
start++; |
// Note it's better to call pcre_fullinfo() than to examine |
389 |
|
// all_options(), since options_ could have changed bewteen |
390 |
|
// compile-time and now, but this is simpler and safe enough. |
391 |
|
if (start+1 < static_cast<int>(str->length()) && |
392 |
|
(*str)[start] == '\r' && (*str)[start+1] == '\n' && |
393 |
|
NewlineMode(options_.all_options()) == PCRE_NEWLINE_CRLF) { |
394 |
|
matchend++; |
395 |
|
} |
396 |
|
// We also need to advance more than one char if we're in utf8 mode. |
397 |
|
#ifdef SUPPORT_UTF8 |
398 |
|
if (options_.utf8()) { |
399 |
|
while (matchend < static_cast<int>(str->length()) && |
400 |
|
((*str)[matchend] & 0xc0) == 0x80) |
401 |
|
matchend++; |
402 |
|
} |
403 |
|
#endif |
404 |
|
if (matchend <= static_cast<int>(str->length())) |
405 |
|
out.append(*str, start, matchend - start); |
406 |
|
start = matchend; |
407 |
} else { |
} else { |
408 |
out.append(*str, start, matchstart - start); |
out.append(*str, start, matchstart - start); |
409 |
Rewrite(&out, rewrite, *str, vec, matches); |
Rewrite(&out, rewrite, *str, vec, matches); |
429 |
int matches = TryMatch(text, 0, UNANCHORED, vec, kVecSize); |
int matches = TryMatch(text, 0, UNANCHORED, vec, kVecSize); |
430 |
if (matches == 0) |
if (matches == 0) |
431 |
return false; |
return false; |
432 |
out->clear(); |
out->erase(); |
433 |
return Rewrite(out, rewrite, text, vec, matches); |
return Rewrite(out, rewrite, text, vec, matches); |
434 |
} |
} |
435 |
|
|
436 |
|
/*static*/ string RE::QuoteMeta(const StringPiece& unquoted) { |
437 |
|
string result; |
438 |
|
|
439 |
|
// Escape any ascii character not in [A-Za-z_0-9]. |
440 |
|
// |
441 |
|
// Note that it's legal to escape a character even if it has no |
442 |
|
// special meaning in a regular expression -- so this function does |
443 |
|
// that. (This also makes it identical to the perl function of the |
444 |
|
// same name; see `perldoc -f quotemeta`.) |
445 |
|
for (int ii = 0; ii < unquoted.size(); ++ii) { |
446 |
|
// Note that using 'isalnum' here raises the benchmark time from |
447 |
|
// 32ns to 58ns: |
448 |
|
if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') && |
449 |
|
(unquoted[ii] < 'A' || unquoted[ii] > 'Z') && |
450 |
|
(unquoted[ii] < '0' || unquoted[ii] > '9') && |
451 |
|
unquoted[ii] != '_' && |
452 |
|
// If this is the part of a UTF8 or Latin1 character, we need |
453 |
|
// to copy this byte without escaping. Experimentally this is |
454 |
|
// what works correctly with the regexp library. |
455 |
|
!(unquoted[ii] & 128)) { |
456 |
|
result += '\\'; |
457 |
|
} |
458 |
|
result += unquoted[ii]; |
459 |
|
} |
460 |
|
|
461 |
|
return result; |
462 |
|
} |
463 |
|
|
464 |
/***** Actual matching and rewriting code *****/ |
/***** Actual matching and rewriting code *****/ |
465 |
|
|
466 |
int RE::TryMatch(const StringPiece& text, |
int RE::TryMatch(const StringPiece& text, |
476 |
|
|
477 |
pcre_extra extra = { 0 }; |
pcre_extra extra = { 0 }; |
478 |
if (options_.match_limit() > 0) { |
if (options_.match_limit() > 0) { |
479 |
extra.flags = PCRE_EXTRA_MATCH_LIMIT; |
extra.flags |= PCRE_EXTRA_MATCH_LIMIT; |
480 |
extra.match_limit = options_.match_limit(); |
extra.match_limit = options_.match_limit(); |
481 |
} |
} |
482 |
|
if (options_.match_limit_recursion() > 0) { |
483 |
|
extra.flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; |
484 |
|
extra.match_limit_recursion = options_.match_limit_recursion(); |
485 |
|
} |
486 |
int rc = pcre_exec(re, // The regular expression object |
int rc = pcre_exec(re, // The regular expression object |
487 |
&extra, |
&extra, |
488 |
text.data(), |
(text.data() == NULL) ? "" : text.data(), |
489 |
text.size(), |
text.size(), |
490 |
startpos, |
startpos, |
491 |
(anchor == UNANCHORED) ? 0 : PCRE_ANCHORED, |
(anchor == UNANCHORED) ? 0 : PCRE_ANCHORED, |
532 |
|
|
533 |
*consumed = vec[1]; |
*consumed = vec[1]; |
534 |
|
|
535 |
if (args == NULL) { |
if (n == 0 || args == NULL) { |
536 |
// We are not interested in results |
// We are not interested in results |
537 |
return true; |
return true; |
538 |
} |
} |
539 |
|
|
540 |
|
if (NumberOfCapturingGroups() < n) { |
541 |
|
// RE has fewer capturing groups than number of arg pointers passed in |
542 |
|
return false; |
543 |
|
} |
544 |
|
|
545 |
// If we got here, we must have matched the whole pattern. |
// If we got here, we must have matched the whole pattern. |
546 |
// We do not need (can not do) any more checks on the value of 'matches' here |
// We do not need (can not do) any more checks on the value of 'matches' here |
547 |
// -- see the comment for TryMatch. |
// -- see the comment for TryMatch. |
605 |
|
|
606 |
// Return the number of capturing subpatterns, or -1 if the |
// Return the number of capturing subpatterns, or -1 if the |
607 |
// regexp wasn't valid on construction. |
// regexp wasn't valid on construction. |
608 |
int RE::NumberOfCapturingGroups() { |
int RE::NumberOfCapturingGroups() const { |
609 |
if (re_partial_ == NULL) return -1; |
if (re_partial_ == NULL) return -1; |
610 |
|
|
611 |
int result; |
int result; |
701 |
if (n == 0) return false; |
if (n == 0) return false; |
702 |
char buf[kMaxNumberLength+1]; |
char buf[kMaxNumberLength+1]; |
703 |
str = TerminateNumber(buf, str, n); |
str = TerminateNumber(buf, str, n); |
704 |
|
if (str[0] == '-') return false; // strtoul() on a negative number?! |
705 |
char* end; |
char* end; |
706 |
errno = 0; |
errno = 0; |
707 |
unsigned long r = strtoul(str, &end, radix); |
unsigned long r = strtoul(str, &end, radix); |
791 |
if (n == 0) return false; |
if (n == 0) return false; |
792 |
char buf[kMaxNumberLength+1]; |
char buf[kMaxNumberLength+1]; |
793 |
str = TerminateNumber(buf, str, n); |
str = TerminateNumber(buf, str, n); |
794 |
|
if (str[0] == '-') return false; // strtoull() on a negative number?! |
795 |
char* end; |
char* end; |
796 |
errno = 0; |
errno = 0; |
797 |
#if defined HAVE_STRTOQ |
#if defined HAVE_STRTOQ |
846 |
return parse_##name##_radix(str, n, dest, 0); \ |
return parse_##name##_radix(str, n, dest, 0); \ |
847 |
} |
} |
848 |
|
|
849 |
DEFINE_INTEGER_PARSERS(short); |
DEFINE_INTEGER_PARSERS(short) /* */ |
850 |
DEFINE_INTEGER_PARSERS(ushort); |
DEFINE_INTEGER_PARSERS(ushort) /* */ |
851 |
DEFINE_INTEGER_PARSERS(int); |
DEFINE_INTEGER_PARSERS(int) /* Don't use semicolons after these */ |
852 |
DEFINE_INTEGER_PARSERS(uint); |
DEFINE_INTEGER_PARSERS(uint) /* statements because they can cause */ |
853 |
DEFINE_INTEGER_PARSERS(long); |
DEFINE_INTEGER_PARSERS(long) /* compiler warnings if the checking */ |
854 |
DEFINE_INTEGER_PARSERS(ulong); |
DEFINE_INTEGER_PARSERS(ulong) /* level is turned up high enough. */ |
855 |
DEFINE_INTEGER_PARSERS(longlong); |
DEFINE_INTEGER_PARSERS(longlong) /* */ |
856 |
DEFINE_INTEGER_PARSERS(ulonglong); |
DEFINE_INTEGER_PARSERS(ulonglong) /* */ |
857 |
|
|
858 |
#undef DEFINE_INTEGER_PARSERS |
#undef DEFINE_INTEGER_PARSERS |
859 |
|
|