Report errors on invalid replacements in string replace

If the replacement in `string replace` is invalid, prior to this fix we would
enter into an infinite loop trying to parse it. Instead report errors correctly.

Fixes #3381
This commit is contained in:
ridiculousfish
2018-09-27 22:28:39 -04:00
parent ae54b66799
commit a1728d61af
4 changed files with 64 additions and 65 deletions

View File

@@ -133,7 +133,7 @@ class arg_iterator_t {
}
}
};
}
} // namespace
// This is used by the string subcommands to communicate with the option parser which flags are
// valid and get the result of parsing the command for flags.
@@ -411,32 +411,23 @@ static wcstring construct_short_opts(options_t *opts) { //!OCLINT(high npath co
// Note that several long flags share the same short flag. That is okay. The caller is expected
// to indicate that a max of one of the long flags sharing a short flag is valid.
static const struct woption long_options[] = {{L"all", no_argument, NULL, 'a'},
{L"chars", required_argument, NULL, 'c'},
{L"count", required_argument, NULL, 'n'},
{L"entire", no_argument, NULL, 'e'},
{L"filter", no_argument, NULL, 'f'},
{L"ignore-case", no_argument, NULL, 'i'},
{L"index", no_argument, NULL, 'n'},
{L"invert", no_argument, NULL, 'v'},
{L"left", no_argument, NULL, 'l'},
{L"length", required_argument, NULL, 'l'},
{L"max", required_argument, NULL, 'm'},
{L"no-empty", no_argument, NULL, 'n'},
{L"no-newline", no_argument, NULL, 'N'},
{L"no-quoted", no_argument, NULL, 'n'},
{L"quiet", no_argument, NULL, 'q'},
{L"regex", no_argument, NULL, 'r'},
{L"right", no_argument, NULL, 'r'},
{L"start", required_argument, NULL, 's'},
{L"style", required_argument, NULL, 1},
{NULL, 0, NULL, 0}};
static const struct woption long_options[] = {
{L"all", no_argument, NULL, 'a'}, {L"chars", required_argument, NULL, 'c'},
{L"count", required_argument, NULL, 'n'}, {L"entire", no_argument, NULL, 'e'},
{L"filter", no_argument, NULL, 'f'}, {L"ignore-case", no_argument, NULL, 'i'},
{L"index", no_argument, NULL, 'n'}, {L"invert", no_argument, NULL, 'v'},
{L"left", no_argument, NULL, 'l'}, {L"length", required_argument, NULL, 'l'},
{L"max", required_argument, NULL, 'm'}, {L"no-empty", no_argument, NULL, 'n'},
{L"no-newline", no_argument, NULL, 'N'}, {L"no-quoted", no_argument, NULL, 'n'},
{L"quiet", no_argument, NULL, 'q'}, {L"regex", no_argument, NULL, 'r'},
{L"right", no_argument, NULL, 'r'}, {L"start", required_argument, NULL, 's'},
{L"style", required_argument, NULL, 1}, {NULL, 0, NULL, 0}};
static std::unordered_map<char, decltype(*handle_flag_N)> flag_to_function = {
{'N', handle_flag_N}, {'a', handle_flag_a}, {'c', handle_flag_c}, {'e', handle_flag_e},
{'f', handle_flag_f}, {'i', handle_flag_i}, {'l', handle_flag_l}, {'m', handle_flag_m},
{'n', handle_flag_n}, {'q', handle_flag_q}, {'r', handle_flag_r}, {'s', handle_flag_s},
{'v', handle_flag_v}, {1, handle_flag_1} };
{'v', handle_flag_v}, {1, handle_flag_1}};
/// Parse the arguments for flags recognized by a specific string subcommand.
static int parse_opts(options_t *opts, int *optind, int n_req_args, int argc, wchar_t **argv,
@@ -689,8 +680,9 @@ struct compiled_regex_t {
int err_code = 0;
PCRE2_SIZE err_offset = 0;
code = pcre2_compile(PCRE2_SPTR(pattern.c_str()), pattern.length(),
options | (ignore_case ? PCRE2_CASELESS : 0), &err_code, &err_offset, 0);
code =
pcre2_compile(PCRE2_SPTR(pattern.c_str()), pattern.length(),
options | (ignore_case ? PCRE2_CASELESS : 0), &err_code, &err_offset, 0);
if (code == 0) {
string_error(streams, _(L"%ls: Regular expression compile error: %ls\n"), argv0,
pcre2_strerror(err_code).c_str());
@@ -789,7 +781,7 @@ class pcre2_matcher_t : public string_matcher_t {
// See pcre2demo.c for an explanation of this logic.
PCRE2_SIZE arglen = arg.length();
int rc = report_match(
arg, pcre2_match(regex.code, PCRE2_SPTR(arg.c_str()), arglen, 0, 0, regex.match, 0));
arg, pcre2_match(regex.code, PCRE2_SPTR(arg.c_str()), arglen, 0, 0, regex.match, 0));
if (rc < 0) { // pcre2 match error.
return false;
} else if (rc == 0) { // no match
@@ -815,8 +807,8 @@ class pcre2_matcher_t : public string_matcher_t {
options = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED;
}
rc = report_match(arg, pcre2_match(regex.code, PCRE2_SPTR(arg.c_str()), arglen, offset, options,
regex.match, 0));
rc = report_match(arg, pcre2_match(regex.code, PCRE2_SPTR(arg.c_str()), arglen, offset,
options, regex.match, 0));
if (rc < 0) {
return false;
}
@@ -955,7 +947,8 @@ bool literal_replacer_t::replace_matches(const wcstring &arg) {
const wchar_t *cur = arg.c_str();
const wchar_t *end = cur + arg.size();
while (cur < end) {
if ((opts.all || !replacement_occurred) && cmp_func(cur, pattern.c_str(), patlen) == 0) {
if ((opts.all || !replacement_occurred) &&
cmp_func(cur, pattern.c_str(), patlen) == 0) {
result += replacement;
cur += patlen;
replacement_occurred = true;
@@ -978,7 +971,7 @@ bool literal_replacer_t::replace_matches(const wcstring &arg) {
/// A return value of true means all is well (even if no replacements were performed), false
/// indicates an unrecoverable error.
bool regex_replacer_t::replace_matches(const wcstring &arg) {
if (!regex.code) return false; // pcre2_compile() failed
if (!regex.code) return false; // pcre2_compile() failed
if (!replacement) return false; // replacement was an invalid string
uint32_t options = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH | PCRE2_SUBSTITUTE_EXTENDED |
@@ -1076,9 +1069,11 @@ static int string_split_maybe0(parser_t &parser, io_streams_t &streams, int argc
arg_iterator_t aiter(argv, optind, streams, is_split0);
while (const wcstring *arg = aiter.nextstr()) {
if (opts.right) {
split_about(arg->rbegin(), arg->rend(), sep.rbegin(), sep.rend(), &splits, opts.max, opts.no_empty);
split_about(arg->rbegin(), arg->rend(), sep.rbegin(), sep.rend(), &splits, opts.max,
opts.no_empty);
} else {
split_about(arg->begin(), arg->end(), sep.begin(), sep.end(), &splits, opts.max, opts.no_empty);
split_about(arg->begin(), arg->end(), sep.begin(), sep.end(), &splits, opts.max,
opts.no_empty);
}
arg_count++;
}
@@ -1247,7 +1242,8 @@ static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_
}
// A helper function for lower and upper.
static int string_transform(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv, std::wint_t (*func)(std::wint_t)) {
static int string_transform(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv,
std::wint_t (*func)(std::wint_t)) {
options_t opts;
opts.quiet_valid = true;
int optind;