Add string-replace-fewer-backslashes feature

This disables an extra round of escaping in the `string replace -r`
replacement string.

Currently, to add a backslash to an a or b (to "escape" it):

    string replace -ra '([ab])' '\\\\\\\$1' a

7 backslashes!

This removes one of the layers, so now 3 or 4 works (each one escaped
for the single-quotes, so pcre receives two, which it reads as one literal):

    string replace -ra '([ab])' '\\\\$1' a

This is backwards-incompatible as replacement strings will change
meaning, so we put it behind a feature flag.

The name is kinda crappy, though.

Fixes #5474.
This commit is contained in:
Fabian Homborg
2019-01-12 20:20:35 +01:00
parent a7a12c5c96
commit 864bb1f7a6
8 changed files with 17 additions and 2 deletions

View File

@@ -26,6 +26,7 @@
#include "builtin.h"
#include "common.h"
#include "fallback.h" // IWYU pragma: keep
#include "future_feature_flags.h"
#include "io.h"
#include "parse_util.h"
#include "pcre2.h"
@@ -933,8 +934,13 @@ class regex_replacer_t : public string_replacer_t {
regex_replacer_t(const wchar_t *argv0, const wcstring &pattern, const wcstring &replacement_,
const options_t &opts, io_streams_t &streams)
: string_replacer_t(argv0, opts, streams),
regex(argv0, pattern, opts.ignore_case, streams),
replacement(interpret_escapes(replacement_)) {}
regex(argv0, pattern, opts.ignore_case, streams) {
if (feature_test(features_t::string_replace_backslash)) {
replacement = replacement_;
} else {
replacement = interpret_escapes(replacement_);
}
}
bool replace_matches(const wcstring &arg) override;
};

View File

@@ -13,6 +13,7 @@ features_t &mutable_fish_features() { return global_features; }
const features_t::metadata_t features_t::metadata[features_t::flag_count] = {
{stderr_nocaret, L"stderr-nocaret", L"3.0", L"^ no longer redirects stderr"},
{qmark_noglob, L"qmark-noglob", L"3.0", L"? no longer globs"},
{string_replace_backslash, L"string-replace-fewer-backslashes", L"3.1", L"string replace -r needs fewer backslashes in the replacement"},
};
const struct features_t::metadata_t *features_t::metadata_for(const wchar_t *name) {

View File

@@ -17,6 +17,9 @@ class features_t {
/// Whether ? is supported as a glob.
qmark_noglob,
/// Whether string replace -r double-unescapes the replacement.
string_replace_backslash,
/// The number of flags.
flag_count
};

View File

@@ -0,0 +1 @@
--features 'no-string-replace-fewer-backslashes' -C 'string replace -ra "\\\\" "\\\\\\\\" -- "a\b\c"'

View File

@@ -0,0 +1 @@
a\b\c

View File

@@ -0,0 +1 @@
--features 'string-replace-fewer-backslashes' -C 'string replace -ra "\\\\" "\\\\\\\\" -- "a\b\c"'

View File

@@ -0,0 +1 @@
a\\b\\c

View File

@@ -6,5 +6,6 @@ test_function
# Future Feature Flags
stderr-nocaret off 3.0 ^ no longer redirects stderr
qmark-noglob off 3.0 ? no longer globs
string-replace-fewer-backslashes off 3.1 string replace -r needs fewer backslashes in the replacement
1
2