summaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/hush.c26
-rw-r--r--shell/hush_test/hush-parsing/starquoted2.right2
-rwxr-xr-xshell/hush_test/hush-parsing/starquoted2.tests14
3 files changed, 36 insertions, 6 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 5b5a542..2c1d31c 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 0000000..46f2436
--- /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 0000000..782d71b
--- /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