From 5947aa0171ee99e56acd22ba0189a1e07414fcce Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 30 Sep 2018 19:34:01 -0400 Subject: [PATCH] Join variables by their delimiter in quoted expansion This switches quoted expansion like "$foo" to use foo's delimiter instead of space. The delimiter is space for normal variables and colonf or path variables. Expansions like "$PATH" will now expand using ':'. --- src/common.cpp | 11 +++++++++++ src/env.cpp | 7 +++++-- src/env.h | 3 +++ src/expand.cpp | 14 +++++--------- tests/expansion.err | 3 +++ tests/expansion.in | 5 +++++ tests/expansion.out | 4 ++++ tests/test3.in | 5 +++-- tests/test3.out | 2 ++ 9 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index b4063df79..b3c8ff178 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1898,7 +1898,18 @@ wcstring_list_t split_string(const wcstring &val, wchar_t sep) { } wcstring join_strings(const wcstring_list_t &vals, wchar_t sep) { + if (vals.empty()) return wcstring{}; + + // Reserve the size we will need. + // count-1 separators, plus the length of all strings. + size_t size = vals.size() - 1; + for (const wcstring &s : vals) { + size += s.size(); + } + + // Construct the string. wcstring result; + result.reserve(size); bool first = true; for (const wcstring &s : vals) { if (!first) { diff --git a/src/env.cpp b/src/env.cpp index 861755a87..09c4f8305 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1302,10 +1302,13 @@ int env_remove(const wcstring &key, int var_mode) { const wcstring_list_t &env_var_t::as_list() const { return vals; } +wchar_t env_var_t::get_delimiter() const { + return is_pathvar() ? PATH_ARRAY_SEP : NONPATH_ARRAY_SEP; +} + /// Return a string representation of the var. wcstring env_var_t::as_string() const { - wchar_t sep = is_pathvar() ? PATH_ARRAY_SEP : NONPATH_ARRAY_SEP; - return join_strings(vals, sep); + return join_strings(vals, get_delimiter()); } void env_var_t::to_list(wcstring_list_t &out) const { diff --git a/src/env.h b/src/env.h index 0283c2dce..114a91cbe 100644 --- a/src/env.h +++ b/src/env.h @@ -101,6 +101,9 @@ class env_var_t { void to_list(wcstring_list_t &out) const; const wcstring_list_t &as_list() const; + /// \return the character used when delimiting quoted expansion. + wchar_t get_delimiter() const; + void set_vals(wcstring_list_t v) { vals = std::move(v); } void set_exports(bool exportv) { diff --git a/src/expand.cpp b/src/expand.cpp index d4bc6e553..b2cb2ef03 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -447,6 +447,9 @@ static bool expand_variables(wcstring instr, std::vector *out, siz } if (is_single) { + // Quoted expansion. Here we expect the variable's delimiter. + // Note history always has a space delimiter. + wchar_t delimit = history ? L' ' : var->get_delimiter(); wcstring res(instr, 0, varexp_char_idx); if (!res.empty()) { if (res.back() != VARIABLE_EXPAND_SINGLE) { @@ -457,15 +460,8 @@ static bool expand_variables(wcstring instr, std::vector *out, siz } } - // Append all entries in var_item_list, separated by spaces. - // Remove the last space. - if (!var_item_list.empty()) { - for (const wcstring &item : var_item_list) { - res.append(item); - res.push_back(L' '); - } - res.pop_back(); - } + // Append all entries in var_item_list, separated by the delimiter. + res.append(join_strings(var_item_list, delimit)); res.append(instr, var_name_and_slice_stop, wcstring::npos); return expand_variables(std::move(res), out, varexp_char_idx, errors); } else { diff --git a/tests/expansion.err b/tests/expansion.err index 65ffe2e28..35a49d308 100644 --- a/tests/expansion.err +++ b/tests/expansion.err @@ -19,3 +19,6 @@ echo $$paren #################### # Test tilde expansion + +#################### +# Test delimiters diff --git a/tests/expansion.in b/tests/expansion.in index a0fdd2138..afb76321d 100644 --- a/tests/expansion.in +++ b/tests/expansion.in @@ -126,3 +126,8 @@ end unlink $tmpdir/linkhome rmdir $tmpdir/realhome rmdir $tmpdir + +logmsg Test delimiters +set TEST_DELIMITER one two three +set TEST_DELIMITER_PATH one two three +echo "$TEST_DELIMITER" "$TEST_DELIMITER_PATH" diff --git a/tests/expansion.out b/tests/expansion.out index ca7596395..ab86d0c92 100644 --- a/tests/expansion.out +++ b/tests/expansion.out @@ -73,3 +73,7 @@ All digits: 0 #################### # Test tilde expansion + +#################### +# Test delimiters +one two three one:two:three diff --git a/tests/test3.in b/tests/test3.in index fbd70106c..5cad24e86 100644 --- a/tests/test3.in +++ b/tests/test3.in @@ -306,8 +306,9 @@ set -gx FOOPATH one two three four ../test/root/bin/fish -c 'echo Elements in FOO and FOOPATH: (count $FOO) (count $FOOPATH)' # some must use colon separators! set -lx MANPATH man1 man2 man3 ; env | grep '^MANPATH=' -# ensure we don't escape colon values +# ensure we don't escape space and colon values set -x DONT_ESCAPE_COLONS 1: 2: :3: ; env | grep '^DONT_ESCAPE_COLONS=' - +set -x DONT_ESCAPE_SPACES '1 ' '2 ' ' 3 ' 4 ; env | grep '^DONT_ESCAPE_SPACES=' +set -x DONT_ESCAPE_COLONS_PATH 1: 2: :3: ; env | grep '^DONT_ESCAPE_COLONS_PATH=' true diff --git a/tests/test3.out b/tests/test3.out index 4c52ca154..c466c8f6b 100644 --- a/tests/test3.out +++ b/tests/test3.out @@ -44,3 +44,5 @@ Elements in DISPLAY: 1 Elements in FOO and FOOPATH: 1 4 MANPATH=man1:man2:man3 DONT_ESCAPE_COLONS=1: 2: :3: +DONT_ESCAPE_SPACES=1 2 3 4 +DONT_ESCAPE_COLONS_PATH=1::2:::3: