hush: fix handling of empty arguments
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 18 Oct 2009 09:46:35 +0000 (11:46 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 18 Oct 2009 09:46:35 +0000 (11:46 +0200)
function                                             old     new   delta
builtin_exec                                          25      83     +58
parse_stream                                        2242    2261     +19
run_pipe                                            1782    1787      +5
static.pseudo_null_str                                 -       3      +3
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/0 up/down: 85/0)               Total: 85 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/hush.c
shell/hush_test/hush-misc/empty_args.right [new file with mode: 0644]
shell/hush_test/hush-misc/empty_args.tests [new file with mode: 0755]
shell/hush_test/hush-psubst/emptytick.right
shell/hush_test/hush-psubst/emptytick.tests

index 1d2826d9a5ef9eb50d6b510aa08569d50f1cff1d..46bb7e9a27da6ff6aeab8e4a8ef9ee59c0b4df86 100644 (file)
@@ -2185,7 +2185,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
 
        ored_ch = 0;
 
-       debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg);
+       debug_printf_expand("expand_vars_to_list: arg:'%s' or_mask:%x\n", arg, or_mask);
        debug_print_list("expand_vars_to_list", output, n);
        n = o_save_ptr(output, n);
        debug_print_list("expand_vars_to_list[0]", output, n);
@@ -3425,7 +3425,7 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save,
        sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
        execvp(argv[0], argv);
        bb_perror_msg("can't execute '%s'", argv[0]);
-       _exit(EXIT_FAILURE);
+       _exit(127); /* bash compat */
 }
 
 /* Called after [v]fork() in run_pipe
@@ -3895,7 +3895,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
                        argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
                }
 
-               /* if someone gives us an empty string: ``, $(), ... */
+               /* if someone gives us an empty string: `cmd with empty output` */
                if (!argv_expanded[0]) {
                        debug_leave();
                        return 0;
@@ -5802,7 +5802,7 @@ static int parse_stream_dquoted(o_string *as_string,
        if (ch != '\n') {
                next = i_peek(input);
        }
-       debug_printf_parse(": ch=%c (%d) escape=%d\n",
+       debug_printf_parse("\" ch=%c (%d) escape=%d\n",
                                        ch, ch, dest->o_escape);
        if (ch == '\\') {
                if (next == EOF) {
@@ -5882,6 +5882,11 @@ static struct pipe *parse_stream(char **pstring,
                        end_trigger ? end_trigger : 'X');
        debug_enter();
 
+       /* If very first arg is "" or '', dest.data may end up NULL.
+        * Preventing this: */
+       o_addchr(&dest, '\0');
+       dest.length = 0;
+
        G.ifs = get_local_var_value("IFS");
        if (G.ifs == NULL)
                G.ifs = " \t\n";
@@ -6979,16 +6984,32 @@ static int FAST_FUNC builtin_cd(char **argv)
 
 static int FAST_FUNC builtin_exec(char **argv)
 {
-       if (*++argv == NULL)
-               return EXIT_SUCCESS; /* bash does this */
-       {
+       static const char pseudo_null_str[] = { SPECIAL_VAR_SYMBOL, SPECIAL_VAR_SYMBOL, '\0' };
+       char **pp = argv;
 #if !BB_MMU
-               nommu_save_t dummy;
+       nommu_save_t dummy;
 #endif
-               /* TODO: if exec fails, bash does NOT exit! We do... */
-               pseudo_exec_argv(&dummy, argv, 0, NULL);
-               /* never returns */
+
+       if (*++argv == NULL)
+               return EXIT_SUCCESS; /* bash does this */
+
+       /* Make sure empty arguments aren't ignored */
+       /* Example: exec ls '' */
+       pp = argv;
+       while (*pp) {
+               if ((*pp)[0] == '\0')
+                       *pp = (char*)pseudo_null_str;
+               pp++;
        }
+
+       /* Careful: we can end up here after [v]fork. Do not restore
+        * tty pgrp then, only top-level shell process does that */
+       if (G_saved_tty_pgrp && getpid() == G.root_pid)
+               tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
+
+       /* TODO: if exec fails, bash does NOT exit! We do... */
+       pseudo_exec_argv(&dummy, argv, 0, NULL);
+       /* never returns */
 }
 
 static int FAST_FUNC builtin_exit(char **argv)
diff --git a/shell/hush_test/hush-misc/empty_args.right b/shell/hush_test/hush-misc/empty_args.right
new file mode 100644 (file)
index 0000000..38ed8b8
--- /dev/null
@@ -0,0 +1,6 @@
+Null 0th arg:
+hush: can't execute '': No such file or directory
+127
+Null 1st arg:
+0
+Null arg in exec:
diff --git a/shell/hush_test/hush-misc/empty_args.tests b/shell/hush_test/hush-misc/empty_args.tests
new file mode 100755 (executable)
index 0000000..efce549
--- /dev/null
@@ -0,0 +1,9 @@
+echo Null 0th arg:
+""
+echo $?
+echo Null 1st arg:
+# printf without args would print usage info
+printf ""
+echo $?
+echo Null arg in exec:
+exec printf ""
index d4b70c58ae5a3e5a15abe7bc7e8a2992c8fafabc..1f60ecfdad464de29061286a04eb4103f37b34ab 100644 (file)
@@ -1,14 +1,17 @@
 0
 0
+hush: can't execute '': No such file or directory
 0
+hush: can't execute '': No such file or directory
 0
 0
 0
 0
 0
+hush: can't execute '': No such file or directory
 0
+hush: can't execute '': No such file or directory
 0
 0
 0
-0
-0
+hush: can't execute '': No such file or directory
index af3a1836c0aff0ff3b19a3cfe4390b2c8c04629f..a269f025aa0bf36f4a73ff9edad56b05084578e7 100755 (executable)
@@ -1,16 +1,20 @@
 true;  ``; echo $?
 false; ``; echo $?
+# UNFIXED BUG. bash sets $? to 127:
 true;  `""`; echo $?
+# bash sets $? to 127:
 false; `""`; echo $?
 true;  `     `; echo $?
 false; `     `; echo $?
 
 true;  $(); echo $?
 false; $(); echo $?
+# bash sets $? to 127:
 true;  $(""); echo $?
+# bash sets $? to 127:
 false; $(""); echo $?
 true;  $(     ); echo $?
 false; $(     ); echo $?
 
-true;  exec ''; echo $?
-false; exec ''; echo $?
+exec ''; echo $?
+echo Not reached