diff options
-rw-r--r-- | shell/hush.c | 26 | ||||
-rw-r--r-- | shell/hush_test/hush-parsing/starquoted2.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-parsing/starquoted2.tests | 14 |
3 files changed, 36 insertions, 6 deletions
diff --git a/shell/hush.c b/shell/hush.c index 5b5a54241..2c1d31c6d 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -2380,7 +2380,10 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) p = strchr(p, SPECIAL_VAR_SYMBOL); first_ch = arg[0] | or_mask; /* forced to "quoted" if or_mask = 0x80 */ - ored_ch |= first_ch; + /* "$@" is special. Even if quoted, it can still + * expand to nothing (not even an empty string) */ + if ((first_ch & 0x7f) != '@') + ored_ch |= first_ch; val = NULL; switch (first_ch & 0x7f) { /* Highest bit in first_ch indicates that var is double-quoted */ @@ -2401,6 +2404,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) i = 1; if (!global_argv[i]) break; + ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ smallint sv = output->o_quote; /* unquoted var's contents should be globbed, so don't quote */ @@ -2932,15 +2936,25 @@ static int done_word(o_string *word, struct p_context *ctx) } } #endif - if (word->nonnull /* we saw "xx" or 'xx' */ + if (word->nonnull /* word had "xx" or 'xx' at least as part of it */ /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) /* (otherwise it's "abc".... and is already safe) */ ) { - /* Insert "empty variable" reference, this makes - * e.g. "", $empty"" etc to not disappear */ - o_addchr(word, SPECIAL_VAR_SYMBOL); - o_addchr(word, SPECIAL_VAR_SYMBOL); + /* but exclude "$@"! it expands to no word despite "" */ + char *p = word->data; + while (p[0] == SPECIAL_VAR_SYMBOL + && (p[1] & 0x7f) == '@' + && p[2] == SPECIAL_VAR_SYMBOL + ) { + p += 3; + } + if (p == word->data || p[0] != '\0') { + /* Insert "empty variable" reference, this makes + * e.g. "", $empty"" etc to not disappear */ + o_addchr(word, SPECIAL_VAR_SYMBOL); + o_addchr(word, SPECIAL_VAR_SYMBOL); + } } child->argv = add_malloced_string_to_strings(child->argv, xstrdup(word->data)); debug_print_strings("word appended to argv", child->argv); diff --git a/shell/hush_test/hush-parsing/starquoted2.right b/shell/hush_test/hush-parsing/starquoted2.right new file mode 100644 index 000000000..46f24369e --- /dev/null +++ b/shell/hush_test/hush-parsing/starquoted2.right @@ -0,0 +1,2 @@ +Should be printed +Should be printed diff --git a/shell/hush_test/hush-parsing/starquoted2.tests b/shell/hush_test/hush-parsing/starquoted2.tests new file mode 100755 index 000000000..782d71b88 --- /dev/null +++ b/shell/hush_test/hush-parsing/starquoted2.tests @@ -0,0 +1,14 @@ +if test $# != 0; then + exec "$THIS_SH" "$0" +fi + +# No params! +for a in "$*"; do echo Should be printed; done +for a in "$@"; do echo Should not be printed; done +# Yes, believe it or not, bash is mesmerized by "$@" and stops +# treating "" as "this word cannot be expanded to nothing, +# but must be at least null string". Now it can be expanded to nothing. +for a in "$@"""; do echo Should not be printed; done +for a in """$@"; do echo Should not be printed; done +for a in """$@"''"$@"''; do echo Should not be printed; done +for a in ""; do echo Should be printed; done |