Generates non-fatal failures and returns false if regex is invalid; otherwise returns true.
| 8465 | // Generates non-fatal failures and returns false if regex is invalid; |
| 8466 | // otherwise returns true. |
| 8467 | bool ValidateRegex(const char* regex) { |
| 8468 | if (regex == NULL) { |
| 8469 | // TODO(wan@google.com): fix the source file location in the |
| 8470 | // assertion failures to match where the regex is used in user |
| 8471 | // code. |
| 8472 | ADD_FAILURE() << "NULL is not a valid simple regular expression."; |
| 8473 | return false; |
| 8474 | } |
| 8475 | |
| 8476 | bool is_valid = true; |
| 8477 | |
| 8478 | // True iff ?, *, or + can follow the previous atom. |
| 8479 | bool prev_repeatable = false; |
| 8480 | for (int i = 0; regex[i]; i++) { |
| 8481 | if (regex[i] == '\\') { // An escape sequence |
| 8482 | i++; |
| 8483 | if (regex[i] == '\0') { |
| 8484 | ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) |
| 8485 | << "'\\' cannot appear at the end."; |
| 8486 | return false; |
| 8487 | } |
| 8488 | |
| 8489 | if (!IsValidEscape(regex[i])) { |
| 8490 | ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) |
| 8491 | << "invalid escape sequence \"\\" << regex[i] << "\"."; |
| 8492 | is_valid = false; |
| 8493 | } |
| 8494 | prev_repeatable = true; |
| 8495 | } else { // Not an escape sequence. |
| 8496 | const char ch = regex[i]; |
| 8497 | |
| 8498 | if (ch == '^' && i > 0) { |
| 8499 | ADD_FAILURE() << FormatRegexSyntaxError(regex, i) |
| 8500 | << "'^' can only appear at the beginning."; |
| 8501 | is_valid = false; |
| 8502 | } else if (ch == '$' && regex[i + 1] != '\0') { |
| 8503 | ADD_FAILURE() << FormatRegexSyntaxError(regex, i) |
| 8504 | << "'$' can only appear at the end."; |
| 8505 | is_valid = false; |
| 8506 | } else if (IsInSet(ch, "()[]{}|")) { |
| 8507 | ADD_FAILURE() << FormatRegexSyntaxError(regex, i) |
| 8508 | << "'" << ch << "' is unsupported."; |
| 8509 | is_valid = false; |
| 8510 | } else if (IsRepeat(ch) && !prev_repeatable) { |
| 8511 | ADD_FAILURE() << FormatRegexSyntaxError(regex, i) |
| 8512 | << "'" << ch << "' can only follow a repeatable token."; |
| 8513 | is_valid = false; |
| 8514 | } |
| 8515 | |
| 8516 | prev_repeatable = !IsInSet(ch, "^$?*+"); |
| 8517 | } |
| 8518 | } |
| 8519 | |
| 8520 | return is_valid; |
| 8521 | } |
| 8522 | |
| 8523 | // Matches a repeated regex atom followed by a valid simple regular |
| 8524 | // expression. The regex atom is defined as c if escaped is false, |
no test coverage detected