mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-12 21:18:53 +00:00
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
This commit is contained in:
parent
d0c85471b4
commit
3981b644d6
7 changed files with 153 additions and 13 deletions
53
expand.cpp
53
expand.cpp
|
@ -1086,10 +1086,15 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (!(in[stop_pos ]))
|
const wchar_t nc = in[stop_pos];
|
||||||
|
if (!(nc))
|
||||||
break;
|
break;
|
||||||
if (!(iswalnum(in[stop_pos]) ||
|
if (nc == VARIABLE_EXPAND_EMPTY)
|
||||||
(wcschr(L"_", in[stop_pos])!= 0)))
|
{
|
||||||
|
stop_pos++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!(wcsvarchr(nc)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
stop_pos++;
|
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);
|
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())
|
if (! var_val.missing())
|
||||||
{
|
{
|
||||||
|
@ -1174,7 +1187,18 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
|
||||||
{
|
{
|
||||||
in[i]=0;
|
in[i]=0;
|
||||||
wcstring res = in;
|
wcstring res = in;
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
if (in[i-1] != VARIABLE_EXPAND_SINGLE)
|
||||||
|
{
|
||||||
res.push_back(INTERNAL_SEPARATOR);
|
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++)
|
for (size_t j=0; j<var_item_list.size(); j++)
|
||||||
{
|
{
|
||||||
|
@ -1204,15 +1228,19 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
|
||||||
if (is_ok)
|
if (is_ok)
|
||||||
{
|
{
|
||||||
wcstring new_in;
|
wcstring new_in;
|
||||||
|
new_in.append(in, i);
|
||||||
|
|
||||||
if (start_pos > 0)
|
if (i > 0)
|
||||||
new_in.append(in, start_pos - 1);
|
{
|
||||||
|
if (in[i-1] != VARIABLE_EXPAND)
|
||||||
// at this point new_in.size() is start_pos - 1
|
|
||||||
if (start_pos>1 && new_in[start_pos-2]!=VARIABLE_EXPAND)
|
|
||||||
{
|
{
|
||||||
new_in.push_back(INTERNAL_SEPARATOR);
|
new_in.push_back(INTERNAL_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
else if (next.empty())
|
||||||
|
{
|
||||||
|
new_in.push_back(VARIABLE_EXPAND_EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
new_in.append(next);
|
new_in.append(next);
|
||||||
new_in.append(in + stop_pos);
|
new_in.append(in + stop_pos);
|
||||||
is_ok &= expand_variables2(parser, new_in, out, i, errors);
|
is_ok &= expand_variables2(parser, new_in, out, i, errors);
|
||||||
|
@ -1243,8 +1271,11 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
|
||||||
Expansion to single argument.
|
Expansion to single argument.
|
||||||
*/
|
*/
|
||||||
wcstring res;
|
wcstring res;
|
||||||
in[i] = 0;
|
res.append(in, i);
|
||||||
res.append(in);
|
if (i > 0 && in[i-1] == VARIABLE_EXPAND_SINGLE)
|
||||||
|
{
|
||||||
|
res.push_back(VARIABLE_EXPAND_EMPTY);
|
||||||
|
}
|
||||||
res.append(in + stop_pos);
|
res.append(in + stop_pos);
|
||||||
|
|
||||||
is_ok &= expand_variables2(parser, res, out, i, errors);
|
is_ok &= expand_variables2(parser, res, out, i, errors);
|
||||||
|
|
5
expand.h
5
expand.h
|
@ -102,6 +102,11 @@ enum
|
||||||
*/
|
*/
|
||||||
INTERNAL_SEPARATOR,
|
INTERNAL_SEPARATOR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
Character representing an empty variable expansion.
|
||||||
|
Only used transitively while expanding variables.
|
||||||
|
*/
|
||||||
|
VARIABLE_EXPAND_EMPTY,
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
0
tests/expansion.err
Normal file
0
tests/expansion.err
Normal file
64
tests/expansion.in
Normal file
64
tests/expansion.in
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
# Test expansion of variables
|
||||||
|
|
||||||
|
function show --description 'Prints argument count followed by arguments'
|
||||||
|
echo (count $argv) $argv
|
||||||
|
end
|
||||||
|
|
||||||
|
set -l foo
|
||||||
|
show "$foo"
|
||||||
|
show $foo
|
||||||
|
show "prefix$foo"
|
||||||
|
show prefix$foo
|
||||||
|
|
||||||
|
show "$$foo"
|
||||||
|
show $$foo
|
||||||
|
show "prefix$$foo"
|
||||||
|
show prefix$$foo
|
||||||
|
|
||||||
|
set -l foo ''
|
||||||
|
show "$foo"
|
||||||
|
show $foo
|
||||||
|
show "prefix$foo"
|
||||||
|
show prefix$foo
|
||||||
|
|
||||||
|
show "$$foo"
|
||||||
|
show $$foo
|
||||||
|
show "prefix$$foo"
|
||||||
|
show prefix$$foo
|
||||||
|
|
||||||
|
set -l foo bar
|
||||||
|
set -l bar
|
||||||
|
show "$$foo"
|
||||||
|
show $$foo
|
||||||
|
show "prefix$$foo"
|
||||||
|
show prefix$$foo
|
||||||
|
|
||||||
|
set -l bar baz
|
||||||
|
show "$$foo"
|
||||||
|
show $$foo
|
||||||
|
show "prefix$$foo"
|
||||||
|
show prefix$$foo
|
||||||
|
|
||||||
|
set -l bar baz quux
|
||||||
|
show "$$foo"
|
||||||
|
show $$foo
|
||||||
|
show "prefix$$foo"
|
||||||
|
show prefix$$foo
|
||||||
|
|
||||||
|
set -l foo bar fooer fooest
|
||||||
|
set -l fooer
|
||||||
|
set -l fooest
|
||||||
|
show "$$foo"
|
||||||
|
show $$foo
|
||||||
|
show "prefix$$foo"
|
||||||
|
show prefix$$foo
|
||||||
|
|
||||||
|
set -l fooer ''
|
||||||
|
show $$foo
|
||||||
|
show prefix$$foo
|
||||||
|
|
||||||
|
set -l foo bar '' fooest
|
||||||
|
show "$$foo"
|
||||||
|
show $$foo
|
||||||
|
show "prefix$$foo"
|
||||||
|
show prefix$$foo
|
38
tests/expansion.out
Normal file
38
tests/expansion.out
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1 prefix
|
||||||
|
0
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1 prefix
|
||||||
|
0
|
||||||
|
1
|
||||||
|
1
|
||||||
|
1 prefix
|
||||||
|
1 prefix
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1 prefix
|
||||||
|
0
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1 prefix
|
||||||
|
0
|
||||||
|
1 baz
|
||||||
|
1 baz
|
||||||
|
1 prefixbaz
|
||||||
|
1 prefixbaz
|
||||||
|
1 baz quux
|
||||||
|
2 baz quux
|
||||||
|
1 prefixbaz quux
|
||||||
|
2 prefixbaz prefixquux
|
||||||
|
1 baz quux fooer fooest
|
||||||
|
2 baz quux
|
||||||
|
1 prefixbaz quux fooer fooest
|
||||||
|
2 prefixbaz prefixquux
|
||||||
|
3 baz quux
|
||||||
|
3 prefixbaz prefixquux prefix
|
||||||
|
1 baz quux fooest
|
||||||
|
2 baz quux
|
||||||
|
1 prefixbaz quux fooest
|
||||||
|
2 prefixbaz prefixquux
|
1
tests/expansion.status
Normal file
1
tests/expansion.status
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
|
@ -1,4 +1,5 @@
|
||||||
Testing high level script functionality
|
Testing high level script functionality
|
||||||
|
File expansion.in tested ok
|
||||||
File printf.in tested ok
|
File printf.in tested ok
|
||||||
File test1.in tested ok
|
File test1.in tested ok
|
||||||
File test2.in tested ok
|
File test2.in tested ok
|
||||||
|
|
Loading…
Reference in a new issue