diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-26 00:07:27 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-26 00:07:27 +0200 |
commit | 2093ad296f8a4528ad0e106b52074871a2bf070e (patch) | |
tree | d94c965340ab56f3394b4c3fba675df29ed43f80 | |
parent | 1e3e2ccd5dd280371c9ca29c0e0304a0d40592af (diff) |
hush: fix ${##}, ${#?}, ${#!} handling
function old new delta
parse_dollar 786 820 +34
expand_one_var 1579 1592 +13
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 47/0) Total: 47 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rwxr-xr-x | shell/ash_test/ash-vars/param_expand_alt.tests | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-vars/param_expand_len1.right | 11 | ||||
-rwxr-xr-x | shell/ash_test/ash-vars/param_expand_len1.tests | 31 | ||||
-rw-r--r-- | shell/hush.c | 27 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/param_expand_alt.tests | 2 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/param_expand_len1.right | 11 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/param_expand_len1.tests | 31 |
7 files changed, 106 insertions, 9 deletions
diff --git a/shell/ash_test/ash-vars/param_expand_alt.tests b/shell/ash_test/ash-vars/param_expand_alt.tests index c9c4249af..d80452434 100755 --- a/shell/ash_test/ash-vars/param_expand_alt.tests +++ b/shell/ash_test/ash-vars/param_expand_alt.tests @@ -6,7 +6,7 @@ # now some funky ones. # ${V+word} "if V unset, then substitute nothing, else substitute word" # ${V:+word} "if V unset or '', then substitute nothing, else substitute word" -# bash doesn't accept ${#+}. ash prints 0 (not $#). +# bash doesn't accept ${#+}. ash prints 0 (not $#): "len of $+" echo _${#+}_ _${#:+}_ # Forms with non-empty word work as expected in both ash and bash. echo _${#+z}_ _${#:+z}_ diff --git a/shell/ash_test/ash-vars/param_expand_len1.right b/shell/ash_test/ash-vars/param_expand_len1.right new file mode 100644 index 000000000..dff3c7bb1 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_len1.right @@ -0,0 +1,11 @@ +One:1 +Two:2 +Three:3 + +One:1 +Two:2 +Three:3 + +Ok ${#$}: 0 + +Ok ${#!}: 0 diff --git a/shell/ash_test/ash-vars/param_expand_len1.tests b/shell/ash_test/ash-vars/param_expand_len1.tests new file mode 100755 index 000000000..e1beab320 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_len1.tests @@ -0,0 +1,31 @@ +# ${#c} for any single char c means "length of $c", including all special vars + +false +echo One:${#?} +(exit 10) +echo Two:${#?} +(exit 100) +echo Three:${#?} + +echo +echo One:${##} +set -- 1 2 3 4 5 6 7 8 9 0 +echo Two:${##} +set -- 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ + 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ + 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ + 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 +echo Three:${##} + +echo +v=$$ +test "${#v}" = "${#$}" +echo 'Ok ${#$}:' $? + +echo +sleep 0 & +v=$! +test "${#v}" = "${#!}" +echo 'Ok ${#!}:' $? + +# TODO: ${#-} ${#_} diff --git a/shell/hush.c b/shell/hush.c index 11b33f40a..d0225edb9 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -4466,6 +4466,8 @@ static int parse_dollar(o_string *as_string, case '@': /* args */ goto make_one_char_var; case '{': { + char len_single_ch; + o_addchr(dest, SPECIAL_VAR_SYMBOL); ch = i_getch(input); /* eat '{' */ @@ -4485,6 +4487,7 @@ static int parse_dollar(o_string *as_string, return 0; } nommu_addchr(as_string, ch); + len_single_ch = ch; ch |= quote_mask; /* It's possible to just call add_till_closing_bracket() at this point. @@ -4509,9 +4512,18 @@ static int parse_dollar(o_string *as_string, /* handle parameter expansions * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 */ - if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */ - goto bad_dollar_syntax; - + if (!strchr(VAR_SUBST_OPS, ch)) { /* ${var<bad_char>... */ + if (len_single_ch != '#' + /*|| !strchr(SPECIAL_VARS_STR, ch) - disallow errors like ${#+} ? */ + || i_peek(input) != '}' + ) { + goto bad_dollar_syntax; + } + /* else: it's "length of C" ${#C} op, + * where C is a single char + * special var name, e.g. ${#!}. + */ + } /* Eat everything until closing '}' (or ':') */ end_ch = '}'; if (BASH_SUBSTR @@ -4568,6 +4580,7 @@ static int parse_dollar(o_string *as_string, } break; } + len_single_ch = 0; /* it can't be ${#C} op */ } o_addchr(dest, SPECIAL_VAR_SYMBOL); break; @@ -5559,10 +5572,10 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha first_char = arg[0] = arg0 & 0x7f; exp_op = 0; - if (first_char == '#' && arg[1] /* ${#... but not ${#} */ - && (!exp_saveptr /* and (not ${#<op_char>...} */ - || (arg[1] == '?' && arg[2] == '\0') /* or ${#?} - "len of $?") */ - ) + if (first_char == '#' && arg[1] /* ${#...} but not ${#} */ + && (!exp_saveptr /* and ( not(${#<op_char>...}) */ + || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */ + ) /* NB: skipping ^^^specvar check mishandles ${#::2} */ ) { /* It must be length operator: ${#var} */ var++; diff --git a/shell/hush_test/hush-vars/param_expand_alt.tests b/shell/hush_test/hush-vars/param_expand_alt.tests index c9c4249af..d80452434 100755 --- a/shell/hush_test/hush-vars/param_expand_alt.tests +++ b/shell/hush_test/hush-vars/param_expand_alt.tests @@ -6,7 +6,7 @@ # now some funky ones. # ${V+word} "if V unset, then substitute nothing, else substitute word" # ${V:+word} "if V unset or '', then substitute nothing, else substitute word" -# bash doesn't accept ${#+}. ash prints 0 (not $#). +# bash doesn't accept ${#+}. ash prints 0 (not $#): "len of $+" echo _${#+}_ _${#:+}_ # Forms with non-empty word work as expected in both ash and bash. echo _${#+z}_ _${#:+z}_ diff --git a/shell/hush_test/hush-vars/param_expand_len1.right b/shell/hush_test/hush-vars/param_expand_len1.right new file mode 100644 index 000000000..dff3c7bb1 --- /dev/null +++ b/shell/hush_test/hush-vars/param_expand_len1.right @@ -0,0 +1,11 @@ +One:1 +Two:2 +Three:3 + +One:1 +Two:2 +Three:3 + +Ok ${#$}: 0 + +Ok ${#!}: 0 diff --git a/shell/hush_test/hush-vars/param_expand_len1.tests b/shell/hush_test/hush-vars/param_expand_len1.tests new file mode 100755 index 000000000..e1beab320 --- /dev/null +++ b/shell/hush_test/hush-vars/param_expand_len1.tests @@ -0,0 +1,31 @@ +# ${#c} for any single char c means "length of $c", including all special vars + +false +echo One:${#?} +(exit 10) +echo Two:${#?} +(exit 100) +echo Three:${#?} + +echo +echo One:${##} +set -- 1 2 3 4 5 6 7 8 9 0 +echo Two:${##} +set -- 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ + 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ + 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ + 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 +echo Three:${##} + +echo +v=$$ +test "${#v}" = "${#$}" +echo 'Ok ${#$}:' $? + +echo +sleep 0 & +v=$! +test "${#v}" = "${#!}" +echo 'Ok ${#!}:' $? + +# TODO: ${#-} ${#_} |