diff options
author | 2014-08-20 21:19:08 -0700 | |
---|---|---|
committer | 2014-08-20 21:45:07 -0700 | |
commit | 3981b644d68f6b6947b4a12810c2fa5e09da4e58 (patch) | |
tree | fd00d82b90283ba3810eb04ffc40b1632fc1e155 /expand.cpp | |
parent | d0c85471b40fee548efc35a370f5d5c4b656f66a (diff) |
Fix double expansions (`$$foo`)
Double expansions of variables had the following issues:
* `"$$foo"` threw an error no matter what the value of `$foo` was.
* `set -l foo ''; echo $$foo` threw an error because of the expansion of
`$foo` to `''`.
With this change, double expansion always works properly. When
double-expanding a multi-valued variable, in a double-quoted string the
first word of the inner expansion is used for the outer expansion, and
outside of a quoted string every word is used for the double-expansion
in each of the arguments.
> set -l foo bar baz
> set -l bar one two
> set -l baz three four
> echo "$$foo"
one two baz
> echo $$foo
one two three four
Diffstat (limited to 'expand.cpp')
-rw-r--r-- | expand.cpp | 57 |
1 files changed, 44 insertions, 13 deletions
@@ -1086,10 +1086,15 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std:: while (1) { - if (!(in[stop_pos ])) + const wchar_t nc = in[stop_pos]; + if (!(nc)) break; - if (!(iswalnum(in[stop_pos]) || - (wcschr(L"_", in[stop_pos])!= 0))) + if (nc == VARIABLE_EXPAND_EMPTY) + { + stop_pos++; + break; + } + if (!(wcsvarchr(nc))) break; stop_pos++; @@ -1108,7 +1113,15 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std:: } var_tmp.append(in + start_pos, var_len); - env_var_t var_val = expand_var(var_tmp.c_str()); + env_var_t var_val; + if (var_len == 1 && var_tmp[0] == VARIABLE_EXPAND_EMPTY) + { + var_val = env_var_t::missing_var(); + } + else + { + var_val = expand_var(var_tmp.c_str()); + } if (! var_val.missing()) { @@ -1174,7 +1187,18 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std:: { in[i]=0; wcstring res = in; - res.push_back(INTERNAL_SEPARATOR); + if (i > 0) + { + if (in[i-1] != VARIABLE_EXPAND_SINGLE) + { + res.push_back(INTERNAL_SEPARATOR); + } + else if (var_item_list.empty() || var_item_list.front().empty()) + { + // first expansion is empty, but we need to recursively expand + res.push_back(VARIABLE_EXPAND_EMPTY); + } + } for (size_t j=0; j<var_item_list.size(); j++) { @@ -1204,14 +1228,18 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std:: if (is_ok) { wcstring new_in; + new_in.append(in, i); - if (start_pos > 0) - new_in.append(in, start_pos - 1); - - // at this point new_in.size() is start_pos - 1 - if (start_pos>1 && new_in[start_pos-2]!=VARIABLE_EXPAND) + if (i > 0) { - new_in.push_back(INTERNAL_SEPARATOR); + if (in[i-1] != VARIABLE_EXPAND) + { + new_in.push_back(INTERNAL_SEPARATOR); + } + else if (next.empty()) + { + new_in.push_back(VARIABLE_EXPAND_EMPTY); + } } new_in.append(next); new_in.append(in + stop_pos); @@ -1243,8 +1271,11 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std:: Expansion to single argument. */ wcstring res; - in[i] = 0; - res.append(in); + res.append(in, i); + if (i > 0 && in[i-1] == VARIABLE_EXPAND_SINGLE) + { + res.push_back(VARIABLE_EXPAND_EMPTY); + } res.append(in + stop_pos); is_ok &= expand_variables2(parser, res, out, i, errors); |