Disallow escaped characters in variable expansion

Prior to this fix, an escaped character like \x41 (hex for ascii A)
was interpreted the same was as A, so that $\x41 would be the same
as $A. Fix this by inserting an INTERNAL_SEPARATOR before these escapes,
so that we no longer treat it as part of the variable name.

This also affects brackets; don't treat echo $foo\1331\135 the same as
echo $foo[1].

Fixes #7969
This commit is contained in:
ridiculousfish 2021-05-05 16:23:06 -07:00
parent 3f35012afb
commit 555af37616
3 changed files with 19 additions and 1 deletions

View File

@ -6,6 +6,7 @@ Notable improvements and fixes
Syntax changes and new commands
-------------------------------
- Escaped characters like ``\x41`` are no longer valid in a variable name (:issue:`7969`).
Deprecations and removed features
---------------------------------

View File

@ -1155,7 +1155,7 @@ static maybe_t<wchar_t> string_last_char(const wcstring &str) {
}
/// Given a null terminated string starting with a backslash, read the escape as if it is unquoted,
/// appending to result. Return the number of characters consumed, or 0 on error.
/// appending to result. Return the number of characters consumed, or none on error.
maybe_t<size_t> read_unquoted_escape(const wchar_t *input, wcstring *result, bool allow_incomplete,
bool unescape_special) {
assert(input[0] == L'\\' && "Not an escape");
@ -1244,6 +1244,9 @@ maybe_t<size_t> read_unquoted_escape(const wchar_t *input, wcstring *result, boo
}
if (res <= max_val) {
// Prepend internal sep so this does not get treated as part of a variable.
// See #7969.
if (unescape_special) result->push_back(INTERNAL_SEPARATOR);
result_char_or_none =
static_cast<wchar_t>((byte_literal ? ENCODE_DIRECT_BASE : 0) + res);
} else {

View File

@ -78,3 +78,17 @@ echo $list # CHECK: 2 1 3
set -l list 1 2 3
set list[2..] $list[-1..2]
echo $list # CHECK: 1 3 2
# Regression test for #7969.
set fo shorty
set foo A B C
echo $foo[1]
# CHECK: A
echo $foo\1331\135
# CHECK: A[1] B[1] C[1]
echo $foo\x5b1\x5d
# CHECK: A[1] B[1] C[1]
echo $foo\1331\135
# CHECK: A[1] B[1] C[1]
echo $fo\157
# CHECK: shortyo