diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-04 19:29:21 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-04 19:29:21 +0000 |
| commit | 552433bc5af76c8e5b52278b3d609e9a18c19dc2 (patch) | |
| tree | a12123629e6ce6779a9385ed17775ca8e9313808 | |
| parent | a24c8caeb29a11fafc748399ab6b30e6b8e638f0 (diff) | |
| download | busybox-w32-552433bc5af76c8e5b52278b3d609e9a18c19dc2.tar.gz busybox-w32-552433bc5af76c8e5b52278b3d609e9a18c19dc2.tar.bz2 busybox-w32-552433bc5af76c8e5b52278b3d609e9a18c19dc2.zip | |
hush: fix "var=val >file" not creating file
function old new delta
static.null_ptr - 4 +4
run_list 2018 2020 +2
handle_dollar 667 626 -41
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 1/1 up/down: 6/-41) Total: -35 bytes
| -rw-r--r-- | shell/hush.c | 128 | ||||
| -rw-r--r-- | shell/hush_test/hush-misc/redir1.right | 10 | ||||
| -rwxr-xr-x | shell/hush_test/hush-misc/redir1.tests | 32 |
3 files changed, 121 insertions, 49 deletions
diff --git a/shell/hush.c b/shell/hush.c index 0ac42c90b..61db928b2 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -593,7 +593,6 @@ static const struct built_in_command bltins[] = { | |||
| 593 | }; | 593 | }; |
| 594 | 594 | ||
| 595 | 595 | ||
| 596 | /* Normal */ | ||
| 597 | static void maybe_die(const char *notice, const char *msg) | 596 | static void maybe_die(const char *notice, const char *msg) |
| 598 | { | 597 | { |
| 599 | /* Was using fancy stuff: | 598 | /* Was using fancy stuff: |
| @@ -615,6 +614,7 @@ static void maybe_die(const char *notice, const char *msg) | |||
| 615 | #define syntax(msg) _syntax(msg, __LINE__) | 614 | #define syntax(msg) _syntax(msg, __LINE__) |
| 616 | #endif | 615 | #endif |
| 617 | 616 | ||
| 617 | |||
| 618 | static int glob_needed(const char *s) | 618 | static int glob_needed(const char *s) |
| 619 | { | 619 | { |
| 620 | while (*s) { | 620 | while (*s) { |
| @@ -816,7 +816,7 @@ static void free_strings(char **strings) | |||
| 816 | * sigset_t blocked_set: current blocked signal set | 816 | * sigset_t blocked_set: current blocked signal set |
| 817 | * | 817 | * |
| 818 | * "trap - SIGxxx": | 818 | * "trap - SIGxxx": |
| 819 | * clear bit in blocked_set unless it is also in non_DFL | 819 | * clear bit in blocked_set unless it is also in non_DFL_mask |
| 820 | * "trap 'cmd' SIGxxx": | 820 | * "trap 'cmd' SIGxxx": |
| 821 | * set bit in blocked_set (even if 'cmd' is '') | 821 | * set bit in blocked_set (even if 'cmd' is '') |
| 822 | * after [v]fork, if we plan to be a shell: | 822 | * after [v]fork, if we plan to be a shell: |
| @@ -2292,24 +2292,33 @@ static void pseudo_exec(nommu_save_t *nommu_save, | |||
| 2292 | struct command *command, | 2292 | struct command *command, |
| 2293 | char **argv_expanded) | 2293 | char **argv_expanded) |
| 2294 | { | 2294 | { |
| 2295 | if (command->argv) | 2295 | if (command->argv) { |
| 2296 | pseudo_exec_argv(nommu_save, command->argv, command->assignment_cnt, argv_expanded); | 2296 | pseudo_exec_argv(nommu_save, command->argv, |
| 2297 | command->assignment_cnt, argv_expanded); | ||
| 2298 | } | ||
| 2297 | 2299 | ||
| 2298 | if (command->group) { | 2300 | if (command->group) { |
| 2299 | #if !BB_MMU | 2301 | /* Cases when we are here: |
| 2300 | bb_error_msg_and_die("nested lists are not supported on NOMMU"); | 2302 | * ( list ) |
| 2301 | #else | 2303 | * { list } & |
| 2304 | * ... | ( list ) | ... | ||
| 2305 | * ... | { list } | ... | ||
| 2306 | */ | ||
| 2307 | #if BB_MMU | ||
| 2302 | int rcode; | 2308 | int rcode; |
| 2303 | debug_printf_exec("pseudo_exec: run_list\n"); | 2309 | debug_printf_exec("pseudo_exec: run_list\n"); |
| 2304 | rcode = run_list(command->group); | 2310 | rcode = run_list(command->group); |
| 2305 | /* OK to leak memory by not calling free_pipe_list, | 2311 | /* OK to leak memory by not calling free_pipe_list, |
| 2306 | * since this process is about to exit */ | 2312 | * since this process is about to exit */ |
| 2307 | _exit(rcode); | 2313 | _exit(rcode); |
| 2314 | #else | ||
| 2315 | //TODO: re-exec "hush -c command->group_as_a_string" | ||
| 2316 | bb_error_msg_and_die("nested lists are not supported on NOMMU"); | ||
| 2308 | #endif | 2317 | #endif |
| 2309 | } | 2318 | } |
| 2310 | 2319 | ||
| 2311 | /* Can happen. See what bash does with ">foo" by itself. */ | 2320 | /* Can happen. See what bash does with ">foo" by itself. */ |
| 2312 | debug_printf("trying to pseudo_exec null command\n"); | 2321 | debug_printf("pseudo_exec'ed null command\n"); |
| 2313 | _exit(EXIT_SUCCESS); | 2322 | _exit(EXIT_SUCCESS); |
| 2314 | } | 2323 | } |
| 2315 | 2324 | ||
| @@ -2563,61 +2572,72 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||
| 2563 | /* run_pipe() starts all the jobs, but doesn't wait for anything | 2572 | /* run_pipe() starts all the jobs, but doesn't wait for anything |
| 2564 | * to finish. See checkjobs(). | 2573 | * to finish. See checkjobs(). |
| 2565 | * | 2574 | * |
| 2566 | * return code is normally -1, when the caller has to wait for children | 2575 | * Return code is normally -1, when the caller has to wait for children |
| 2567 | * to finish to determine the exit status of the pipe. If the pipe | 2576 | * to finish to determine the exit status of the pipe. If the pipe |
| 2568 | * is a simple builtin command, however, the action is done by the | 2577 | * is a simple builtin command, however, the action is done by the |
| 2569 | * time run_pipe returns, and the exit code is provided as the | 2578 | * time run_pipe returns, and the exit code is provided as the |
| 2570 | * return value. | 2579 | * return value. |
| 2571 | * | 2580 | * |
| 2572 | * The input of the pipe is always stdin, the output is always | ||
| 2573 | * stdout. The outpipe[] mechanism in BusyBox-0.48 lash is bogus, | ||
| 2574 | * because it tries to avoid running the command substitution in | ||
| 2575 | * subshell, when that is in fact necessary. The subshell process | ||
| 2576 | * now has its stdout directed to the input of the appropriate pipe, | ||
| 2577 | * so this routine is noticeably simpler. | ||
| 2578 | * | ||
| 2579 | * Returns -1 only if started some children. IOW: we have to | 2581 | * Returns -1 only if started some children. IOW: we have to |
| 2580 | * mask out retvals of builtins etc with 0xff! | 2582 | * mask out retvals of builtins etc with 0xff! |
| 2583 | * | ||
| 2584 | * The only case when we do not need to [v]fork is when the pipe | ||
| 2585 | * is single, non-backgrounded, non-subshell command. Examples: | ||
| 2586 | * cmd ; ... { list } ; ... | ||
| 2587 | * cmd && ... { list } && ... | ||
| 2588 | * cmd || ... { list } || ... | ||
| 2589 | * If it is, then we can run cmd as a builtin, NOFORK [do we do this?], | ||
| 2590 | * or (if SH_STANDALONE) an applet, and we can run the { list } | ||
| 2591 | * with run_list(). Otherwise, we fork and exec cmd. | ||
| 2592 | * | ||
| 2593 | * Cases when we must fork: | ||
| 2594 | * non-single: cmd | cmd | ||
| 2595 | * backgrounded: cmd & { list } & | ||
| 2596 | * subshell: ( list ) [&] | ||
| 2581 | */ | 2597 | */ |
| 2582 | static int run_pipe(struct pipe *pi) | 2598 | static int run_pipe(struct pipe *pi) |
| 2583 | { | 2599 | { |
| 2600 | static const char *const null_ptr = NULL; | ||
| 2584 | int i; | 2601 | int i; |
| 2585 | int nextin; | 2602 | int nextin; |
| 2586 | int pipefds[2]; /* pipefds[0] is for reading */ | 2603 | int pipefds[2]; /* pipefds[0] is for reading */ |
| 2587 | struct command *command; | 2604 | struct command *command; |
| 2588 | char **argv_expanded; | 2605 | char **argv_expanded; |
| 2589 | char **argv; | 2606 | char **argv; |
| 2590 | const struct built_in_command *x; | ||
| 2591 | char *p; | 2607 | char *p; |
| 2592 | /* it is not always needed, but we aim to smaller code */ | 2608 | /* it is not always needed, but we aim to smaller code */ |
| 2593 | int squirrel[] = { -1, -1, -1 }; | 2609 | int squirrel[] = { -1, -1, -1 }; |
| 2594 | int rcode; | 2610 | int rcode; |
| 2595 | const int single_and_fg = (pi->num_cmds == 1 && pi->followup != PIPE_BG); | ||
| 2596 | 2611 | ||
| 2597 | debug_printf_exec("run_pipe start: single_and_fg=%d\n", single_and_fg); | 2612 | debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds); |
| 2598 | 2613 | ||
| 2599 | #if ENABLE_HUSH_JOB | 2614 | USE_HUSH_JOB(pi->pgrp = -1;) |
| 2600 | pi->pgrp = -1; | ||
| 2601 | #endif | ||
| 2602 | pi->alive_cmds = 1; | ||
| 2603 | pi->stopped_cmds = 0; | 2615 | pi->stopped_cmds = 0; |
| 2604 | |||
| 2605 | /* Check if this is a simple builtin (not part of a pipe). | ||
| 2606 | * Builtins within pipes have to fork anyway, and are handled in | ||
| 2607 | * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. | ||
| 2608 | */ | ||
| 2609 | command = &(pi->cmds[0]); | 2616 | command = &(pi->cmds[0]); |
| 2617 | argv_expanded = NULL; | ||
| 2610 | 2618 | ||
| 2611 | #if ENABLE_HUSH_FUNCTIONS | 2619 | if (pi->num_cmds != 1 |
| 2612 | if (single_and_fg && command->group && command->grp_type == GRP_FUNCTION) { | 2620 | || pi->followup == PIPE_BG |
| 2613 | /* We "execute" function definition */ | 2621 | || command->grp_type == GRP_SUBSHELL |
| 2614 | bb_error_msg("here we ought to remember function definition, and go on"); | 2622 | ) { |
| 2615 | return EXIT_SUCCESS; | 2623 | goto must_fork; |
| 2616 | } | 2624 | } |
| 2617 | #endif | ||
| 2618 | 2625 | ||
| 2619 | if (single_and_fg && command->group && command->grp_type == GRP_NORMAL) { | 2626 | pi->alive_cmds = 1; |
| 2620 | debug_printf("non-subshell grouping\n"); | 2627 | |
| 2628 | debug_printf_exec(": group:%p argv:'%s'\n", | ||
| 2629 | command->group, command->argv ? command->argv[0] : "NONE"); | ||
| 2630 | |||
| 2631 | if (command->group) { | ||
| 2632 | #if ENABLE_HUSH_FUNCTIONS | ||
| 2633 | if (command->grp_type == GRP_FUNCTION) { | ||
| 2634 | /* func () { list } */ | ||
| 2635 | bb_error_msg("here we ought to remember function definition, and go on"); | ||
| 2636 | return EXIT_SUCCESS; | ||
| 2637 | } | ||
| 2638 | #endif | ||
| 2639 | /* { list } */ | ||
| 2640 | debug_printf("non-subshell group\n"); | ||
| 2621 | setup_redirects(command, squirrel); | 2641 | setup_redirects(command, squirrel); |
| 2622 | debug_printf_exec(": run_list\n"); | 2642 | debug_printf_exec(": run_list\n"); |
| 2623 | rcode = run_list(command->group) & 0xff; | 2643 | rcode = run_list(command->group) & 0xff; |
| @@ -2627,26 +2647,33 @@ static int run_pipe(struct pipe *pi) | |||
| 2627 | return rcode; | 2647 | return rcode; |
| 2628 | } | 2648 | } |
| 2629 | 2649 | ||
| 2630 | argv = command->argv; | 2650 | argv = command->argv ? command->argv : (char **) &null_ptr; |
| 2631 | argv_expanded = NULL; | 2651 | { |
| 2632 | 2652 | const struct built_in_command *x; | |
| 2633 | if (single_and_fg && argv != NULL) { | ||
| 2634 | char **new_env = NULL; | 2653 | char **new_env = NULL; |
| 2635 | char **old_env = NULL; | 2654 | char **old_env = NULL; |
| 2636 | 2655 | ||
| 2637 | i = command->assignment_cnt; | 2656 | if (argv[command->assignment_cnt] == NULL) { |
| 2638 | if (i != 0 && argv[i] == NULL) { | 2657 | /* Assignments, but no command */ |
| 2639 | /* assignments, but no command: set local environment */ | 2658 | /* Ensure redirects take effect. Try "a=t >file" */ |
| 2640 | for (i = 0; argv[i] != NULL; i++) { | 2659 | setup_redirects(command, squirrel); |
| 2641 | debug_printf("local environment set: %s\n", argv[i]); | 2660 | restore_redirects(squirrel); |
| 2642 | p = expand_string_to_string(argv[i]); | 2661 | /* Set shell variables */ |
| 2662 | while (*argv) { | ||
| 2663 | p = expand_string_to_string(*argv); | ||
| 2664 | debug_printf_exec("set shell var:'%s'->'%s'\n", | ||
| 2665 | *argv, p); | ||
| 2643 | set_local_var(p, 0); | 2666 | set_local_var(p, 0); |
| 2667 | argv++; | ||
| 2644 | } | 2668 | } |
| 2645 | return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ | 2669 | /* Do we need to flag set_local_var() errors? |
| 2670 | * "assignment to readonly var" and "putenv error" | ||
| 2671 | */ | ||
| 2672 | return EXIT_SUCCESS; | ||
| 2646 | } | 2673 | } |
| 2647 | 2674 | ||
| 2648 | /* Expand the rest into (possibly) many strings each */ | 2675 | /* Expand the rest into (possibly) many strings each */ |
| 2649 | argv_expanded = expand_strvec_to_strvec(argv + i); | 2676 | argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); |
| 2650 | 2677 | ||
| 2651 | for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { | 2678 | for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { |
| 2652 | if (strcmp(argv_expanded[0], x->cmd) != 0) | 2679 | if (strcmp(argv_expanded[0], x->cmd) != 0) |
| @@ -2694,8 +2721,10 @@ static int run_pipe(struct pipe *pi) | |||
| 2694 | goto clean_up_and_ret; | 2721 | goto clean_up_and_ret; |
| 2695 | } | 2722 | } |
| 2696 | #endif | 2723 | #endif |
| 2724 | /* It is neither builtin nor applet. We must fork. */ | ||
| 2697 | } | 2725 | } |
| 2698 | 2726 | ||
| 2727 | must_fork: | ||
| 2699 | /* NB: argv_expanded may already be created, and that | 2728 | /* NB: argv_expanded may already be created, and that |
| 2700 | * might include `cmd` runs! Do not rerun it! We *must* | 2729 | * might include `cmd` runs! Do not rerun it! We *must* |
| 2701 | * use argv_expanded if it's non-NULL */ | 2730 | * use argv_expanded if it's non-NULL */ |
| @@ -2715,8 +2744,9 @@ static int run_pipe(struct pipe *pi) | |||
| 2715 | if (command->argv) { | 2744 | if (command->argv) { |
| 2716 | debug_printf_exec(": pipe member '%s' '%s'...\n", | 2745 | debug_printf_exec(": pipe member '%s' '%s'...\n", |
| 2717 | command->argv[0], command->argv[1]); | 2746 | command->argv[0], command->argv[1]); |
| 2718 | } else | 2747 | } else { |
| 2719 | debug_printf_exec(": pipe member with no argv\n"); | 2748 | debug_printf_exec(": pipe member with no argv\n"); |
| 2749 | } | ||
| 2720 | 2750 | ||
| 2721 | /* pipes are inserted between pairs of commands */ | 2751 | /* pipes are inserted between pairs of commands */ |
| 2722 | pipefds[0] = 0; | 2752 | pipefds[0] = 0; |
diff --git a/shell/hush_test/hush-misc/redir1.right b/shell/hush_test/hush-misc/redir1.right new file mode 100644 index 000000000..ac90b4a0a --- /dev/null +++ b/shell/hush_test/hush-misc/redir1.right | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | Test 1: var:ok | ||
| 2 | File created:ok | ||
| 3 | Test 2: var:ok | ||
| 4 | File created:ok | ||
| 5 | Test 3: var:ok | ||
| 6 | File created:ok | ||
| 7 | Test 4: var:ok | ||
| 8 | File created:ok | ||
| 9 | Test 5: var:ok | ||
| 10 | File created:ok | ||
diff --git a/shell/hush_test/hush-misc/redir1.tests b/shell/hush_test/hush-misc/redir1.tests new file mode 100755 index 000000000..5f6c20612 --- /dev/null +++ b/shell/hush_test/hush-misc/redir1.tests | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | rm shell_test_$$ 2>/dev/null | ||
| 2 | var=bad | ||
| 3 | var=ok >shell_test_$$ | ||
| 4 | echo "Test 1: var:$var" | ||
| 5 | test -f shell_test_$$ && echo "File created:ok" | ||
| 6 | |||
| 7 | rm shell_test_$$ 2>/dev/null | ||
| 8 | var=ok | ||
| 9 | true | var=bad >shell_test_$$ | ||
| 10 | echo "Test 2: var:$var" | ||
| 11 | test -f shell_test_$$ && echo "File created:ok" | ||
| 12 | |||
| 13 | rm shell_test_$$ 2>/dev/null | ||
| 14 | var=bad | ||
| 15 | { var=ok >shell_test_$$; } | ||
| 16 | echo "Test 3: var:$var" | ||
| 17 | test -f shell_test_$$ && echo "File created:ok" | ||
| 18 | |||
| 19 | rm shell_test_$$ 2>/dev/null | ||
| 20 | var=ok | ||
| 21 | { var=bad >shell_test_$$; } & | ||
| 22 | usleep 100000 | ||
| 23 | echo "Test 4: var:$var" | ||
| 24 | test -f shell_test_$$ && echo "File created:ok" | ||
| 25 | |||
| 26 | rm shell_test_$$ 2>/dev/null | ||
| 27 | var=ok | ||
| 28 | ( var=bad >shell_test_$$ ) | ||
| 29 | echo "Test 5: var:$var" | ||
| 30 | test -f shell_test_$$ && echo "File created:ok" | ||
| 31 | |||
| 32 | rm shell_test_$$ 2>/dev/null | ||
