diff -urpN busybox-1.13.3/shell/hush.c busybox-1.13.3-hush/shell/hush.c --- busybox-1.13.3/shell/hush.c 2009-02-26 12:46:55.000000000 +0100 +++ busybox-1.13.3-hush/shell/hush.c 2009-03-20 13:01:52.000000000 +0100 @@ -458,8 +458,9 @@ struct globals { smallint fake_mode; /* these three support $?, $#, and $1 */ smalluint last_return_code; + smalluint global_args_malloced; + int global_argc; /* NB: $# + 1 */ char **global_argv; - int global_argc; #if ENABLE_HUSH_LOOPS unsigned depth_break_continue; unsigned depth_of_loop; @@ -633,7 +634,7 @@ static char *unbackslash(char *src) return dst; } -static char **add_strings_to_strings(char **strings, char **add) +static char **add_strings_to_strings(char **strings, char **add, int need_to_dup) { int i; unsigned count1; @@ -658,7 +659,7 @@ static char **add_strings_to_strings(cha v[count1 + count2] = NULL; i = count2; while (--i >= 0) - v[count1 + i] = add[i]; + v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]); return v; } @@ -667,7 +668,7 @@ static char **add_string_to_strings(char char *v[2]; v[0] = add; v[1] = NULL; - return add_strings_to_strings(strings, v); + return add_strings_to_strings(strings, v, /*dup:*/ 0); } static void putenv_all(char **strings) @@ -1213,8 +1214,13 @@ static int o_glob(o_string *o, int n) * Otherwise, just finish current list[] and start new */ static int o_save_ptr(o_string *o, int n) { - if (o->o_glob) - return o_glob(o, n); /* o_save_ptr_helper is inside */ + if (o->o_glob) { /* if globbing is requested */ + /* If o->has_empty_slot, list[n] was already globbed + * (if it was requested back then when it was filled) + * so don't do that again! */ + if (!o->has_empty_slot) + return o_glob(o, n); /* o_save_ptr_helper is inside */ + } return o_save_ptr_helper(o, n); } @@ -4279,6 +4285,11 @@ int hush_main(int argc, char **argv) switch (opt) { case 'c': G.global_argv = argv + optind; + if (!argv[optind]) { + /* -c 'script' (no params): prevent empty $0 */ + *--G.global_argv = argv[0]; + optind--; + } /* else -c 'script' PAR0 PAR1: $0 is PAR0 */ G.global_argc = argc - optind; opt = parse_and_run_string(optarg, 0 /* parse_flag */); goto final_return; @@ -4639,17 +4650,68 @@ static int builtin_read(char **argv) return set_local_var(string, 0); } -/* built-in 'set [VAR=value]' handler */ +/* built-in 'set' handler + * SUSv3 says: + * set [-abCefmnuvx] [-h] [-o option] [argument...] + * set [+abCefmnuvx] [+h] [+o option] [argument...] + * set -- [argument...] + * set -o + * set +o + * Implementations shall support the options in both their hyphen and + * plus-sign forms. These options can also be specified as options to sh. + * Examples: + * Write out all variables and their values: set + * Set $1, $2, and $3 and set "$#" to 3: set c a b + * Turn on the -x and -v options: set -xv + * Unset all positional parameters: set -- + * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x" + * Set the positional parameters to the expansion of x, even if x expands + * with a leading '-' or '+': set -- $x + * + * So far, we only support "set -- [argument...]" by ignoring all options + * (also, "-o option" will be mishandled by taking "option" as parameter #1). + */ static int builtin_set(char **argv) { - char *temp = argv[1]; struct variable *e; + char **pp; + char *arg = *++argv; - if (temp == NULL) + if (arg == NULL) { for (e = G.top_var; e; e = e->next) puts(e->varstr); - else - set_local_var(xstrdup(temp), 0); + } else { + /* NB: G.global_argv[0] ($0) is never freed/changed */ + + if (G.global_args_malloced) { + pp = G.global_argv; + while (*++pp) + free(*pp); + G.global_argv[1] = NULL; + } else { + G.global_args_malloced = 1; + pp = xzalloc(sizeof(pp[0]) * 2); + pp[0] = G.global_argv[0]; /* retain $0 */ + G.global_argv = pp; + } + do { + if (arg[0] == '+') + continue; + if (arg[0] != '-') + break; + if (arg[1] == '-' && arg[2] == '\0') { + argv++; + break; + } + } while ((arg = *++argv) != NULL); + /* Now argv[0] is 1st argument */ + + /* This realloc's G.global_argv */ + G.global_argv = pp = add_strings_to_strings(G.global_argv, argv, /*dup:*/ 1); + G.global_argc = 1; + while (*++pp) + G.global_argc++; + } return EXIT_SUCCESS; } diff -urpN busybox-1.13.3/shell/hush_test/hush-parsing/starquoted2.right busybox-1.13.3-hush/shell/hush_test/hush-parsing/starquoted2.right --- busybox-1.13.3/shell/hush_test/hush-parsing/starquoted2.right 2009-02-26 12:46:52.000000000 +0100 +++ busybox-1.13.3-hush/shell/hush_test/hush-parsing/starquoted2.right 2009-03-20 12:32:30.000000000 +0100 @@ -1,2 +1,3 @@ Should be printed Should be printed +Empty: diff -urpN busybox-1.13.3/shell/hush_test/hush-parsing/starquoted2.tests busybox-1.13.3-hush/shell/hush_test/hush-parsing/starquoted2.tests --- busybox-1.13.3/shell/hush_test/hush-parsing/starquoted2.tests 2009-02-26 12:46:52.000000000 +0100 +++ busybox-1.13.3-hush/shell/hush_test/hush-parsing/starquoted2.tests 2009-03-20 12:32:30.000000000 +0100 @@ -12,3 +12,6 @@ for a in "$@"""; do echo Should not be p 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 + +# Bug 207: "$@" expands to nothing, and we erroneously glob "%s\\n" twice: +printf "Empty:%s\\n" "$@"