diff options
| author | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-05 14:50:59 +0200 |
|---|---|---|
| committer | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-05 14:50:59 +0200 |
| commit | b36abf2dfcb47cc0ca659c916b3efef9f757ea0c (patch) | |
| tree | b6df2d914b46ce369c600c317e9e792f9930b80d /shell | |
| parent | 38292b68c962b9d470fa4e577020749c8c69226d (diff) | |
| download | busybox-w32-b36abf2dfcb47cc0ca659c916b3efef9f757ea0c.tar.gz busybox-w32-b36abf2dfcb47cc0ca659c916b3efef9f757ea0c.tar.bz2 busybox-w32-b36abf2dfcb47cc0ca659c916b3efef9f757ea0c.zip | |
hush: big reordering: move parser functions to the beginning. no code changes
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 4084 |
1 files changed, 2042 insertions, 2042 deletions
diff --git a/shell/hush.c b/shell/hush.c index 1bed72159..2afe12fea 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -2515,20 +2515,1864 @@ static char **o_finalize_list(o_string *o, int n) | |||
| 2515 | return list; | 2515 | return list; |
| 2516 | } | 2516 | } |
| 2517 | 2517 | ||
| 2518 | static void free_pipe_list(struct pipe *head); | ||
| 2518 | 2519 | ||
| 2519 | /* Expansion can recurse */ | 2520 | /* Return code is the exit status of the pipe */ |
| 2520 | #if ENABLE_HUSH_TICK | 2521 | static void free_pipe(struct pipe *pi) |
| 2521 | static int process_command_subs(o_string *dest, const char *s); | 2522 | { |
| 2523 | char **p; | ||
| 2524 | struct command *command; | ||
| 2525 | struct redir_struct *r, *rnext; | ||
| 2526 | int a, i; | ||
| 2527 | |||
| 2528 | if (pi->stopped_cmds > 0) /* why? */ | ||
| 2529 | return; | ||
| 2530 | debug_printf_clean("run pipe: (pid %d)\n", getpid()); | ||
| 2531 | for (i = 0; i < pi->num_cmds; i++) { | ||
| 2532 | command = &pi->cmds[i]; | ||
| 2533 | debug_printf_clean(" command %d:\n", i); | ||
| 2534 | if (command->argv) { | ||
| 2535 | for (a = 0, p = command->argv; *p; a++, p++) { | ||
| 2536 | debug_printf_clean(" argv[%d] = %s\n", a, *p); | ||
| 2537 | } | ||
| 2538 | free_strings(command->argv); | ||
| 2539 | command->argv = NULL; | ||
| 2540 | } | ||
| 2541 | /* not "else if": on syntax error, we may have both! */ | ||
| 2542 | if (command->group) { | ||
| 2543 | debug_printf_clean(" begin group (cmd_type:%d)\n", | ||
| 2544 | command->cmd_type); | ||
| 2545 | free_pipe_list(command->group); | ||
| 2546 | debug_printf_clean(" end group\n"); | ||
| 2547 | command->group = NULL; | ||
| 2548 | } | ||
| 2549 | /* else is crucial here. | ||
| 2550 | * If group != NULL, child_func is meaningless */ | ||
| 2551 | #if ENABLE_HUSH_FUNCTIONS | ||
| 2552 | else if (command->child_func) { | ||
| 2553 | debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func); | ||
| 2554 | command->child_func->parent_cmd = NULL; | ||
| 2555 | } | ||
| 2522 | #endif | 2556 | #endif |
| 2523 | static char *expand_string_to_string(const char *str); | 2557 | #if !BB_MMU |
| 2558 | free(command->group_as_string); | ||
| 2559 | command->group_as_string = NULL; | ||
| 2560 | #endif | ||
| 2561 | for (r = command->redirects; r; r = rnext) { | ||
| 2562 | debug_printf_clean(" redirect %d%s", | ||
| 2563 | r->rd_fd, redir_table[r->rd_type].descrip); | ||
| 2564 | /* guard against the case >$FOO, where foo is unset or blank */ | ||
| 2565 | if (r->rd_filename) { | ||
| 2566 | debug_printf_clean(" fname:'%s'\n", r->rd_filename); | ||
| 2567 | free(r->rd_filename); | ||
| 2568 | r->rd_filename = NULL; | ||
| 2569 | } | ||
| 2570 | debug_printf_clean(" rd_dup:%d\n", r->rd_dup); | ||
| 2571 | rnext = r->next; | ||
| 2572 | free(r); | ||
| 2573 | } | ||
| 2574 | command->redirects = NULL; | ||
| 2575 | } | ||
| 2576 | free(pi->cmds); /* children are an array, they get freed all at once */ | ||
| 2577 | pi->cmds = NULL; | ||
| 2578 | #if ENABLE_HUSH_JOB | ||
| 2579 | free(pi->cmdtext); | ||
| 2580 | pi->cmdtext = NULL; | ||
| 2581 | #endif | ||
| 2582 | } | ||
| 2583 | |||
| 2584 | static void free_pipe_list(struct pipe *head) | ||
| 2585 | { | ||
| 2586 | struct pipe *pi, *next; | ||
| 2587 | |||
| 2588 | for (pi = head; pi; pi = next) { | ||
| 2589 | #if HAS_KEYWORDS | ||
| 2590 | debug_printf_clean(" pipe reserved word %d\n", pi->res_word); | ||
| 2591 | #endif | ||
| 2592 | free_pipe(pi); | ||
| 2593 | debug_printf_clean("pipe followup code %d\n", pi->followup); | ||
| 2594 | next = pi->next; | ||
| 2595 | /*pi->next = NULL;*/ | ||
| 2596 | free(pi); | ||
| 2597 | } | ||
| 2598 | } | ||
| 2599 | |||
| 2600 | |||
| 2601 | /*** Parsing routines ***/ | ||
| 2602 | |||
| 2603 | static struct pipe *new_pipe(void) | ||
| 2604 | { | ||
| 2605 | struct pipe *pi; | ||
| 2606 | pi = xzalloc(sizeof(struct pipe)); | ||
| 2607 | /*pi->followup = 0; - deliberately invalid value */ | ||
| 2608 | /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */ | ||
| 2609 | return pi; | ||
| 2610 | } | ||
| 2611 | |||
| 2612 | /* Command (member of a pipe) is complete, or we start a new pipe | ||
| 2613 | * if ctx->command is NULL. | ||
| 2614 | * No errors possible here. | ||
| 2615 | */ | ||
| 2616 | static int done_command(struct parse_context *ctx) | ||
| 2617 | { | ||
| 2618 | /* The command is really already in the pipe structure, so | ||
| 2619 | * advance the pipe counter and make a new, null command. */ | ||
| 2620 | struct pipe *pi = ctx->pipe; | ||
| 2621 | struct command *command = ctx->command; | ||
| 2622 | |||
| 2623 | if (command) { | ||
| 2624 | if (IS_NULL_CMD(command)) { | ||
| 2625 | debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); | ||
| 2626 | goto clear_and_ret; | ||
| 2627 | } | ||
| 2628 | pi->num_cmds++; | ||
| 2629 | debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); | ||
| 2630 | //debug_print_tree(ctx->list_head, 20); | ||
| 2631 | } else { | ||
| 2632 | debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds); | ||
| 2633 | } | ||
| 2634 | |||
| 2635 | /* Only real trickiness here is that the uncommitted | ||
| 2636 | * command structure is not counted in pi->num_cmds. */ | ||
| 2637 | pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1)); | ||
| 2638 | ctx->command = command = &pi->cmds[pi->num_cmds]; | ||
| 2639 | clear_and_ret: | ||
| 2640 | memset(command, 0, sizeof(*command)); | ||
| 2641 | return pi->num_cmds; /* used only for 0/nonzero check */ | ||
| 2642 | } | ||
| 2643 | |||
| 2644 | static void done_pipe(struct parse_context *ctx, pipe_style type) | ||
| 2645 | { | ||
| 2646 | int not_null; | ||
| 2647 | |||
| 2648 | debug_printf_parse("done_pipe entered, followup %d\n", type); | ||
| 2649 | /* Close previous command */ | ||
| 2650 | not_null = done_command(ctx); | ||
| 2651 | ctx->pipe->followup = type; | ||
| 2652 | #if HAS_KEYWORDS | ||
| 2653 | ctx->pipe->pi_inverted = ctx->ctx_inverted; | ||
| 2654 | ctx->ctx_inverted = 0; | ||
| 2655 | ctx->pipe->res_word = ctx->ctx_res_w; | ||
| 2656 | #endif | ||
| 2657 | |||
| 2658 | /* Without this check, even just <enter> on command line generates | ||
| 2659 | * tree of three NOPs (!). Which is harmless but annoying. | ||
| 2660 | * IOW: it is safe to do it unconditionally. */ | ||
| 2661 | if (not_null | ||
| 2662 | #if ENABLE_HUSH_IF | ||
| 2663 | || ctx->ctx_res_w == RES_FI | ||
| 2664 | #endif | ||
| 2665 | #if ENABLE_HUSH_LOOPS | ||
| 2666 | || ctx->ctx_res_w == RES_DONE | ||
| 2667 | || ctx->ctx_res_w == RES_FOR | ||
| 2668 | || ctx->ctx_res_w == RES_IN | ||
| 2669 | #endif | ||
| 2670 | #if ENABLE_HUSH_CASE | ||
| 2671 | || ctx->ctx_res_w == RES_ESAC | ||
| 2672 | #endif | ||
| 2673 | ) { | ||
| 2674 | struct pipe *new_p; | ||
| 2675 | debug_printf_parse("done_pipe: adding new pipe: " | ||
| 2676 | "not_null:%d ctx->ctx_res_w:%d\n", | ||
| 2677 | not_null, ctx->ctx_res_w); | ||
| 2678 | new_p = new_pipe(); | ||
| 2679 | ctx->pipe->next = new_p; | ||
| 2680 | ctx->pipe = new_p; | ||
| 2681 | /* RES_THEN, RES_DO etc are "sticky" - | ||
| 2682 | * they remain set for pipes inside if/while. | ||
| 2683 | * This is used to control execution. | ||
| 2684 | * RES_FOR and RES_IN are NOT sticky (needed to support | ||
| 2685 | * cases where variable or value happens to match a keyword): | ||
| 2686 | */ | ||
| 2687 | #if ENABLE_HUSH_LOOPS | ||
| 2688 | if (ctx->ctx_res_w == RES_FOR | ||
| 2689 | || ctx->ctx_res_w == RES_IN) | ||
| 2690 | ctx->ctx_res_w = RES_NONE; | ||
| 2691 | #endif | ||
| 2692 | #if ENABLE_HUSH_CASE | ||
| 2693 | if (ctx->ctx_res_w == RES_MATCH) | ||
| 2694 | ctx->ctx_res_w = RES_CASE_BODY; | ||
| 2695 | if (ctx->ctx_res_w == RES_CASE) | ||
| 2696 | ctx->ctx_res_w = RES_CASE_IN; | ||
| 2697 | #endif | ||
| 2698 | ctx->command = NULL; /* trick done_command below */ | ||
| 2699 | /* Create the memory for command, roughly: | ||
| 2700 | * ctx->pipe->cmds = new struct command; | ||
| 2701 | * ctx->command = &ctx->pipe->cmds[0]; | ||
| 2702 | */ | ||
| 2703 | done_command(ctx); | ||
| 2704 | //debug_print_tree(ctx->list_head, 10); | ||
| 2705 | } | ||
| 2706 | debug_printf_parse("done_pipe return\n"); | ||
| 2707 | } | ||
| 2708 | |||
| 2709 | static void initialize_context(struct parse_context *ctx) | ||
| 2710 | { | ||
| 2711 | memset(ctx, 0, sizeof(*ctx)); | ||
| 2712 | ctx->pipe = ctx->list_head = new_pipe(); | ||
| 2713 | /* Create the memory for command, roughly: | ||
| 2714 | * ctx->pipe->cmds = new struct command; | ||
| 2715 | * ctx->command = &ctx->pipe->cmds[0]; | ||
| 2716 | */ | ||
| 2717 | done_command(ctx); | ||
| 2718 | } | ||
| 2719 | |||
| 2720 | /* If a reserved word is found and processed, parse context is modified | ||
| 2721 | * and 1 is returned. | ||
| 2722 | */ | ||
| 2723 | #if HAS_KEYWORDS | ||
| 2724 | struct reserved_combo { | ||
| 2725 | char literal[6]; | ||
| 2726 | unsigned char res; | ||
| 2727 | unsigned char assignment_flag; | ||
| 2728 | int flag; | ||
| 2729 | }; | ||
| 2730 | enum { | ||
| 2731 | FLAG_END = (1 << RES_NONE ), | ||
| 2732 | # if ENABLE_HUSH_IF | ||
| 2733 | FLAG_IF = (1 << RES_IF ), | ||
| 2734 | FLAG_THEN = (1 << RES_THEN ), | ||
| 2735 | FLAG_ELIF = (1 << RES_ELIF ), | ||
| 2736 | FLAG_ELSE = (1 << RES_ELSE ), | ||
| 2737 | FLAG_FI = (1 << RES_FI ), | ||
| 2738 | # endif | ||
| 2739 | # if ENABLE_HUSH_LOOPS | ||
| 2740 | FLAG_FOR = (1 << RES_FOR ), | ||
| 2741 | FLAG_WHILE = (1 << RES_WHILE), | ||
| 2742 | FLAG_UNTIL = (1 << RES_UNTIL), | ||
| 2743 | FLAG_DO = (1 << RES_DO ), | ||
| 2744 | FLAG_DONE = (1 << RES_DONE ), | ||
| 2745 | FLAG_IN = (1 << RES_IN ), | ||
| 2746 | # endif | ||
| 2747 | # if ENABLE_HUSH_CASE | ||
| 2748 | FLAG_MATCH = (1 << RES_MATCH), | ||
| 2749 | FLAG_ESAC = (1 << RES_ESAC ), | ||
| 2750 | # endif | ||
| 2751 | FLAG_START = (1 << RES_XXXX ), | ||
| 2752 | }; | ||
| 2753 | |||
| 2754 | static const struct reserved_combo* match_reserved_word(o_string *word) | ||
| 2755 | { | ||
| 2756 | /* Mostly a list of accepted follow-up reserved words. | ||
| 2757 | * FLAG_END means we are done with the sequence, and are ready | ||
| 2758 | * to turn the compound list into a command. | ||
| 2759 | * FLAG_START means the word must start a new compound list. | ||
| 2760 | */ | ||
| 2761 | static const struct reserved_combo reserved_list[] = { | ||
| 2762 | # if ENABLE_HUSH_IF | ||
| 2763 | { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, | ||
| 2764 | { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START }, | ||
| 2765 | { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, | ||
| 2766 | { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN }, | ||
| 2767 | { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI }, | ||
| 2768 | { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, | ||
| 2769 | # endif | ||
| 2770 | # if ENABLE_HUSH_LOOPS | ||
| 2771 | { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, | ||
| 2772 | { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, | ||
| 2773 | { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, | ||
| 2774 | { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, | ||
| 2775 | { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE }, | ||
| 2776 | { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, | ||
| 2777 | # endif | ||
| 2778 | # if ENABLE_HUSH_CASE | ||
| 2779 | { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, | ||
| 2780 | { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, | ||
| 2781 | # endif | ||
| 2782 | }; | ||
| 2783 | const struct reserved_combo *r; | ||
| 2784 | |||
| 2785 | for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { | ||
| 2786 | if (strcmp(word->data, r->literal) == 0) | ||
| 2787 | return r; | ||
| 2788 | } | ||
| 2789 | return NULL; | ||
| 2790 | } | ||
| 2791 | /* Return 0: not a keyword, 1: keyword | ||
| 2792 | */ | ||
| 2793 | static int reserved_word(o_string *word, struct parse_context *ctx) | ||
| 2794 | { | ||
| 2795 | # if ENABLE_HUSH_CASE | ||
| 2796 | static const struct reserved_combo reserved_match = { | ||
| 2797 | "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC | ||
| 2798 | }; | ||
| 2799 | # endif | ||
| 2800 | const struct reserved_combo *r; | ||
| 2801 | |||
| 2802 | if (word->has_quoted_part) | ||
| 2803 | return 0; | ||
| 2804 | r = match_reserved_word(word); | ||
| 2805 | if (!r) | ||
| 2806 | return 0; | ||
| 2807 | |||
| 2808 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); | ||
| 2809 | # if ENABLE_HUSH_CASE | ||
| 2810 | if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) { | ||
| 2811 | /* "case word IN ..." - IN part starts first MATCH part */ | ||
| 2812 | r = &reserved_match; | ||
| 2813 | } else | ||
| 2814 | # endif | ||
| 2815 | if (r->flag == 0) { /* '!' */ | ||
| 2816 | if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ | ||
| 2817 | syntax_error("! ! command"); | ||
| 2818 | ctx->ctx_res_w = RES_SNTX; | ||
| 2819 | } | ||
| 2820 | ctx->ctx_inverted = 1; | ||
| 2821 | return 1; | ||
| 2822 | } | ||
| 2823 | if (r->flag & FLAG_START) { | ||
| 2824 | struct parse_context *old; | ||
| 2825 | |||
| 2826 | old = xmalloc(sizeof(*old)); | ||
| 2827 | debug_printf_parse("push stack %p\n", old); | ||
| 2828 | *old = *ctx; /* physical copy */ | ||
| 2829 | initialize_context(ctx); | ||
| 2830 | ctx->stack = old; | ||
| 2831 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { | ||
| 2832 | syntax_error_at(word->data); | ||
| 2833 | ctx->ctx_res_w = RES_SNTX; | ||
| 2834 | return 1; | ||
| 2835 | } else { | ||
| 2836 | /* "{...} fi" is ok. "{...} if" is not | ||
| 2837 | * Example: | ||
| 2838 | * if { echo foo; } then { echo bar; } fi */ | ||
| 2839 | if (ctx->command->group) | ||
| 2840 | done_pipe(ctx, PIPE_SEQ); | ||
| 2841 | } | ||
| 2842 | |||
| 2843 | ctx->ctx_res_w = r->res; | ||
| 2844 | ctx->old_flag = r->flag; | ||
| 2845 | word->o_assignment = r->assignment_flag; | ||
| 2846 | |||
| 2847 | if (ctx->old_flag & FLAG_END) { | ||
| 2848 | struct parse_context *old; | ||
| 2849 | |||
| 2850 | done_pipe(ctx, PIPE_SEQ); | ||
| 2851 | debug_printf_parse("pop stack %p\n", ctx->stack); | ||
| 2852 | old = ctx->stack; | ||
| 2853 | old->command->group = ctx->list_head; | ||
| 2854 | old->command->cmd_type = CMD_NORMAL; | ||
| 2855 | # if !BB_MMU | ||
| 2856 | o_addstr(&old->as_string, ctx->as_string.data); | ||
| 2857 | o_free_unsafe(&ctx->as_string); | ||
| 2858 | old->command->group_as_string = xstrdup(old->as_string.data); | ||
| 2859 | debug_printf_parse("pop, remembering as:'%s'\n", | ||
| 2860 | old->command->group_as_string); | ||
| 2861 | # endif | ||
| 2862 | *ctx = *old; /* physical copy */ | ||
| 2863 | free(old); | ||
| 2864 | } | ||
| 2865 | return 1; | ||
| 2866 | } | ||
| 2867 | #endif /* HAS_KEYWORDS */ | ||
| 2868 | |||
| 2869 | /* Word is complete, look at it and update parsing context. | ||
| 2870 | * Normal return is 0. Syntax errors return 1. | ||
| 2871 | * Note: on return, word is reset, but not o_free'd! | ||
| 2872 | */ | ||
| 2873 | static int done_word(o_string *word, struct parse_context *ctx) | ||
| 2874 | { | ||
| 2875 | struct command *command = ctx->command; | ||
| 2876 | |||
| 2877 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, command); | ||
| 2878 | if (word->length == 0 && !word->has_quoted_part) { | ||
| 2879 | debug_printf_parse("done_word return 0: true null, ignored\n"); | ||
| 2880 | return 0; | ||
| 2881 | } | ||
| 2882 | |||
| 2883 | if (ctx->pending_redirect) { | ||
| 2884 | /* We do not glob in e.g. >*.tmp case. bash seems to glob here | ||
| 2885 | * only if run as "bash", not "sh" */ | ||
| 2886 | /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html | ||
| 2887 | * "2.7 Redirection | ||
| 2888 | * ...the word that follows the redirection operator | ||
| 2889 | * shall be subjected to tilde expansion, parameter expansion, | ||
| 2890 | * command substitution, arithmetic expansion, and quote | ||
| 2891 | * removal. Pathname expansion shall not be performed | ||
| 2892 | * on the word by a non-interactive shell; an interactive | ||
| 2893 | * shell may perform it, but shall do so only when | ||
| 2894 | * the expansion would result in one word." | ||
| 2895 | */ | ||
| 2896 | ctx->pending_redirect->rd_filename = xstrdup(word->data); | ||
| 2897 | /* Cater for >\file case: | ||
| 2898 | * >\a creates file a; >\\a, >"\a", >"\\a" create file \a | ||
| 2899 | * Same with heredocs: | ||
| 2900 | * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H | ||
| 2901 | */ | ||
| 2902 | if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) { | ||
| 2903 | unbackslash(ctx->pending_redirect->rd_filename); | ||
| 2904 | /* Is it <<"HEREDOC"? */ | ||
| 2905 | if (word->has_quoted_part) { | ||
| 2906 | ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED; | ||
| 2907 | } | ||
| 2908 | } | ||
| 2909 | debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); | ||
| 2910 | ctx->pending_redirect = NULL; | ||
| 2911 | } else { | ||
| 2912 | /* If this word wasn't an assignment, next ones definitely | ||
| 2913 | * can't be assignments. Even if they look like ones. */ | ||
| 2914 | if (word->o_assignment != DEFINITELY_ASSIGNMENT | ||
| 2915 | && word->o_assignment != WORD_IS_KEYWORD | ||
| 2916 | ) { | ||
| 2917 | word->o_assignment = NOT_ASSIGNMENT; | ||
| 2918 | } else { | ||
| 2919 | if (word->o_assignment == DEFINITELY_ASSIGNMENT) | ||
| 2920 | command->assignment_cnt++; | ||
| 2921 | word->o_assignment = MAYBE_ASSIGNMENT; | ||
| 2922 | } | ||
| 2923 | |||
| 2924 | #if HAS_KEYWORDS | ||
| 2925 | # if ENABLE_HUSH_CASE | ||
| 2926 | if (ctx->ctx_dsemicolon | ||
| 2927 | && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */ | ||
| 2928 | ) { | ||
| 2929 | /* already done when ctx_dsemicolon was set to 1: */ | ||
| 2930 | /* ctx->ctx_res_w = RES_MATCH; */ | ||
| 2931 | ctx->ctx_dsemicolon = 0; | ||
| 2932 | } else | ||
| 2933 | # endif | ||
| 2934 | if (!command->argv /* if it's the first word... */ | ||
| 2935 | # if ENABLE_HUSH_LOOPS | ||
| 2936 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ | ||
| 2937 | && ctx->ctx_res_w != RES_IN | ||
| 2938 | # endif | ||
| 2939 | # if ENABLE_HUSH_CASE | ||
| 2940 | && ctx->ctx_res_w != RES_CASE | ||
| 2941 | # endif | ||
| 2942 | ) { | ||
| 2943 | debug_printf_parse("checking '%s' for reserved-ness\n", word->data); | ||
| 2944 | if (reserved_word(word, ctx)) { | ||
| 2945 | o_reset_to_empty_unquoted(word); | ||
| 2946 | debug_printf_parse("done_word return %d\n", | ||
| 2947 | (ctx->ctx_res_w == RES_SNTX)); | ||
| 2948 | return (ctx->ctx_res_w == RES_SNTX); | ||
| 2949 | } | ||
| 2950 | # ifdef CMD_SINGLEWORD_NOGLOB_COND | ||
| 2951 | if (strcmp(word->data, "export") == 0 | ||
| 2952 | # if ENABLE_HUSH_LOCAL | ||
| 2953 | || strcmp(word->data, "local") == 0 | ||
| 2954 | # endif | ||
| 2955 | ) { | ||
| 2956 | command->cmd_type = CMD_SINGLEWORD_NOGLOB_COND; | ||
| 2957 | } else | ||
| 2958 | # endif | ||
| 2959 | # if ENABLE_HUSH_BASH_COMPAT | ||
| 2960 | if (strcmp(word->data, "[[") == 0) { | ||
| 2961 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | ||
| 2962 | } | ||
| 2963 | /* fall through */ | ||
| 2964 | # endif | ||
| 2965 | } | ||
| 2966 | #endif | ||
| 2967 | if (command->group) { | ||
| 2968 | /* "{ echo foo; } echo bar" - bad */ | ||
| 2969 | syntax_error_at(word->data); | ||
| 2970 | debug_printf_parse("done_word return 1: syntax error, " | ||
| 2971 | "groups and arglists don't mix\n"); | ||
| 2972 | return 1; | ||
| 2973 | } | ||
| 2974 | if (word->has_quoted_part | ||
| 2975 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ | ||
| 2976 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) | ||
| 2977 | /* (otherwise it's known to be not empty and is already safe) */ | ||
| 2978 | ) { | ||
| 2979 | /* exclude "$@" - it can expand to no word despite "" */ | ||
| 2980 | char *p = word->data; | ||
| 2981 | while (p[0] == SPECIAL_VAR_SYMBOL | ||
| 2982 | && (p[1] & 0x7f) == '@' | ||
| 2983 | && p[2] == SPECIAL_VAR_SYMBOL | ||
| 2984 | ) { | ||
| 2985 | p += 3; | ||
| 2986 | } | ||
| 2987 | if (p == word->data || p[0] != '\0') { | ||
| 2988 | /* saw no "$@", or not only "$@" but some | ||
| 2989 | * real text is there too */ | ||
| 2990 | /* insert "empty variable" reference, this makes | ||
| 2991 | * e.g. "", $empty"" etc to not disappear */ | ||
| 2992 | o_addchr(word, SPECIAL_VAR_SYMBOL); | ||
| 2993 | o_addchr(word, SPECIAL_VAR_SYMBOL); | ||
| 2994 | } | ||
| 2995 | } | ||
| 2996 | command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); | ||
| 2997 | debug_print_strings("word appended to argv", command->argv); | ||
| 2998 | } | ||
| 2999 | |||
| 3000 | #if ENABLE_HUSH_LOOPS | ||
| 3001 | if (ctx->ctx_res_w == RES_FOR) { | ||
| 3002 | if (word->has_quoted_part | ||
| 3003 | || !is_well_formed_var_name(command->argv[0], '\0') | ||
| 3004 | ) { | ||
| 3005 | /* bash says just "not a valid identifier" */ | ||
| 3006 | syntax_error("not a valid identifier in for"); | ||
| 3007 | return 1; | ||
| 3008 | } | ||
| 3009 | /* Force FOR to have just one word (variable name) */ | ||
| 3010 | /* NB: basically, this makes hush see "for v in ..." | ||
| 3011 | * syntax as if it is "for v; in ...". FOR and IN become | ||
| 3012 | * two pipe structs in parse tree. */ | ||
| 3013 | done_pipe(ctx, PIPE_SEQ); | ||
| 3014 | } | ||
| 3015 | #endif | ||
| 3016 | #if ENABLE_HUSH_CASE | ||
| 3017 | /* Force CASE to have just one word */ | ||
| 3018 | if (ctx->ctx_res_w == RES_CASE) { | ||
| 3019 | done_pipe(ctx, PIPE_SEQ); | ||
| 3020 | } | ||
| 3021 | #endif | ||
| 3022 | |||
| 3023 | o_reset_to_empty_unquoted(word); | ||
| 3024 | |||
| 3025 | debug_printf_parse("done_word return 0\n"); | ||
| 3026 | return 0; | ||
| 3027 | } | ||
| 3028 | |||
| 3029 | |||
| 3030 | /* Peek ahead in the input to find out if we have a "&n" construct, | ||
| 3031 | * as in "2>&1", that represents duplicating a file descriptor. | ||
| 3032 | * Return: | ||
| 3033 | * REDIRFD_CLOSE if >&- "close fd" construct is seen, | ||
| 3034 | * REDIRFD_SYNTAX_ERR if syntax error, | ||
| 3035 | * REDIRFD_TO_FILE if no & was seen, | ||
| 3036 | * or the number found. | ||
| 3037 | */ | ||
| 3038 | #if BB_MMU | ||
| 3039 | #define parse_redir_right_fd(as_string, input) \ | ||
| 3040 | parse_redir_right_fd(input) | ||
| 3041 | #endif | ||
| 3042 | static int parse_redir_right_fd(o_string *as_string, struct in_str *input) | ||
| 3043 | { | ||
| 3044 | int ch, d, ok; | ||
| 3045 | |||
| 3046 | ch = i_peek(input); | ||
| 3047 | if (ch != '&') | ||
| 3048 | return REDIRFD_TO_FILE; | ||
| 3049 | |||
| 3050 | ch = i_getch(input); /* get the & */ | ||
| 3051 | nommu_addchr(as_string, ch); | ||
| 3052 | ch = i_peek(input); | ||
| 3053 | if (ch == '-') { | ||
| 3054 | ch = i_getch(input); | ||
| 3055 | nommu_addchr(as_string, ch); | ||
| 3056 | return REDIRFD_CLOSE; | ||
| 3057 | } | ||
| 3058 | d = 0; | ||
| 3059 | ok = 0; | ||
| 3060 | while (ch != EOF && isdigit(ch)) { | ||
| 3061 | d = d*10 + (ch-'0'); | ||
| 3062 | ok = 1; | ||
| 3063 | ch = i_getch(input); | ||
| 3064 | nommu_addchr(as_string, ch); | ||
| 3065 | ch = i_peek(input); | ||
| 3066 | } | ||
| 3067 | if (ok) return d; | ||
| 3068 | |||
| 3069 | //TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2) | ||
| 3070 | |||
| 3071 | bb_error_msg("ambiguous redirect"); | ||
| 3072 | return REDIRFD_SYNTAX_ERR; | ||
| 3073 | } | ||
| 3074 | |||
| 3075 | /* Return code is 0 normal, 1 if a syntax error is detected | ||
| 3076 | */ | ||
| 3077 | static int parse_redirect(struct parse_context *ctx, | ||
| 3078 | int fd, | ||
| 3079 | redir_type style, | ||
| 3080 | struct in_str *input) | ||
| 3081 | { | ||
| 3082 | struct command *command = ctx->command; | ||
| 3083 | struct redir_struct *redir; | ||
| 3084 | struct redir_struct **redirp; | ||
| 3085 | int dup_num; | ||
| 3086 | |||
| 3087 | dup_num = REDIRFD_TO_FILE; | ||
| 3088 | if (style != REDIRECT_HEREDOC) { | ||
| 3089 | /* Check for a '>&1' type redirect */ | ||
| 3090 | dup_num = parse_redir_right_fd(&ctx->as_string, input); | ||
| 3091 | if (dup_num == REDIRFD_SYNTAX_ERR) | ||
| 3092 | return 1; | ||
| 3093 | } else { | ||
| 3094 | int ch = i_peek(input); | ||
| 3095 | dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ | ||
| 3096 | if (dup_num) { /* <<-... */ | ||
| 3097 | ch = i_getch(input); | ||
| 3098 | nommu_addchr(&ctx->as_string, ch); | ||
| 3099 | ch = i_peek(input); | ||
| 3100 | } | ||
| 3101 | } | ||
| 3102 | |||
| 3103 | if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) { | ||
| 3104 | int ch = i_peek(input); | ||
| 3105 | if (ch == '|') { | ||
| 3106 | /* >|FILE redirect ("clobbering" >). | ||
| 3107 | * Since we do not support "set -o noclobber" yet, | ||
| 3108 | * >| and > are the same for now. Just eat |. | ||
| 3109 | */ | ||
| 3110 | ch = i_getch(input); | ||
| 3111 | nommu_addchr(&ctx->as_string, ch); | ||
| 3112 | } | ||
| 3113 | } | ||
| 3114 | |||
| 3115 | /* Create a new redir_struct and append it to the linked list */ | ||
| 3116 | redirp = &command->redirects; | ||
| 3117 | while ((redir = *redirp) != NULL) { | ||
| 3118 | redirp = &(redir->next); | ||
| 3119 | } | ||
| 3120 | *redirp = redir = xzalloc(sizeof(*redir)); | ||
| 3121 | /* redir->next = NULL; */ | ||
| 3122 | /* redir->rd_filename = NULL; */ | ||
| 3123 | redir->rd_type = style; | ||
| 3124 | redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd; | ||
| 3125 | |||
| 3126 | debug_printf_parse("redirect type %d %s\n", redir->rd_fd, | ||
| 3127 | redir_table[style].descrip); | ||
| 3128 | |||
| 3129 | redir->rd_dup = dup_num; | ||
| 3130 | if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) { | ||
| 3131 | /* Erik had a check here that the file descriptor in question | ||
| 3132 | * is legit; I postpone that to "run time" | ||
| 3133 | * A "-" representation of "close me" shows up as a -3 here */ | ||
| 3134 | debug_printf_parse("duplicating redirect '%d>&%d'\n", | ||
| 3135 | redir->rd_fd, redir->rd_dup); | ||
| 3136 | } else { | ||
| 3137 | /* Set ctx->pending_redirect, so we know what to do at the | ||
| 3138 | * end of the next parsed word. */ | ||
| 3139 | ctx->pending_redirect = redir; | ||
| 3140 | } | ||
| 3141 | return 0; | ||
| 3142 | } | ||
| 3143 | |||
| 3144 | /* If a redirect is immediately preceded by a number, that number is | ||
| 3145 | * supposed to tell which file descriptor to redirect. This routine | ||
| 3146 | * looks for such preceding numbers. In an ideal world this routine | ||
| 3147 | * needs to handle all the following classes of redirects... | ||
| 3148 | * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo | ||
| 3149 | * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo | ||
| 3150 | * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo | ||
| 3151 | * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo | ||
| 3152 | * | ||
| 3153 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html | ||
| 3154 | * "2.7 Redirection | ||
| 3155 | * ... If n is quoted, the number shall not be recognized as part of | ||
| 3156 | * the redirection expression. For example: | ||
| 3157 | * echo \2>a | ||
| 3158 | * writes the character 2 into file a" | ||
| 3159 | * We are getting it right by setting ->has_quoted_part on any \<char> | ||
| 3160 | * | ||
| 3161 | * A -1 return means no valid number was found, | ||
| 3162 | * the caller should use the appropriate default for this redirection. | ||
| 3163 | */ | ||
| 3164 | static int redirect_opt_num(o_string *o) | ||
| 3165 | { | ||
| 3166 | int num; | ||
| 3167 | |||
| 3168 | if (o->data == NULL) | ||
| 3169 | return -1; | ||
| 3170 | num = bb_strtou(o->data, NULL, 10); | ||
| 3171 | if (errno || num < 0) | ||
| 3172 | return -1; | ||
| 3173 | o_reset_to_empty_unquoted(o); | ||
| 3174 | return num; | ||
| 3175 | } | ||
| 3176 | |||
| 3177 | #if BB_MMU | ||
| 3178 | #define fetch_till_str(as_string, input, word, skip_tabs) \ | ||
| 3179 | fetch_till_str(input, word, skip_tabs) | ||
| 3180 | #endif | ||
| 3181 | static char *fetch_till_str(o_string *as_string, | ||
| 3182 | struct in_str *input, | ||
| 3183 | const char *word, | ||
| 3184 | int skip_tabs) | ||
| 3185 | { | ||
| 3186 | o_string heredoc = NULL_O_STRING; | ||
| 3187 | int past_EOL = 0; | ||
| 3188 | int ch; | ||
| 3189 | |||
| 3190 | goto jump_in; | ||
| 3191 | while (1) { | ||
| 3192 | ch = i_getch(input); | ||
| 3193 | nommu_addchr(as_string, ch); | ||
| 3194 | if (ch == '\n') { | ||
| 3195 | if (strcmp(heredoc.data + past_EOL, word) == 0) { | ||
| 3196 | heredoc.data[past_EOL] = '\0'; | ||
| 3197 | debug_printf_parse("parsed heredoc '%s'\n", heredoc.data); | ||
| 3198 | return heredoc.data; | ||
| 3199 | } | ||
| 3200 | do { | ||
| 3201 | o_addchr(&heredoc, ch); | ||
| 3202 | past_EOL = heredoc.length; | ||
| 3203 | jump_in: | ||
| 3204 | do { | ||
| 3205 | ch = i_getch(input); | ||
| 3206 | nommu_addchr(as_string, ch); | ||
| 3207 | } while (skip_tabs && ch == '\t'); | ||
| 3208 | } while (ch == '\n'); | ||
| 3209 | } | ||
| 3210 | if (ch == EOF) { | ||
| 3211 | o_free_unsafe(&heredoc); | ||
| 3212 | return NULL; | ||
| 3213 | } | ||
| 3214 | o_addchr(&heredoc, ch); | ||
| 3215 | nommu_addchr(as_string, ch); | ||
| 3216 | } | ||
| 3217 | } | ||
| 3218 | |||
| 3219 | /* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs | ||
| 3220 | * and load them all. There should be exactly heredoc_cnt of them. | ||
| 3221 | */ | ||
| 3222 | static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input) | ||
| 3223 | { | ||
| 3224 | struct pipe *pi = ctx->list_head; | ||
| 3225 | |||
| 3226 | while (pi && heredoc_cnt) { | ||
| 3227 | int i; | ||
| 3228 | struct command *cmd = pi->cmds; | ||
| 3229 | |||
| 3230 | debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n", | ||
| 3231 | pi->num_cmds, | ||
| 3232 | cmd->argv ? cmd->argv[0] : "NONE"); | ||
| 3233 | for (i = 0; i < pi->num_cmds; i++) { | ||
| 3234 | struct redir_struct *redir = cmd->redirects; | ||
| 3235 | |||
| 3236 | debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n", | ||
| 3237 | i, cmd->argv ? cmd->argv[0] : "NONE"); | ||
| 3238 | while (redir) { | ||
| 3239 | if (redir->rd_type == REDIRECT_HEREDOC) { | ||
| 3240 | char *p; | ||
| 3241 | |||
| 3242 | redir->rd_type = REDIRECT_HEREDOC2; | ||
| 3243 | /* redir->rd_dup is (ab)used to indicate <<- */ | ||
| 3244 | p = fetch_till_str(&ctx->as_string, input, | ||
| 3245 | redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS); | ||
| 3246 | if (!p) { | ||
| 3247 | syntax_error("unexpected EOF in here document"); | ||
| 3248 | return 1; | ||
| 3249 | } | ||
| 3250 | free(redir->rd_filename); | ||
| 3251 | redir->rd_filename = p; | ||
| 3252 | heredoc_cnt--; | ||
| 3253 | } | ||
| 3254 | redir = redir->next; | ||
| 3255 | } | ||
| 3256 | cmd++; | ||
| 3257 | } | ||
| 3258 | pi = pi->next; | ||
| 3259 | } | ||
| 3260 | #if 0 | ||
| 3261 | /* Should be 0. If it isn't, it's a parse error */ | ||
| 3262 | if (heredoc_cnt) | ||
| 3263 | bb_error_msg_and_die("heredoc BUG 2"); | ||
| 3264 | #endif | ||
| 3265 | return 0; | ||
| 3266 | } | ||
| 3267 | |||
| 3268 | |||
| 3269 | static int run_list(struct pipe *pi); | ||
| 3270 | #if BB_MMU | ||
| 3271 | #define parse_stream(pstring, input, end_trigger) \ | ||
| 3272 | parse_stream(input, end_trigger) | ||
| 3273 | #endif | ||
| 3274 | static struct pipe *parse_stream(char **pstring, | ||
| 3275 | struct in_str *input, | ||
| 3276 | int end_trigger); | ||
| 3277 | |||
| 3278 | |||
| 3279 | #if !ENABLE_HUSH_FUNCTIONS | ||
| 3280 | #define parse_group(dest, ctx, input, ch) \ | ||
| 3281 | parse_group(ctx, input, ch) | ||
| 3282 | #endif | ||
| 3283 | static int parse_group(o_string *dest, struct parse_context *ctx, | ||
| 3284 | struct in_str *input, int ch) | ||
| 3285 | { | ||
| 3286 | /* dest contains characters seen prior to ( or {. | ||
| 3287 | * Typically it's empty, but for function defs, | ||
| 3288 | * it contains function name (without '()'). */ | ||
| 3289 | struct pipe *pipe_list; | ||
| 3290 | int endch; | ||
| 3291 | struct command *command = ctx->command; | ||
| 3292 | |||
| 3293 | debug_printf_parse("parse_group entered\n"); | ||
| 3294 | #if ENABLE_HUSH_FUNCTIONS | ||
| 3295 | if (ch == '(' && !dest->has_quoted_part) { | ||
| 3296 | if (dest->length) | ||
| 3297 | if (done_word(dest, ctx)) | ||
| 3298 | return 1; | ||
| 3299 | if (!command->argv) | ||
| 3300 | goto skip; /* (... */ | ||
| 3301 | if (command->argv[1]) { /* word word ... (... */ | ||
| 3302 | syntax_error_unexpected_ch('('); | ||
| 3303 | return 1; | ||
| 3304 | } | ||
| 3305 | /* it is "word(..." or "word (..." */ | ||
| 3306 | do | ||
| 3307 | ch = i_getch(input); | ||
| 3308 | while (ch == ' ' || ch == '\t'); | ||
| 3309 | if (ch != ')') { | ||
| 3310 | syntax_error_unexpected_ch(ch); | ||
| 3311 | return 1; | ||
| 3312 | } | ||
| 3313 | nommu_addchr(&ctx->as_string, ch); | ||
| 3314 | do | ||
| 3315 | ch = i_getch(input); | ||
| 3316 | while (ch == ' ' || ch == '\t' || ch == '\n'); | ||
| 3317 | if (ch != '{') { | ||
| 3318 | syntax_error_unexpected_ch(ch); | ||
| 3319 | return 1; | ||
| 3320 | } | ||
| 3321 | nommu_addchr(&ctx->as_string, ch); | ||
| 3322 | command->cmd_type = CMD_FUNCDEF; | ||
| 3323 | goto skip; | ||
| 3324 | } | ||
| 3325 | #endif | ||
| 3326 | |||
| 3327 | #if 0 /* Prevented by caller */ | ||
| 3328 | if (command->argv /* word [word]{... */ | ||
| 3329 | || dest->length /* word{... */ | ||
| 3330 | || dest->has_quoted_part /* ""{... */ | ||
| 3331 | ) { | ||
| 3332 | syntax_error(NULL); | ||
| 3333 | debug_printf_parse("parse_group return 1: " | ||
| 3334 | "syntax error, groups and arglists don't mix\n"); | ||
| 3335 | return 1; | ||
| 3336 | } | ||
| 3337 | #endif | ||
| 3338 | |||
| 3339 | #if ENABLE_HUSH_FUNCTIONS | ||
| 3340 | skip: | ||
| 3341 | #endif | ||
| 3342 | endch = '}'; | ||
| 3343 | if (ch == '(') { | ||
| 3344 | endch = ')'; | ||
| 3345 | command->cmd_type = CMD_SUBSHELL; | ||
| 3346 | } else { | ||
| 3347 | /* bash does not allow "{echo...", requires whitespace */ | ||
| 3348 | ch = i_getch(input); | ||
| 3349 | if (ch != ' ' && ch != '\t' && ch != '\n') { | ||
| 3350 | syntax_error_unexpected_ch(ch); | ||
| 3351 | return 1; | ||
| 3352 | } | ||
| 3353 | nommu_addchr(&ctx->as_string, ch); | ||
| 3354 | } | ||
| 3355 | |||
| 3356 | { | ||
| 3357 | #if BB_MMU | ||
| 3358 | # define as_string NULL | ||
| 3359 | #else | ||
| 3360 | char *as_string = NULL; | ||
| 3361 | #endif | ||
| 3362 | pipe_list = parse_stream(&as_string, input, endch); | ||
| 3363 | #if !BB_MMU | ||
| 3364 | if (as_string) | ||
| 3365 | o_addstr(&ctx->as_string, as_string); | ||
| 3366 | #endif | ||
| 3367 | /* empty ()/{} or parse error? */ | ||
| 3368 | if (!pipe_list || pipe_list == ERR_PTR) { | ||
| 3369 | /* parse_stream already emitted error msg */ | ||
| 3370 | if (!BB_MMU) | ||
| 3371 | free(as_string); | ||
| 3372 | debug_printf_parse("parse_group return 1: " | ||
| 3373 | "parse_stream returned %p\n", pipe_list); | ||
| 3374 | return 1; | ||
| 3375 | } | ||
| 3376 | command->group = pipe_list; | ||
| 3377 | #if !BB_MMU | ||
| 3378 | as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */ | ||
| 3379 | command->group_as_string = as_string; | ||
| 3380 | debug_printf_parse("end of group, remembering as:'%s'\n", | ||
| 3381 | command->group_as_string); | ||
| 3382 | #endif | ||
| 3383 | #undef as_string | ||
| 3384 | } | ||
| 3385 | debug_printf_parse("parse_group return 0\n"); | ||
| 3386 | return 0; | ||
| 3387 | /* command remains "open", available for possible redirects */ | ||
| 3388 | } | ||
| 3389 | |||
| 3390 | #if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS | ||
| 3391 | /* Subroutines for copying $(...) and `...` things */ | ||
| 3392 | static void add_till_backquote(o_string *dest, struct in_str *input); | ||
| 3393 | /* '...' */ | ||
| 3394 | static void add_till_single_quote(o_string *dest, struct in_str *input) | ||
| 3395 | { | ||
| 3396 | while (1) { | ||
| 3397 | int ch = i_getch(input); | ||
| 3398 | if (ch == EOF) { | ||
| 3399 | syntax_error_unterm_ch('\''); | ||
| 3400 | /*xfunc_die(); - redundant */ | ||
| 3401 | } | ||
| 3402 | if (ch == '\'') | ||
| 3403 | return; | ||
| 3404 | o_addchr(dest, ch); | ||
| 3405 | } | ||
| 3406 | } | ||
| 3407 | /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ | ||
| 3408 | static void add_till_double_quote(o_string *dest, struct in_str *input) | ||
| 3409 | { | ||
| 3410 | while (1) { | ||
| 3411 | int ch = i_getch(input); | ||
| 3412 | if (ch == EOF) { | ||
| 3413 | syntax_error_unterm_ch('"'); | ||
| 3414 | /*xfunc_die(); - redundant */ | ||
| 3415 | } | ||
| 3416 | if (ch == '"') | ||
| 3417 | return; | ||
| 3418 | if (ch == '\\') { /* \x. Copy both chars. */ | ||
| 3419 | o_addchr(dest, ch); | ||
| 3420 | ch = i_getch(input); | ||
| 3421 | } | ||
| 3422 | o_addchr(dest, ch); | ||
| 3423 | if (ch == '`') { | ||
| 3424 | add_till_backquote(dest, input); | ||
| 3425 | o_addchr(dest, ch); | ||
| 3426 | continue; | ||
| 3427 | } | ||
| 3428 | //if (ch == '$') ... | ||
| 3429 | } | ||
| 3430 | } | ||
| 3431 | /* Process `cmd` - copy contents until "`" is seen. Complicated by | ||
| 3432 | * \` quoting. | ||
| 3433 | * "Within the backquoted style of command substitution, backslash | ||
| 3434 | * shall retain its literal meaning, except when followed by: '$', '`', or '\'. | ||
| 3435 | * The search for the matching backquote shall be satisfied by the first | ||
| 3436 | * backquote found without a preceding backslash; during this search, | ||
| 3437 | * if a non-escaped backquote is encountered within a shell comment, | ||
| 3438 | * a here-document, an embedded command substitution of the $(command) | ||
| 3439 | * form, or a quoted string, undefined results occur. A single-quoted | ||
| 3440 | * or double-quoted string that begins, but does not end, within the | ||
| 3441 | * "`...`" sequence produces undefined results." | ||
| 3442 | * Example Output | ||
| 3443 | * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST | ||
| 3444 | */ | ||
| 3445 | static void add_till_backquote(o_string *dest, struct in_str *input) | ||
| 3446 | { | ||
| 3447 | while (1) { | ||
| 3448 | int ch = i_getch(input); | ||
| 3449 | if (ch == EOF) { | ||
| 3450 | syntax_error_unterm_ch('`'); | ||
| 3451 | /*xfunc_die(); - redundant */ | ||
| 3452 | } | ||
| 3453 | if (ch == '`') | ||
| 3454 | return; | ||
| 3455 | if (ch == '\\') { | ||
| 3456 | /* \x. Copy both chars unless it is \` */ | ||
| 3457 | int ch2 = i_getch(input); | ||
| 3458 | if (ch2 == EOF) { | ||
| 3459 | syntax_error_unterm_ch('`'); | ||
| 3460 | /*xfunc_die(); - redundant */ | ||
| 3461 | } | ||
| 3462 | if (ch2 != '`' && ch2 != '$' && ch2 != '\\') | ||
| 3463 | o_addchr(dest, ch); | ||
| 3464 | ch = ch2; | ||
| 3465 | } | ||
| 3466 | o_addchr(dest, ch); | ||
| 3467 | } | ||
| 3468 | } | ||
| 3469 | /* Process $(cmd) - copy contents until ")" is seen. Complicated by | ||
| 3470 | * quoting and nested ()s. | ||
| 3471 | * "With the $(command) style of command substitution, all characters | ||
| 3472 | * following the open parenthesis to the matching closing parenthesis | ||
| 3473 | * constitute the command. Any valid shell script can be used for command, | ||
| 3474 | * except a script consisting solely of redirections which produces | ||
| 3475 | * unspecified results." | ||
| 3476 | * Example Output | ||
| 3477 | * echo $(echo '(TEST)' BEST) (TEST) BEST | ||
| 3478 | * echo $(echo 'TEST)' BEST) TEST) BEST | ||
| 3479 | * echo $(echo \(\(TEST\) BEST) ((TEST) BEST | ||
| 3480 | * | ||
| 3481 | * Also adapted to eat ${var%...} and $((...)) constructs, since ... part | ||
| 3482 | * can contain arbitrary constructs, just like $(cmd). | ||
| 3483 | * In bash compat mode, it needs to also be able to stop on ':' or '/' | ||
| 3484 | * for ${var:N[:M]} and ${var/P[/R]} parsing. | ||
| 3485 | */ | ||
| 3486 | #define DOUBLE_CLOSE_CHAR_FLAG 0x80 | ||
| 3487 | static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch) | ||
| 3488 | { | ||
| 3489 | int ch; | ||
| 3490 | char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; | ||
| 3491 | # if ENABLE_HUSH_BASH_COMPAT | ||
| 3492 | char end_char2 = end_ch >> 8; | ||
| 3493 | # endif | ||
| 3494 | end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); | ||
| 3495 | |||
| 3496 | while (1) { | ||
| 3497 | ch = i_getch(input); | ||
| 3498 | if (ch == EOF) { | ||
| 3499 | syntax_error_unterm_ch(end_ch); | ||
| 3500 | /*xfunc_die(); - redundant */ | ||
| 3501 | } | ||
| 3502 | if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { | ||
| 3503 | if (!dbl) | ||
| 3504 | break; | ||
| 3505 | /* we look for closing )) of $((EXPR)) */ | ||
| 3506 | if (i_peek(input) == end_ch) { | ||
| 3507 | i_getch(input); /* eat second ')' */ | ||
| 3508 | break; | ||
| 3509 | } | ||
| 3510 | } | ||
| 3511 | o_addchr(dest, ch); | ||
| 3512 | if (ch == '(' || ch == '{') { | ||
| 3513 | ch = (ch == '(' ? ')' : '}'); | ||
| 3514 | add_till_closing_bracket(dest, input, ch); | ||
| 3515 | o_addchr(dest, ch); | ||
| 3516 | continue; | ||
| 3517 | } | ||
| 3518 | if (ch == '\'') { | ||
| 3519 | add_till_single_quote(dest, input); | ||
| 3520 | o_addchr(dest, ch); | ||
| 3521 | continue; | ||
| 3522 | } | ||
| 3523 | if (ch == '"') { | ||
| 3524 | add_till_double_quote(dest, input); | ||
| 3525 | o_addchr(dest, ch); | ||
| 3526 | continue; | ||
| 3527 | } | ||
| 3528 | if (ch == '`') { | ||
| 3529 | add_till_backquote(dest, input); | ||
| 3530 | o_addchr(dest, ch); | ||
| 3531 | continue; | ||
| 3532 | } | ||
| 3533 | if (ch == '\\') { | ||
| 3534 | /* \x. Copy verbatim. Important for \(, \) */ | ||
| 3535 | ch = i_getch(input); | ||
| 3536 | if (ch == EOF) { | ||
| 3537 | syntax_error_unterm_ch(')'); | ||
| 3538 | /*xfunc_die(); - redundant */ | ||
| 3539 | } | ||
| 3540 | o_addchr(dest, ch); | ||
| 3541 | continue; | ||
| 3542 | } | ||
| 3543 | } | ||
| 3544 | return ch; | ||
| 3545 | } | ||
| 3546 | #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */ | ||
| 3547 | |||
| 3548 | /* Return code: 0 for OK, 1 for syntax error */ | ||
| 3549 | #if BB_MMU | ||
| 3550 | #define parse_dollar(as_string, dest, input) \ | ||
| 3551 | parse_dollar(dest, input) | ||
| 3552 | #define as_string NULL | ||
| 3553 | #endif | ||
| 3554 | static int parse_dollar(o_string *as_string, | ||
| 3555 | o_string *dest, | ||
| 3556 | struct in_str *input) | ||
| 3557 | { | ||
| 3558 | int ch = i_peek(input); /* first character after the $ */ | ||
| 3559 | unsigned char quote_mask = dest->o_escape ? 0x80 : 0; | ||
| 3560 | |||
| 3561 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); | ||
| 3562 | if (isalpha(ch)) { | ||
| 3563 | ch = i_getch(input); | ||
| 3564 | nommu_addchr(as_string, ch); | ||
| 3565 | make_var: | ||
| 3566 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3567 | while (1) { | ||
| 3568 | debug_printf_parse(": '%c'\n", ch); | ||
| 3569 | o_addchr(dest, ch | quote_mask); | ||
| 3570 | quote_mask = 0; | ||
| 3571 | ch = i_peek(input); | ||
| 3572 | if (!isalnum(ch) && ch != '_') | ||
| 3573 | break; | ||
| 3574 | ch = i_getch(input); | ||
| 3575 | nommu_addchr(as_string, ch); | ||
| 3576 | } | ||
| 3577 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3578 | } else if (isdigit(ch)) { | ||
| 3579 | make_one_char_var: | ||
| 3580 | ch = i_getch(input); | ||
| 3581 | nommu_addchr(as_string, ch); | ||
| 3582 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3583 | debug_printf_parse(": '%c'\n", ch); | ||
| 3584 | o_addchr(dest, ch | quote_mask); | ||
| 3585 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3586 | } else switch (ch) { | ||
| 3587 | case '$': /* pid */ | ||
| 3588 | case '!': /* last bg pid */ | ||
| 3589 | case '?': /* last exit code */ | ||
| 3590 | case '#': /* number of args */ | ||
| 3591 | case '*': /* args */ | ||
| 3592 | case '@': /* args */ | ||
| 3593 | goto make_one_char_var; | ||
| 3594 | case '{': { | ||
| 3595 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3596 | |||
| 3597 | ch = i_getch(input); /* eat '{' */ | ||
| 3598 | nommu_addchr(as_string, ch); | ||
| 3599 | |||
| 3600 | ch = i_getch(input); /* first char after '{' */ | ||
| 3601 | nommu_addchr(as_string, ch); | ||
| 3602 | /* It should be ${?}, or ${#var}, | ||
| 3603 | * or even ${?+subst} - operator acting on a special variable, | ||
| 3604 | * or the beginning of variable name. | ||
| 3605 | */ | ||
| 3606 | if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */ | ||
| 3607 | bad_dollar_syntax: | ||
| 3608 | syntax_error_unterm_str("${name}"); | ||
| 3609 | debug_printf_parse("parse_dollar return 1: unterminated ${name}\n"); | ||
| 3610 | return 1; | ||
| 3611 | } | ||
| 3612 | ch |= quote_mask; | ||
| 3613 | |||
| 3614 | /* It's possible to just call add_till_closing_bracket() at this point. | ||
| 3615 | * However, this regresses some of our testsuite cases | ||
| 3616 | * which check invalid constructs like ${%}. | ||
| 3617 | * Oh well... let's check that the var name part is fine... */ | ||
| 3618 | |||
| 3619 | while (1) { | ||
| 3620 | unsigned pos; | ||
| 3621 | |||
| 3622 | o_addchr(dest, ch); | ||
| 3623 | debug_printf_parse(": '%c'\n", ch); | ||
| 3624 | |||
| 3625 | ch = i_getch(input); | ||
| 3626 | nommu_addchr(as_string, ch); | ||
| 3627 | if (ch == '}') | ||
| 3628 | break; | ||
| 3629 | |||
| 3630 | if (!isalnum(ch) && ch != '_') { | ||
| 3631 | unsigned end_ch; | ||
| 3632 | unsigned char last_ch; | ||
| 3633 | /* handle parameter expansions | ||
| 3634 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 | ||
| 3635 | */ | ||
| 3636 | if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */ | ||
| 3637 | goto bad_dollar_syntax; | ||
| 3638 | |||
| 3639 | /* Eat everything until closing '}' (or ':') */ | ||
| 3640 | end_ch = '}'; | ||
| 3641 | if (ENABLE_HUSH_BASH_COMPAT | ||
| 3642 | && ch == ':' | ||
| 3643 | && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input)) | ||
| 3644 | ) { | ||
| 3645 | /* It's ${var:N[:M]} thing */ | ||
| 3646 | end_ch = '}' * 0x100 + ':'; | ||
| 3647 | } | ||
| 3648 | if (ENABLE_HUSH_BASH_COMPAT | ||
| 3649 | && ch == '/' | ||
| 3650 | ) { | ||
| 3651 | /* It's ${var/[/]pattern[/repl]} thing */ | ||
| 3652 | if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */ | ||
| 3653 | i_getch(input); | ||
| 3654 | nommu_addchr(as_string, '/'); | ||
| 3655 | ch = '\\'; | ||
| 3656 | } | ||
| 3657 | end_ch = '}' * 0x100 + '/'; | ||
| 3658 | } | ||
| 3659 | o_addchr(dest, ch); | ||
| 3660 | again: | ||
| 3661 | if (!BB_MMU) | ||
| 3662 | pos = dest->length; | ||
| 3663 | #if ENABLE_HUSH_DOLLAR_OPS | ||
| 3664 | last_ch = add_till_closing_bracket(dest, input, end_ch); | ||
| 3665 | #else | ||
| 3666 | #error Simple code to only allow ${var} is not implemented | ||
| 3667 | #endif | ||
| 3668 | if (as_string) { | ||
| 3669 | o_addstr(as_string, dest->data + pos); | ||
| 3670 | o_addchr(as_string, last_ch); | ||
| 3671 | } | ||
| 3672 | |||
| 3673 | if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) { | ||
| 3674 | /* close the first block: */ | ||
| 3675 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3676 | /* while parsing N from ${var:N[:M]} | ||
| 3677 | * or pattern from ${var/[/]pattern[/repl]} */ | ||
| 3678 | if ((end_ch & 0xff) == last_ch) { | ||
| 3679 | /* got ':' or '/'- parse the rest */ | ||
| 3680 | end_ch = '}'; | ||
| 3681 | goto again; | ||
| 3682 | } | ||
| 3683 | /* got '}' */ | ||
| 3684 | if (end_ch == '}' * 0x100 + ':') { | ||
| 3685 | /* it's ${var:N} - emulate :999999999 */ | ||
| 3686 | o_addstr(dest, "999999999"); | ||
| 3687 | } /* else: it's ${var/[/]pattern} */ | ||
| 3688 | } | ||
| 3689 | break; | ||
| 3690 | } | ||
| 3691 | } | ||
| 3692 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3693 | break; | ||
| 3694 | } | ||
| 3695 | #if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK | ||
| 3696 | case '(': { | ||
| 3697 | unsigned pos; | ||
| 3698 | |||
| 3699 | ch = i_getch(input); | ||
| 3700 | nommu_addchr(as_string, ch); | ||
| 3701 | # if ENABLE_SH_MATH_SUPPORT | ||
| 3702 | if (i_peek(input) == '(') { | ||
| 3703 | ch = i_getch(input); | ||
| 3704 | nommu_addchr(as_string, ch); | ||
| 3705 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3706 | o_addchr(dest, /*quote_mask |*/ '+'); | ||
| 3707 | if (!BB_MMU) | ||
| 3708 | pos = dest->length; | ||
| 3709 | add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG); | ||
| 3710 | if (as_string) { | ||
| 3711 | o_addstr(as_string, dest->data + pos); | ||
| 3712 | o_addchr(as_string, ')'); | ||
| 3713 | o_addchr(as_string, ')'); | ||
| 3714 | } | ||
| 3715 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3716 | break; | ||
| 3717 | } | ||
| 3718 | # endif | ||
| 3719 | # if ENABLE_HUSH_TICK | ||
| 3720 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3721 | o_addchr(dest, quote_mask | '`'); | ||
| 3722 | if (!BB_MMU) | ||
| 3723 | pos = dest->length; | ||
| 3724 | add_till_closing_bracket(dest, input, ')'); | ||
| 3725 | if (as_string) { | ||
| 3726 | o_addstr(as_string, dest->data + pos); | ||
| 3727 | o_addchr(as_string, ')'); | ||
| 3728 | } | ||
| 3729 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3730 | # endif | ||
| 3731 | break; | ||
| 3732 | } | ||
| 3733 | #endif | ||
| 3734 | case '_': | ||
| 3735 | ch = i_getch(input); | ||
| 3736 | nommu_addchr(as_string, ch); | ||
| 3737 | ch = i_peek(input); | ||
| 3738 | if (isalnum(ch)) { /* it's $_name or $_123 */ | ||
| 3739 | ch = '_'; | ||
| 3740 | goto make_var; | ||
| 3741 | } | ||
| 3742 | /* else: it's $_ */ | ||
| 3743 | /* TODO: $_ and $-: */ | ||
| 3744 | /* $_ Shell or shell script name; or last argument of last command | ||
| 3745 | * (if last command wasn't a pipe; if it was, bash sets $_ to ""); | ||
| 3746 | * but in command's env, set to full pathname used to invoke it */ | ||
| 3747 | /* $- Option flags set by set builtin or shell options (-i etc) */ | ||
| 3748 | default: | ||
| 3749 | o_addQchr(dest, '$'); | ||
| 3750 | } | ||
| 3751 | debug_printf_parse("parse_dollar return 0\n"); | ||
| 3752 | return 0; | ||
| 3753 | #undef as_string | ||
| 3754 | } | ||
| 3755 | |||
| 2524 | #if BB_MMU | 3756 | #if BB_MMU |
| 2525 | #define parse_stream_dquoted(as_string, dest, input, dquote_end) \ | 3757 | #define parse_stream_dquoted(as_string, dest, input, dquote_end) \ |
| 2526 | parse_stream_dquoted(dest, input, dquote_end) | 3758 | parse_stream_dquoted(dest, input, dquote_end) |
| 3759 | #define as_string NULL | ||
| 2527 | #endif | 3760 | #endif |
| 2528 | static int parse_stream_dquoted(o_string *as_string, | 3761 | static int parse_stream_dquoted(o_string *as_string, |
| 2529 | o_string *dest, | 3762 | o_string *dest, |
| 2530 | struct in_str *input, | 3763 | struct in_str *input, |
| 2531 | int dquote_end); | 3764 | int dquote_end) |
| 3765 | { | ||
| 3766 | int ch; | ||
| 3767 | int next; | ||
| 3768 | |||
| 3769 | again: | ||
| 3770 | ch = i_getch(input); | ||
| 3771 | if (ch != EOF) | ||
| 3772 | nommu_addchr(as_string, ch); | ||
| 3773 | if (ch == dquote_end) { /* may be only '"' or EOF */ | ||
| 3774 | if (dest->o_assignment == NOT_ASSIGNMENT) | ||
| 3775 | dest->o_escape ^= 1; | ||
| 3776 | debug_printf_parse("parse_stream_dquoted return 0\n"); | ||
| 3777 | return 0; | ||
| 3778 | } | ||
| 3779 | /* note: can't move it above ch == dquote_end check! */ | ||
| 3780 | if (ch == EOF) { | ||
| 3781 | syntax_error_unterm_ch('"'); | ||
| 3782 | /*xfunc_die(); - redundant */ | ||
| 3783 | } | ||
| 3784 | next = '\0'; | ||
| 3785 | if (ch != '\n') { | ||
| 3786 | next = i_peek(input); | ||
| 3787 | } | ||
| 3788 | debug_printf_parse("\" ch=%c (%d) escape=%d\n", | ||
| 3789 | ch, ch, dest->o_escape); | ||
| 3790 | if (ch == '\\') { | ||
| 3791 | if (next == EOF) { | ||
| 3792 | syntax_error("\\<eof>"); | ||
| 3793 | xfunc_die(); | ||
| 3794 | } | ||
| 3795 | /* bash: | ||
| 3796 | * "The backslash retains its special meaning [in "..."] | ||
| 3797 | * only when followed by one of the following characters: | ||
| 3798 | * $, `, ", \, or <newline>. A double quote may be quoted | ||
| 3799 | * within double quotes by preceding it with a backslash." | ||
| 3800 | */ | ||
| 3801 | if (strchr("$`\"\\\n", next) != NULL) { | ||
| 3802 | ch = i_getch(input); | ||
| 3803 | if (ch != '\n') { | ||
| 3804 | o_addqchr(dest, ch); | ||
| 3805 | nommu_addchr(as_string, ch); | ||
| 3806 | } | ||
| 3807 | } else { | ||
| 3808 | o_addqchr(dest, '\\'); | ||
| 3809 | nommu_addchr(as_string, '\\'); | ||
| 3810 | } | ||
| 3811 | goto again; | ||
| 3812 | } | ||
| 3813 | if (ch == '$') { | ||
| 3814 | if (parse_dollar(as_string, dest, input) != 0) { | ||
| 3815 | debug_printf_parse("parse_stream_dquoted return 1: " | ||
| 3816 | "parse_dollar returned non-0\n"); | ||
| 3817 | return 1; | ||
| 3818 | } | ||
| 3819 | goto again; | ||
| 3820 | } | ||
| 3821 | #if ENABLE_HUSH_TICK | ||
| 3822 | if (ch == '`') { | ||
| 3823 | //unsigned pos = dest->length; | ||
| 3824 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3825 | o_addchr(dest, 0x80 | '`'); | ||
| 3826 | add_till_backquote(dest, input); | ||
| 3827 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 3828 | //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); | ||
| 3829 | goto again; | ||
| 3830 | } | ||
| 3831 | #endif | ||
| 3832 | o_addQchr(dest, ch); | ||
| 3833 | if (ch == '=' | ||
| 3834 | && (dest->o_assignment == MAYBE_ASSIGNMENT | ||
| 3835 | || dest->o_assignment == WORD_IS_KEYWORD) | ||
| 3836 | && is_well_formed_var_name(dest->data, '=') | ||
| 3837 | ) { | ||
| 3838 | dest->o_assignment = DEFINITELY_ASSIGNMENT; | ||
| 3839 | } | ||
| 3840 | goto again; | ||
| 3841 | #undef as_string | ||
| 3842 | } | ||
| 3843 | |||
| 3844 | /* | ||
| 3845 | * Scan input until EOF or end_trigger char. | ||
| 3846 | * Return a list of pipes to execute, or NULL on EOF | ||
| 3847 | * or if end_trigger character is met. | ||
| 3848 | * On syntax error, exit is shell is not interactive, | ||
| 3849 | * reset parsing machinery and start parsing anew, | ||
| 3850 | * or return ERR_PTR. | ||
| 3851 | */ | ||
| 3852 | static struct pipe *parse_stream(char **pstring, | ||
| 3853 | struct in_str *input, | ||
| 3854 | int end_trigger) | ||
| 3855 | { | ||
| 3856 | struct parse_context ctx; | ||
| 3857 | o_string dest = NULL_O_STRING; | ||
| 3858 | int is_in_dquote; | ||
| 3859 | int heredoc_cnt; | ||
| 3860 | |||
| 3861 | /* Double-quote state is handled in the state variable is_in_dquote. | ||
| 3862 | * A single-quote triggers a bypass of the main loop until its mate is | ||
| 3863 | * found. When recursing, quote state is passed in via dest->o_escape. | ||
| 3864 | */ | ||
| 3865 | debug_printf_parse("parse_stream entered, end_trigger='%c'\n", | ||
| 3866 | end_trigger ? end_trigger : 'X'); | ||
| 3867 | debug_enter(); | ||
| 3868 | |||
| 3869 | /* If very first arg is "" or '', dest.data may end up NULL. | ||
| 3870 | * Preventing this: */ | ||
| 3871 | o_addchr(&dest, '\0'); | ||
| 3872 | dest.length = 0; | ||
| 3873 | |||
| 3874 | G.ifs = get_local_var_value("IFS"); | ||
| 3875 | if (G.ifs == NULL) | ||
| 3876 | G.ifs = defifs; | ||
| 3877 | |||
| 3878 | reset: | ||
| 3879 | #if ENABLE_HUSH_INTERACTIVE | ||
| 3880 | input->promptmode = 0; /* PS1 */ | ||
| 3881 | #endif | ||
| 3882 | /* dest.o_assignment = MAYBE_ASSIGNMENT; - already is */ | ||
| 3883 | initialize_context(&ctx); | ||
| 3884 | is_in_dquote = 0; | ||
| 3885 | heredoc_cnt = 0; | ||
| 3886 | while (1) { | ||
| 3887 | const char *is_ifs; | ||
| 3888 | const char *is_special; | ||
| 3889 | int ch; | ||
| 3890 | int next; | ||
| 3891 | int redir_fd; | ||
| 3892 | redir_type redir_style; | ||
| 3893 | |||
| 3894 | if (is_in_dquote) { | ||
| 3895 | /* dest.has_quoted_part = 1; - already is (see below) */ | ||
| 3896 | if (parse_stream_dquoted(&ctx.as_string, &dest, input, '"')) { | ||
| 3897 | goto parse_error; | ||
| 3898 | } | ||
| 3899 | /* We reached closing '"' */ | ||
| 3900 | is_in_dquote = 0; | ||
| 3901 | } | ||
| 3902 | ch = i_getch(input); | ||
| 3903 | debug_printf_parse(": ch=%c (%d) escape=%d\n", | ||
| 3904 | ch, ch, dest.o_escape); | ||
| 3905 | if (ch == EOF) { | ||
| 3906 | struct pipe *pi; | ||
| 3907 | |||
| 3908 | if (heredoc_cnt) { | ||
| 3909 | syntax_error_unterm_str("here document"); | ||
| 3910 | goto parse_error; | ||
| 3911 | } | ||
| 3912 | /* end_trigger == '}' case errors out earlier, | ||
| 3913 | * checking only ')' */ | ||
| 3914 | if (end_trigger == ')') { | ||
| 3915 | syntax_error_unterm_ch('('); /* exits */ | ||
| 3916 | /* goto parse_error; */ | ||
| 3917 | } | ||
| 3918 | |||
| 3919 | if (done_word(&dest, &ctx)) { | ||
| 3920 | goto parse_error; | ||
| 3921 | } | ||
| 3922 | o_free(&dest); | ||
| 3923 | done_pipe(&ctx, PIPE_SEQ); | ||
| 3924 | pi = ctx.list_head; | ||
| 3925 | /* If we got nothing... */ | ||
| 3926 | /* (this makes bare "&" cmd a no-op. | ||
| 3927 | * bash says: "syntax error near unexpected token '&'") */ | ||
| 3928 | if (pi->num_cmds == 0 | ||
| 3929 | IF_HAS_KEYWORDS( && pi->res_word == RES_NONE) | ||
| 3930 | ) { | ||
| 3931 | free_pipe_list(pi); | ||
| 3932 | pi = NULL; | ||
| 3933 | } | ||
| 3934 | #if !BB_MMU | ||
| 3935 | debug_printf_parse("as_string '%s'\n", ctx.as_string.data); | ||
| 3936 | if (pstring) | ||
| 3937 | *pstring = ctx.as_string.data; | ||
| 3938 | else | ||
| 3939 | o_free_unsafe(&ctx.as_string); | ||
| 3940 | #endif | ||
| 3941 | debug_leave(); | ||
| 3942 | debug_printf_parse("parse_stream return %p\n", pi); | ||
| 3943 | return pi; | ||
| 3944 | } | ||
| 3945 | nommu_addchr(&ctx.as_string, ch); | ||
| 3946 | |||
| 3947 | next = '\0'; | ||
| 3948 | if (ch != '\n') | ||
| 3949 | next = i_peek(input); | ||
| 3950 | |||
| 3951 | is_special = "{}<>;&|()#'" /* special outside of "str" */ | ||
| 3952 | "\\$\"" IF_HUSH_TICK("`"); /* always special */ | ||
| 3953 | /* Are { and } special here? */ | ||
| 3954 | if (ctx.command->argv /* word [word]{... - non-special */ | ||
| 3955 | || dest.length /* word{... - non-special */ | ||
| 3956 | || dest.has_quoted_part /* ""{... - non-special */ | ||
| 3957 | || (next != ';' /* }; - special */ | ||
| 3958 | && next != ')' /* }) - special */ | ||
| 3959 | && next != '&' /* }& and }&& ... - special */ | ||
| 3960 | && next != '|' /* }|| ... - special */ | ||
| 3961 | && !strchr(G.ifs, next) /* {word - non-special */ | ||
| 3962 | ) | ||
| 3963 | ) { | ||
| 3964 | /* They are not special, skip "{}" */ | ||
| 3965 | is_special += 2; | ||
| 3966 | } | ||
| 3967 | is_special = strchr(is_special, ch); | ||
| 3968 | is_ifs = strchr(G.ifs, ch); | ||
| 3969 | |||
| 3970 | if (!is_special && !is_ifs) { /* ordinary char */ | ||
| 3971 | ordinary_char: | ||
| 3972 | o_addQchr(&dest, ch); | ||
| 3973 | if ((dest.o_assignment == MAYBE_ASSIGNMENT | ||
| 3974 | || dest.o_assignment == WORD_IS_KEYWORD) | ||
| 3975 | && ch == '=' | ||
| 3976 | && is_well_formed_var_name(dest.data, '=') | ||
| 3977 | ) { | ||
| 3978 | dest.o_assignment = DEFINITELY_ASSIGNMENT; | ||
| 3979 | } | ||
| 3980 | continue; | ||
| 3981 | } | ||
| 3982 | |||
| 3983 | if (is_ifs) { | ||
| 3984 | if (done_word(&dest, &ctx)) { | ||
| 3985 | goto parse_error; | ||
| 3986 | } | ||
| 3987 | if (ch == '\n') { | ||
| 3988 | #if ENABLE_HUSH_CASE | ||
| 3989 | /* "case ... in <newline> word) ..." - | ||
| 3990 | * newlines are ignored (but ';' wouldn't be) */ | ||
| 3991 | if (ctx.command->argv == NULL | ||
| 3992 | && ctx.ctx_res_w == RES_MATCH | ||
| 3993 | ) { | ||
| 3994 | continue; | ||
| 3995 | } | ||
| 3996 | #endif | ||
| 3997 | /* Treat newline as a command separator. */ | ||
| 3998 | done_pipe(&ctx, PIPE_SEQ); | ||
| 3999 | debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt); | ||
| 4000 | if (heredoc_cnt) { | ||
| 4001 | if (fetch_heredocs(heredoc_cnt, &ctx, input)) { | ||
| 4002 | goto parse_error; | ||
| 4003 | } | ||
| 4004 | heredoc_cnt = 0; | ||
| 4005 | } | ||
| 4006 | dest.o_assignment = MAYBE_ASSIGNMENT; | ||
| 4007 | ch = ';'; | ||
| 4008 | /* note: if (is_ifs) continue; | ||
| 4009 | * will still trigger for us */ | ||
| 4010 | } | ||
| 4011 | } | ||
| 4012 | |||
| 4013 | /* "cmd}" or "cmd }..." without semicolon or &: | ||
| 4014 | * } is an ordinary char in this case, even inside { cmd; } | ||
| 4015 | * Pathological example: { ""}; } should exec "}" cmd | ||
| 4016 | */ | ||
| 4017 | if (ch == '}') { | ||
| 4018 | if (!IS_NULL_CMD(ctx.command) /* cmd } */ | ||
| 4019 | || dest.length != 0 /* word} */ | ||
| 4020 | || dest.has_quoted_part /* ""} */ | ||
| 4021 | ) { | ||
| 4022 | goto ordinary_char; | ||
| 4023 | } | ||
| 4024 | if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */ | ||
| 4025 | goto skip_end_trigger; | ||
| 4026 | /* else: } does terminate a group */ | ||
| 4027 | } | ||
| 4028 | |||
| 4029 | if (end_trigger && end_trigger == ch | ||
| 4030 | && (ch != ';' || heredoc_cnt == 0) | ||
| 4031 | #if ENABLE_HUSH_CASE | ||
| 4032 | && (ch != ')' | ||
| 4033 | || ctx.ctx_res_w != RES_MATCH | ||
| 4034 | || (!dest.has_quoted_part && strcmp(dest.data, "esac") == 0) | ||
| 4035 | ) | ||
| 4036 | #endif | ||
| 4037 | ) { | ||
| 4038 | if (heredoc_cnt) { | ||
| 4039 | /* This is technically valid: | ||
| 4040 | * { cat <<HERE; }; echo Ok | ||
| 4041 | * heredoc | ||
| 4042 | * heredoc | ||
| 4043 | * HERE | ||
| 4044 | * but we don't support this. | ||
| 4045 | * We require heredoc to be in enclosing {}/(), | ||
| 4046 | * if any. | ||
| 4047 | */ | ||
| 4048 | syntax_error_unterm_str("here document"); | ||
| 4049 | goto parse_error; | ||
| 4050 | } | ||
| 4051 | if (done_word(&dest, &ctx)) { | ||
| 4052 | goto parse_error; | ||
| 4053 | } | ||
| 4054 | done_pipe(&ctx, PIPE_SEQ); | ||
| 4055 | dest.o_assignment = MAYBE_ASSIGNMENT; | ||
| 4056 | /* Do we sit outside of any if's, loops or case's? */ | ||
| 4057 | if (!HAS_KEYWORDS | ||
| 4058 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) | ||
| 4059 | ) { | ||
| 4060 | o_free(&dest); | ||
| 4061 | #if !BB_MMU | ||
| 4062 | debug_printf_parse("as_string '%s'\n", ctx.as_string.data); | ||
| 4063 | if (pstring) | ||
| 4064 | *pstring = ctx.as_string.data; | ||
| 4065 | else | ||
| 4066 | o_free_unsafe(&ctx.as_string); | ||
| 4067 | #endif | ||
| 4068 | debug_leave(); | ||
| 4069 | debug_printf_parse("parse_stream return %p: " | ||
| 4070 | "end_trigger char found\n", | ||
| 4071 | ctx.list_head); | ||
| 4072 | return ctx.list_head; | ||
| 4073 | } | ||
| 4074 | } | ||
| 4075 | skip_end_trigger: | ||
| 4076 | if (is_ifs) | ||
| 4077 | continue; | ||
| 4078 | |||
| 4079 | /* Catch <, > before deciding whether this word is | ||
| 4080 | * an assignment. a=1 2>z b=2: b=2 is still assignment */ | ||
| 4081 | switch (ch) { | ||
| 4082 | case '>': | ||
| 4083 | redir_fd = redirect_opt_num(&dest); | ||
| 4084 | if (done_word(&dest, &ctx)) { | ||
| 4085 | goto parse_error; | ||
| 4086 | } | ||
| 4087 | redir_style = REDIRECT_OVERWRITE; | ||
| 4088 | if (next == '>') { | ||
| 4089 | redir_style = REDIRECT_APPEND; | ||
| 4090 | ch = i_getch(input); | ||
| 4091 | nommu_addchr(&ctx.as_string, ch); | ||
| 4092 | } | ||
| 4093 | #if 0 | ||
| 4094 | else if (next == '(') { | ||
| 4095 | syntax_error(">(process) not supported"); | ||
| 4096 | goto parse_error; | ||
| 4097 | } | ||
| 4098 | #endif | ||
| 4099 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) | ||
| 4100 | goto parse_error; | ||
| 4101 | continue; /* back to top of while (1) */ | ||
| 4102 | case '<': | ||
| 4103 | redir_fd = redirect_opt_num(&dest); | ||
| 4104 | if (done_word(&dest, &ctx)) { | ||
| 4105 | goto parse_error; | ||
| 4106 | } | ||
| 4107 | redir_style = REDIRECT_INPUT; | ||
| 4108 | if (next == '<') { | ||
| 4109 | redir_style = REDIRECT_HEREDOC; | ||
| 4110 | heredoc_cnt++; | ||
| 4111 | debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt); | ||
| 4112 | ch = i_getch(input); | ||
| 4113 | nommu_addchr(&ctx.as_string, ch); | ||
| 4114 | } else if (next == '>') { | ||
| 4115 | redir_style = REDIRECT_IO; | ||
| 4116 | ch = i_getch(input); | ||
| 4117 | nommu_addchr(&ctx.as_string, ch); | ||
| 4118 | } | ||
| 4119 | #if 0 | ||
| 4120 | else if (next == '(') { | ||
| 4121 | syntax_error("<(process) not supported"); | ||
| 4122 | goto parse_error; | ||
| 4123 | } | ||
| 4124 | #endif | ||
| 4125 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) | ||
| 4126 | goto parse_error; | ||
| 4127 | continue; /* back to top of while (1) */ | ||
| 4128 | } | ||
| 4129 | |||
| 4130 | if (dest.o_assignment == MAYBE_ASSIGNMENT | ||
| 4131 | /* check that we are not in word in "a=1 2>word b=1": */ | ||
| 4132 | && !ctx.pending_redirect | ||
| 4133 | ) { | ||
| 4134 | /* ch is a special char and thus this word | ||
| 4135 | * cannot be an assignment */ | ||
| 4136 | dest.o_assignment = NOT_ASSIGNMENT; | ||
| 4137 | } | ||
| 4138 | |||
| 4139 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ | ||
| 4140 | |||
| 4141 | switch (ch) { | ||
| 4142 | case '#': | ||
| 4143 | if (dest.length == 0) { | ||
| 4144 | while (1) { | ||
| 4145 | ch = i_peek(input); | ||
| 4146 | if (ch == EOF || ch == '\n') | ||
| 4147 | break; | ||
| 4148 | i_getch(input); | ||
| 4149 | /* note: we do not add it to &ctx.as_string */ | ||
| 4150 | } | ||
| 4151 | nommu_addchr(&ctx.as_string, '\n'); | ||
| 4152 | } else { | ||
| 4153 | o_addQchr(&dest, ch); | ||
| 4154 | } | ||
| 4155 | break; | ||
| 4156 | case '\\': | ||
| 4157 | if (next == EOF) { | ||
| 4158 | syntax_error("\\<eof>"); | ||
| 4159 | xfunc_die(); | ||
| 4160 | } | ||
| 4161 | ch = i_getch(input); | ||
| 4162 | if (ch != '\n') { | ||
| 4163 | o_addchr(&dest, '\\'); | ||
| 4164 | /*nommu_addchr(&ctx.as_string, '\\'); - already done */ | ||
| 4165 | o_addchr(&dest, ch); | ||
| 4166 | nommu_addchr(&ctx.as_string, ch); | ||
| 4167 | /* Example: echo Hello \2>file | ||
| 4168 | * we need to know that word 2 is quoted */ | ||
| 4169 | dest.has_quoted_part = 1; | ||
| 4170 | } | ||
| 4171 | #if !BB_MMU | ||
| 4172 | else { | ||
| 4173 | /* It's "\<newline>". Remove trailing '\' from ctx.as_string */ | ||
| 4174 | ctx.as_string.data[--ctx.as_string.length] = '\0'; | ||
| 4175 | } | ||
| 4176 | #endif | ||
| 4177 | break; | ||
| 4178 | case '$': | ||
| 4179 | if (parse_dollar(&ctx.as_string, &dest, input) != 0) { | ||
| 4180 | debug_printf_parse("parse_stream parse error: " | ||
| 4181 | "parse_dollar returned non-0\n"); | ||
| 4182 | goto parse_error; | ||
| 4183 | } | ||
| 4184 | break; | ||
| 4185 | case '\'': | ||
| 4186 | dest.has_quoted_part = 1; | ||
| 4187 | while (1) { | ||
| 4188 | ch = i_getch(input); | ||
| 4189 | if (ch == EOF) { | ||
| 4190 | syntax_error_unterm_ch('\''); | ||
| 4191 | /*xfunc_die(); - redundant */ | ||
| 4192 | } | ||
| 4193 | nommu_addchr(&ctx.as_string, ch); | ||
| 4194 | if (ch == '\'') | ||
| 4195 | break; | ||
| 4196 | o_addqchr(&dest, ch); | ||
| 4197 | } | ||
| 4198 | break; | ||
| 4199 | case '"': | ||
| 4200 | dest.has_quoted_part = 1; | ||
| 4201 | is_in_dquote ^= 1; /* invert */ | ||
| 4202 | if (dest.o_assignment == NOT_ASSIGNMENT) | ||
| 4203 | dest.o_escape ^= 1; | ||
| 4204 | break; | ||
| 4205 | #if ENABLE_HUSH_TICK | ||
| 4206 | case '`': { | ||
| 4207 | unsigned pos; | ||
| 4208 | |||
| 4209 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
| 4210 | o_addchr(&dest, '`'); | ||
| 4211 | pos = dest.length; | ||
| 4212 | add_till_backquote(&dest, input); | ||
| 4213 | # if !BB_MMU | ||
| 4214 | o_addstr(&ctx.as_string, dest.data + pos); | ||
| 4215 | o_addchr(&ctx.as_string, '`'); | ||
| 4216 | # endif | ||
| 4217 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
| 4218 | //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos); | ||
| 4219 | break; | ||
| 4220 | } | ||
| 4221 | #endif | ||
| 4222 | case ';': | ||
| 4223 | #if ENABLE_HUSH_CASE | ||
| 4224 | case_semi: | ||
| 4225 | #endif | ||
| 4226 | if (done_word(&dest, &ctx)) { | ||
| 4227 | goto parse_error; | ||
| 4228 | } | ||
| 4229 | done_pipe(&ctx, PIPE_SEQ); | ||
| 4230 | #if ENABLE_HUSH_CASE | ||
| 4231 | /* Eat multiple semicolons, detect | ||
| 4232 | * whether it means something special */ | ||
| 4233 | while (1) { | ||
| 4234 | ch = i_peek(input); | ||
| 4235 | if (ch != ';') | ||
| 4236 | break; | ||
| 4237 | ch = i_getch(input); | ||
| 4238 | nommu_addchr(&ctx.as_string, ch); | ||
| 4239 | if (ctx.ctx_res_w == RES_CASE_BODY) { | ||
| 4240 | ctx.ctx_dsemicolon = 1; | ||
| 4241 | ctx.ctx_res_w = RES_MATCH; | ||
| 4242 | break; | ||
| 4243 | } | ||
| 4244 | } | ||
| 4245 | #endif | ||
| 4246 | new_cmd: | ||
| 4247 | /* We just finished a cmd. New one may start | ||
| 4248 | * with an assignment */ | ||
| 4249 | dest.o_assignment = MAYBE_ASSIGNMENT; | ||
| 4250 | break; | ||
| 4251 | case '&': | ||
| 4252 | if (done_word(&dest, &ctx)) { | ||
| 4253 | goto parse_error; | ||
| 4254 | } | ||
| 4255 | if (next == '&') { | ||
| 4256 | ch = i_getch(input); | ||
| 4257 | nommu_addchr(&ctx.as_string, ch); | ||
| 4258 | done_pipe(&ctx, PIPE_AND); | ||
| 4259 | } else { | ||
| 4260 | done_pipe(&ctx, PIPE_BG); | ||
| 4261 | } | ||
| 4262 | goto new_cmd; | ||
| 4263 | case '|': | ||
| 4264 | if (done_word(&dest, &ctx)) { | ||
| 4265 | goto parse_error; | ||
| 4266 | } | ||
| 4267 | #if ENABLE_HUSH_CASE | ||
| 4268 | if (ctx.ctx_res_w == RES_MATCH) | ||
| 4269 | break; /* we are in case's "word | word)" */ | ||
| 4270 | #endif | ||
| 4271 | if (next == '|') { /* || */ | ||
| 4272 | ch = i_getch(input); | ||
| 4273 | nommu_addchr(&ctx.as_string, ch); | ||
| 4274 | done_pipe(&ctx, PIPE_OR); | ||
| 4275 | } else { | ||
| 4276 | /* we could pick up a file descriptor choice here | ||
| 4277 | * with redirect_opt_num(), but bash doesn't do it. | ||
| 4278 | * "echo foo 2| cat" yields "foo 2". */ | ||
| 4279 | done_command(&ctx); | ||
| 4280 | #if !BB_MMU | ||
| 4281 | o_reset_to_empty_unquoted(&ctx.as_string); | ||
| 4282 | #endif | ||
| 4283 | } | ||
| 4284 | goto new_cmd; | ||
| 4285 | case '(': | ||
| 4286 | #if ENABLE_HUSH_CASE | ||
| 4287 | /* "case... in [(]word)..." - skip '(' */ | ||
| 4288 | if (ctx.ctx_res_w == RES_MATCH | ||
| 4289 | && ctx.command->argv == NULL /* not (word|(... */ | ||
| 4290 | && dest.length == 0 /* not word(... */ | ||
| 4291 | && dest.has_quoted_part == 0 /* not ""(... */ | ||
| 4292 | ) { | ||
| 4293 | continue; | ||
| 4294 | } | ||
| 4295 | #endif | ||
| 4296 | case '{': | ||
| 4297 | if (parse_group(&dest, &ctx, input, ch) != 0) { | ||
| 4298 | goto parse_error; | ||
| 4299 | } | ||
| 4300 | goto new_cmd; | ||
| 4301 | case ')': | ||
| 4302 | #if ENABLE_HUSH_CASE | ||
| 4303 | if (ctx.ctx_res_w == RES_MATCH) | ||
| 4304 | goto case_semi; | ||
| 4305 | #endif | ||
| 4306 | case '}': | ||
| 4307 | /* proper use of this character is caught by end_trigger: | ||
| 4308 | * if we see {, we call parse_group(..., end_trigger='}') | ||
| 4309 | * and it will match } earlier (not here). */ | ||
| 4310 | syntax_error_unexpected_ch(ch); | ||
| 4311 | goto parse_error; | ||
| 4312 | default: | ||
| 4313 | if (HUSH_DEBUG) | ||
| 4314 | bb_error_msg_and_die("BUG: unexpected %c\n", ch); | ||
| 4315 | } | ||
| 4316 | } /* while (1) */ | ||
| 4317 | |||
| 4318 | parse_error: | ||
| 4319 | { | ||
| 4320 | struct parse_context *pctx; | ||
| 4321 | IF_HAS_KEYWORDS(struct parse_context *p2;) | ||
| 4322 | |||
| 4323 | /* Clean up allocated tree. | ||
| 4324 | * Sample for finding leaks on syntax error recovery path. | ||
| 4325 | * Run it from interactive shell, watch pmap `pidof hush`. | ||
| 4326 | * while if false; then false; fi; do break; fi | ||
| 4327 | * Samples to catch leaks at execution: | ||
| 4328 | * while if (true | {true;}); then echo ok; fi; do break; done | ||
| 4329 | * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done | ||
| 4330 | */ | ||
| 4331 | pctx = &ctx; | ||
| 4332 | do { | ||
| 4333 | /* Update pipe/command counts, | ||
| 4334 | * otherwise freeing may miss some */ | ||
| 4335 | done_pipe(pctx, PIPE_SEQ); | ||
| 4336 | debug_printf_clean("freeing list %p from ctx %p\n", | ||
| 4337 | pctx->list_head, pctx); | ||
| 4338 | debug_print_tree(pctx->list_head, 0); | ||
| 4339 | free_pipe_list(pctx->list_head); | ||
| 4340 | debug_printf_clean("freed list %p\n", pctx->list_head); | ||
| 4341 | #if !BB_MMU | ||
| 4342 | o_free_unsafe(&pctx->as_string); | ||
| 4343 | #endif | ||
| 4344 | IF_HAS_KEYWORDS(p2 = pctx->stack;) | ||
| 4345 | if (pctx != &ctx) { | ||
| 4346 | free(pctx); | ||
| 4347 | } | ||
| 4348 | IF_HAS_KEYWORDS(pctx = p2;) | ||
| 4349 | } while (HAS_KEYWORDS && pctx); | ||
| 4350 | /* Free text, clear all dest fields */ | ||
| 4351 | o_free(&dest); | ||
| 4352 | /* If we are not in top-level parse, we return, | ||
| 4353 | * our caller will propagate error. | ||
| 4354 | */ | ||
| 4355 | if (end_trigger != ';') { | ||
| 4356 | #if !BB_MMU | ||
| 4357 | if (pstring) | ||
| 4358 | *pstring = NULL; | ||
| 4359 | #endif | ||
| 4360 | debug_leave(); | ||
| 4361 | return ERR_PTR; | ||
| 4362 | } | ||
| 4363 | /* Discard cached input, force prompt */ | ||
| 4364 | input->p = NULL; | ||
| 4365 | IF_HUSH_INTERACTIVE(input->promptme = 1;) | ||
| 4366 | goto reset; | ||
| 4367 | } | ||
| 4368 | } | ||
| 4369 | |||
| 4370 | |||
| 4371 | /*** Execution routines ***/ | ||
| 4372 | |||
| 4373 | /* Expansion can recurse, need forward decls: */ | ||
| 4374 | static char *expand_string_to_string(const char *str); | ||
| 4375 | static int process_command_subs(o_string *dest, const char *s); | ||
| 2532 | 4376 | ||
| 2533 | /* expand_strvec_to_strvec() takes a list of strings, expands | 4377 | /* expand_strvec_to_strvec() takes a list of strings, expands |
| 2534 | * all variable references within and returns a pointer to | 4378 | * all variable references within and returns a pointer to |
| @@ -3463,6 +5307,194 @@ static void re_execute_shell(char ***to_free, const char *s, | |||
| 3463 | #endif /* !BB_MMU */ | 5307 | #endif /* !BB_MMU */ |
| 3464 | 5308 | ||
| 3465 | 5309 | ||
| 5310 | static int run_and_free_list(struct pipe *pi); | ||
| 5311 | |||
| 5312 | /* Executing from string: eval, sh -c '...' | ||
| 5313 | * or from file: /etc/profile, . file, sh <script>, sh (intereactive) | ||
| 5314 | * end_trigger controls how often we stop parsing | ||
| 5315 | * NUL: parse all, execute, return | ||
| 5316 | * ';': parse till ';' or newline, execute, repeat till EOF | ||
| 5317 | */ | ||
| 5318 | static void parse_and_run_stream(struct in_str *inp, int end_trigger) | ||
| 5319 | { | ||
| 5320 | /* Why we need empty flag? | ||
| 5321 | * An obscure corner case "false; ``; echo $?": | ||
| 5322 | * empty command in `` should still set $? to 0. | ||
| 5323 | * But we can't just set $? to 0 at the start, | ||
| 5324 | * this breaks "false; echo `echo $?`" case. | ||
| 5325 | */ | ||
| 5326 | bool empty = 1; | ||
| 5327 | while (1) { | ||
| 5328 | struct pipe *pipe_list; | ||
| 5329 | |||
| 5330 | pipe_list = parse_stream(NULL, inp, end_trigger); | ||
| 5331 | if (!pipe_list) { /* EOF */ | ||
| 5332 | if (empty) | ||
| 5333 | G.last_exitcode = 0; | ||
| 5334 | break; | ||
| 5335 | } | ||
| 5336 | debug_print_tree(pipe_list, 0); | ||
| 5337 | debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); | ||
| 5338 | run_and_free_list(pipe_list); | ||
| 5339 | empty = 0; | ||
| 5340 | } | ||
| 5341 | } | ||
| 5342 | |||
| 5343 | static void parse_and_run_string(const char *s) | ||
| 5344 | { | ||
| 5345 | struct in_str input; | ||
| 5346 | setup_string_in_str(&input, s); | ||
| 5347 | parse_and_run_stream(&input, '\0'); | ||
| 5348 | } | ||
| 5349 | |||
| 5350 | static void parse_and_run_file(FILE *f) | ||
| 5351 | { | ||
| 5352 | struct in_str input; | ||
| 5353 | setup_file_in_str(&input, f); | ||
| 5354 | parse_and_run_stream(&input, ';'); | ||
| 5355 | } | ||
| 5356 | |||
| 5357 | #if ENABLE_HUSH_TICK | ||
| 5358 | static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | ||
| 5359 | { | ||
| 5360 | pid_t pid; | ||
| 5361 | int channel[2]; | ||
| 5362 | # if !BB_MMU | ||
| 5363 | char **to_free = NULL; | ||
| 5364 | # endif | ||
| 5365 | |||
| 5366 | xpipe(channel); | ||
| 5367 | pid = BB_MMU ? xfork() : xvfork(); | ||
| 5368 | if (pid == 0) { /* child */ | ||
| 5369 | disable_restore_tty_pgrp_on_exit(); | ||
| 5370 | /* Process substitution is not considered to be usual | ||
| 5371 | * 'command execution'. | ||
| 5372 | * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. | ||
| 5373 | */ | ||
| 5374 | bb_signals(0 | ||
| 5375 | + (1 << SIGTSTP) | ||
| 5376 | + (1 << SIGTTIN) | ||
| 5377 | + (1 << SIGTTOU) | ||
| 5378 | , SIG_IGN); | ||
| 5379 | CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ | ||
| 5380 | close(channel[0]); /* NB: close _first_, then move fd! */ | ||
| 5381 | xmove_fd(channel[1], 1); | ||
| 5382 | /* Prevent it from trying to handle ctrl-z etc */ | ||
| 5383 | IF_HUSH_JOB(G.run_list_level = 1;) | ||
| 5384 | /* Awful hack for `trap` or $(trap). | ||
| 5385 | * | ||
| 5386 | * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html | ||
| 5387 | * contains an example where "trap" is executed in a subshell: | ||
| 5388 | * | ||
| 5389 | * save_traps=$(trap) | ||
| 5390 | * ... | ||
| 5391 | * eval "$save_traps" | ||
| 5392 | * | ||
| 5393 | * Standard does not say that "trap" in subshell shall print | ||
| 5394 | * parent shell's traps. It only says that its output | ||
| 5395 | * must have suitable form, but then, in the above example | ||
| 5396 | * (which is not supposed to be normative), it implies that. | ||
| 5397 | * | ||
| 5398 | * bash (and probably other shell) does implement it | ||
| 5399 | * (traps are reset to defaults, but "trap" still shows them), | ||
| 5400 | * but as a result, "trap" logic is hopelessly messed up: | ||
| 5401 | * | ||
| 5402 | * # trap | ||
| 5403 | * trap -- 'echo Ho' SIGWINCH <--- we have a handler | ||
| 5404 | * # (trap) <--- trap is in subshell - no output (correct, traps are reset) | ||
| 5405 | * # true | trap <--- trap is in subshell - no output (ditto) | ||
| 5406 | * # echo `true | trap` <--- in subshell - output (but traps are reset!) | ||
| 5407 | * trap -- 'echo Ho' SIGWINCH | ||
| 5408 | * # echo `(trap)` <--- in subshell in subshell - output | ||
| 5409 | * trap -- 'echo Ho' SIGWINCH | ||
| 5410 | * # echo `true | (trap)` <--- in subshell in subshell in subshell - output! | ||
| 5411 | * trap -- 'echo Ho' SIGWINCH | ||
| 5412 | * | ||
| 5413 | * The rules when to forget and when to not forget traps | ||
| 5414 | * get really complex and nonsensical. | ||
| 5415 | * | ||
| 5416 | * Our solution: ONLY bare $(trap) or `trap` is special. | ||
| 5417 | */ | ||
| 5418 | s = skip_whitespace(s); | ||
| 5419 | if (strncmp(s, "trap", 4) == 0 | ||
| 5420 | && skip_whitespace(s + 4)[0] == '\0' | ||
| 5421 | ) { | ||
| 5422 | static const char *const argv[] = { NULL, NULL }; | ||
| 5423 | builtin_trap((char**)argv); | ||
| 5424 | exit(0); /* not _exit() - we need to fflush */ | ||
| 5425 | } | ||
| 5426 | # if BB_MMU | ||
| 5427 | reset_traps_to_defaults(); | ||
| 5428 | parse_and_run_string(s); | ||
| 5429 | _exit(G.last_exitcode); | ||
| 5430 | # else | ||
| 5431 | /* We re-execute after vfork on NOMMU. This makes this script safe: | ||
| 5432 | * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG | ||
| 5433 | * huge=`cat BIG` # was blocking here forever | ||
| 5434 | * echo OK | ||
| 5435 | */ | ||
| 5436 | re_execute_shell(&to_free, | ||
| 5437 | s, | ||
| 5438 | G.global_argv[0], | ||
| 5439 | G.global_argv + 1, | ||
| 5440 | NULL); | ||
| 5441 | # endif | ||
| 5442 | } | ||
| 5443 | |||
| 5444 | /* parent */ | ||
| 5445 | *pid_p = pid; | ||
| 5446 | # if ENABLE_HUSH_FAST | ||
| 5447 | G.count_SIGCHLD++; | ||
| 5448 | //bb_error_msg("[%d] fork in generate_stream_from_string:" | ||
| 5449 | // " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", | ||
| 5450 | // getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | ||
| 5451 | # endif | ||
| 5452 | enable_restore_tty_pgrp_on_exit(); | ||
| 5453 | # if !BB_MMU | ||
| 5454 | free(to_free); | ||
| 5455 | # endif | ||
| 5456 | close(channel[1]); | ||
| 5457 | close_on_exec_on(channel[0]); | ||
| 5458 | return xfdopen_for_read(channel[0]); | ||
| 5459 | } | ||
| 5460 | |||
| 5461 | /* Return code is exit status of the process that is run. */ | ||
| 5462 | static int process_command_subs(o_string *dest, const char *s) | ||
| 5463 | { | ||
| 5464 | FILE *fp; | ||
| 5465 | struct in_str pipe_str; | ||
| 5466 | pid_t pid; | ||
| 5467 | int status, ch, eol_cnt; | ||
| 5468 | |||
| 5469 | fp = generate_stream_from_string(s, &pid); | ||
| 5470 | |||
| 5471 | /* Now send results of command back into original context */ | ||
| 5472 | setup_file_in_str(&pipe_str, fp); | ||
| 5473 | eol_cnt = 0; | ||
| 5474 | while ((ch = i_getch(&pipe_str)) != EOF) { | ||
| 5475 | if (ch == '\n') { | ||
| 5476 | eol_cnt++; | ||
| 5477 | continue; | ||
| 5478 | } | ||
| 5479 | while (eol_cnt) { | ||
| 5480 | o_addchr(dest, '\n'); | ||
| 5481 | eol_cnt--; | ||
| 5482 | } | ||
| 5483 | o_addQchr(dest, ch); | ||
| 5484 | } | ||
| 5485 | |||
| 5486 | debug_printf("done reading from `cmd` pipe, closing it\n"); | ||
| 5487 | fclose(fp); | ||
| 5488 | /* We need to extract exitcode. Test case | ||
| 5489 | * "true; echo `sleep 1; false` $?" | ||
| 5490 | * should print 1 */ | ||
| 5491 | safe_waitpid(pid, &status, 0); | ||
| 5492 | debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status)); | ||
| 5493 | return WEXITSTATUS(status); | ||
| 5494 | } | ||
| 5495 | #endif /* ENABLE_HUSH_TICK */ | ||
| 5496 | |||
| 5497 | |||
| 3466 | static void setup_heredoc(struct redir_struct *redir) | 5498 | static void setup_heredoc(struct redir_struct *redir) |
| 3467 | { | 5499 | { |
| 3468 | struct fd_pair pair; | 5500 | struct fd_pair pair; |
| @@ -3622,101 +5654,6 @@ static void restore_redirects(int squirrel[]) | |||
| 3622 | } | 5654 | } |
| 3623 | } | 5655 | } |
| 3624 | 5656 | ||
| 3625 | |||
| 3626 | static void free_pipe_list(struct pipe *head); | ||
| 3627 | |||
| 3628 | /* Return code is the exit status of the pipe */ | ||
| 3629 | static void free_pipe(struct pipe *pi) | ||
| 3630 | { | ||
| 3631 | char **p; | ||
| 3632 | struct command *command; | ||
| 3633 | struct redir_struct *r, *rnext; | ||
| 3634 | int a, i; | ||
| 3635 | |||
| 3636 | if (pi->stopped_cmds > 0) /* why? */ | ||
| 3637 | return; | ||
| 3638 | debug_printf_clean("run pipe: (pid %d)\n", getpid()); | ||
| 3639 | for (i = 0; i < pi->num_cmds; i++) { | ||
| 3640 | command = &pi->cmds[i]; | ||
| 3641 | debug_printf_clean(" command %d:\n", i); | ||
| 3642 | if (command->argv) { | ||
| 3643 | for (a = 0, p = command->argv; *p; a++, p++) { | ||
| 3644 | debug_printf_clean(" argv[%d] = %s\n", a, *p); | ||
| 3645 | } | ||
| 3646 | free_strings(command->argv); | ||
| 3647 | command->argv = NULL; | ||
| 3648 | } | ||
| 3649 | /* not "else if": on syntax error, we may have both! */ | ||
| 3650 | if (command->group) { | ||
| 3651 | debug_printf_clean(" begin group (cmd_type:%d)\n", | ||
| 3652 | command->cmd_type); | ||
| 3653 | free_pipe_list(command->group); | ||
| 3654 | debug_printf_clean(" end group\n"); | ||
| 3655 | command->group = NULL; | ||
| 3656 | } | ||
| 3657 | /* else is crucial here. | ||
| 3658 | * If group != NULL, child_func is meaningless */ | ||
| 3659 | #if ENABLE_HUSH_FUNCTIONS | ||
| 3660 | else if (command->child_func) { | ||
| 3661 | debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func); | ||
| 3662 | command->child_func->parent_cmd = NULL; | ||
| 3663 | } | ||
| 3664 | #endif | ||
| 3665 | #if !BB_MMU | ||
| 3666 | free(command->group_as_string); | ||
| 3667 | command->group_as_string = NULL; | ||
| 3668 | #endif | ||
| 3669 | for (r = command->redirects; r; r = rnext) { | ||
| 3670 | debug_printf_clean(" redirect %d%s", | ||
| 3671 | r->rd_fd, redir_table[r->rd_type].descrip); | ||
| 3672 | /* guard against the case >$FOO, where foo is unset or blank */ | ||
| 3673 | if (r->rd_filename) { | ||
| 3674 | debug_printf_clean(" fname:'%s'\n", r->rd_filename); | ||
| 3675 | free(r->rd_filename); | ||
| 3676 | r->rd_filename = NULL; | ||
| 3677 | } | ||
| 3678 | debug_printf_clean(" rd_dup:%d\n", r->rd_dup); | ||
| 3679 | rnext = r->next; | ||
| 3680 | free(r); | ||
| 3681 | } | ||
| 3682 | command->redirects = NULL; | ||
| 3683 | } | ||
| 3684 | free(pi->cmds); /* children are an array, they get freed all at once */ | ||
| 3685 | pi->cmds = NULL; | ||
| 3686 | #if ENABLE_HUSH_JOB | ||
| 3687 | free(pi->cmdtext); | ||
| 3688 | pi->cmdtext = NULL; | ||
| 3689 | #endif | ||
| 3690 | } | ||
| 3691 | |||
| 3692 | static void free_pipe_list(struct pipe *head) | ||
| 3693 | { | ||
| 3694 | struct pipe *pi, *next; | ||
| 3695 | |||
| 3696 | for (pi = head; pi; pi = next) { | ||
| 3697 | #if HAS_KEYWORDS | ||
| 3698 | debug_printf_clean(" pipe reserved word %d\n", pi->res_word); | ||
| 3699 | #endif | ||
| 3700 | free_pipe(pi); | ||
| 3701 | debug_printf_clean("pipe followup code %d\n", pi->followup); | ||
| 3702 | next = pi->next; | ||
| 3703 | /*pi->next = NULL;*/ | ||
| 3704 | free(pi); | ||
| 3705 | } | ||
| 3706 | } | ||
| 3707 | |||
| 3708 | |||
| 3709 | static int run_list(struct pipe *pi); | ||
| 3710 | #if BB_MMU | ||
| 3711 | #define parse_stream(pstring, input, end_trigger) \ | ||
| 3712 | parse_stream(input, end_trigger) | ||
| 3713 | #endif | ||
| 3714 | static struct pipe *parse_stream(char **pstring, | ||
| 3715 | struct in_str *input, | ||
| 3716 | int end_trigger); | ||
| 3717 | static void parse_and_run_string(const char *s); | ||
| 3718 | |||
| 3719 | |||
| 3720 | static char *find_in_path(const char *arg) | 5657 | static char *find_in_path(const char *arg) |
| 3721 | { | 5658 | { |
| 3722 | char *ret = NULL; | 5659 | char *ret = NULL; |
| @@ -4482,7 +6419,11 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||
| 4482 | #define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, char argv_expanded) \ | 6419 | #define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, char argv_expanded) \ |
| 4483 | redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel) | 6420 | redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel) |
| 4484 | #endif | 6421 | #endif |
| 4485 | static int redirect_and_varexp_helper(char ***new_env_p, struct variable **old_vars_p, struct command *command, int squirrel[3], char **argv_expanded) | 6422 | static int redirect_and_varexp_helper(char ***new_env_p, |
| 6423 | struct variable **old_vars_p, | ||
| 6424 | struct command *command, | ||
| 6425 | int squirrel[3], | ||
| 6426 | char **argv_expanded) | ||
| 4486 | { | 6427 | { |
| 4487 | /* setup_redirects acts on file descriptors, not FILEs. | 6428 | /* setup_redirects acts on file descriptors, not FILEs. |
| 4488 | * This is perfect for work that comes after exec(). | 6429 | * This is perfect for work that comes after exec(). |
| @@ -5291,1947 +7232,6 @@ static int run_and_free_list(struct pipe *pi) | |||
| 5291 | } | 7232 | } |
| 5292 | 7233 | ||
| 5293 | 7234 | ||
| 5294 | static struct pipe *new_pipe(void) | ||
| 5295 | { | ||
| 5296 | struct pipe *pi; | ||
| 5297 | pi = xzalloc(sizeof(struct pipe)); | ||
| 5298 | /*pi->followup = 0; - deliberately invalid value */ | ||
| 5299 | /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */ | ||
| 5300 | return pi; | ||
| 5301 | } | ||
| 5302 | |||
| 5303 | /* Command (member of a pipe) is complete, or we start a new pipe | ||
| 5304 | * if ctx->command is NULL. | ||
| 5305 | * No errors possible here. | ||
| 5306 | */ | ||
| 5307 | static int done_command(struct parse_context *ctx) | ||
| 5308 | { | ||
| 5309 | /* The command is really already in the pipe structure, so | ||
| 5310 | * advance the pipe counter and make a new, null command. */ | ||
| 5311 | struct pipe *pi = ctx->pipe; | ||
| 5312 | struct command *command = ctx->command; | ||
| 5313 | |||
| 5314 | if (command) { | ||
| 5315 | if (IS_NULL_CMD(command)) { | ||
| 5316 | debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); | ||
| 5317 | goto clear_and_ret; | ||
| 5318 | } | ||
| 5319 | pi->num_cmds++; | ||
| 5320 | debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); | ||
| 5321 | //debug_print_tree(ctx->list_head, 20); | ||
| 5322 | } else { | ||
| 5323 | debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds); | ||
| 5324 | } | ||
| 5325 | |||
| 5326 | /* Only real trickiness here is that the uncommitted | ||
| 5327 | * command structure is not counted in pi->num_cmds. */ | ||
| 5328 | pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1)); | ||
| 5329 | ctx->command = command = &pi->cmds[pi->num_cmds]; | ||
| 5330 | clear_and_ret: | ||
| 5331 | memset(command, 0, sizeof(*command)); | ||
| 5332 | return pi->num_cmds; /* used only for 0/nonzero check */ | ||
| 5333 | } | ||
| 5334 | |||
| 5335 | static void done_pipe(struct parse_context *ctx, pipe_style type) | ||
| 5336 | { | ||
| 5337 | int not_null; | ||
| 5338 | |||
| 5339 | debug_printf_parse("done_pipe entered, followup %d\n", type); | ||
| 5340 | /* Close previous command */ | ||
| 5341 | not_null = done_command(ctx); | ||
| 5342 | ctx->pipe->followup = type; | ||
| 5343 | #if HAS_KEYWORDS | ||
| 5344 | ctx->pipe->pi_inverted = ctx->ctx_inverted; | ||
| 5345 | ctx->ctx_inverted = 0; | ||
| 5346 | ctx->pipe->res_word = ctx->ctx_res_w; | ||
| 5347 | #endif | ||
| 5348 | |||
| 5349 | /* Without this check, even just <enter> on command line generates | ||
| 5350 | * tree of three NOPs (!). Which is harmless but annoying. | ||
| 5351 | * IOW: it is safe to do it unconditionally. */ | ||
| 5352 | if (not_null | ||
| 5353 | #if ENABLE_HUSH_IF | ||
| 5354 | || ctx->ctx_res_w == RES_FI | ||
| 5355 | #endif | ||
| 5356 | #if ENABLE_HUSH_LOOPS | ||
| 5357 | || ctx->ctx_res_w == RES_DONE | ||
| 5358 | || ctx->ctx_res_w == RES_FOR | ||
| 5359 | || ctx->ctx_res_w == RES_IN | ||
| 5360 | #endif | ||
| 5361 | #if ENABLE_HUSH_CASE | ||
| 5362 | || ctx->ctx_res_w == RES_ESAC | ||
| 5363 | #endif | ||
| 5364 | ) { | ||
| 5365 | struct pipe *new_p; | ||
| 5366 | debug_printf_parse("done_pipe: adding new pipe: " | ||
| 5367 | "not_null:%d ctx->ctx_res_w:%d\n", | ||
| 5368 | not_null, ctx->ctx_res_w); | ||
| 5369 | new_p = new_pipe(); | ||
| 5370 | ctx->pipe->next = new_p; | ||
| 5371 | ctx->pipe = new_p; | ||
| 5372 | /* RES_THEN, RES_DO etc are "sticky" - | ||
| 5373 | * they remain set for pipes inside if/while. | ||
| 5374 | * This is used to control execution. | ||
| 5375 | * RES_FOR and RES_IN are NOT sticky (needed to support | ||
| 5376 | * cases where variable or value happens to match a keyword): | ||
| 5377 | */ | ||
| 5378 | #if ENABLE_HUSH_LOOPS | ||
| 5379 | if (ctx->ctx_res_w == RES_FOR | ||
| 5380 | || ctx->ctx_res_w == RES_IN) | ||
| 5381 | ctx->ctx_res_w = RES_NONE; | ||
| 5382 | #endif | ||
| 5383 | #if ENABLE_HUSH_CASE | ||
| 5384 | if (ctx->ctx_res_w == RES_MATCH) | ||
| 5385 | ctx->ctx_res_w = RES_CASE_BODY; | ||
| 5386 | if (ctx->ctx_res_w == RES_CASE) | ||
| 5387 | ctx->ctx_res_w = RES_CASE_IN; | ||
| 5388 | #endif | ||
| 5389 | ctx->command = NULL; /* trick done_command below */ | ||
| 5390 | /* Create the memory for command, roughly: | ||
| 5391 | * ctx->pipe->cmds = new struct command; | ||
| 5392 | * ctx->command = &ctx->pipe->cmds[0]; | ||
| 5393 | */ | ||
| 5394 | done_command(ctx); | ||
| 5395 | //debug_print_tree(ctx->list_head, 10); | ||
| 5396 | } | ||
| 5397 | debug_printf_parse("done_pipe return\n"); | ||
| 5398 | } | ||
| 5399 | |||
| 5400 | static void initialize_context(struct parse_context *ctx) | ||
| 5401 | { | ||
| 5402 | memset(ctx, 0, sizeof(*ctx)); | ||
| 5403 | ctx->pipe = ctx->list_head = new_pipe(); | ||
| 5404 | /* Create the memory for command, roughly: | ||
| 5405 | * ctx->pipe->cmds = new struct command; | ||
| 5406 | * ctx->command = &ctx->pipe->cmds[0]; | ||
| 5407 | */ | ||
| 5408 | done_command(ctx); | ||
| 5409 | } | ||
| 5410 | |||
| 5411 | /* If a reserved word is found and processed, parse context is modified | ||
| 5412 | * and 1 is returned. | ||
| 5413 | */ | ||
| 5414 | #if HAS_KEYWORDS | ||
| 5415 | struct reserved_combo { | ||
| 5416 | char literal[6]; | ||
| 5417 | unsigned char res; | ||
| 5418 | unsigned char assignment_flag; | ||
| 5419 | int flag; | ||
| 5420 | }; | ||
| 5421 | enum { | ||
| 5422 | FLAG_END = (1 << RES_NONE ), | ||
| 5423 | # if ENABLE_HUSH_IF | ||
| 5424 | FLAG_IF = (1 << RES_IF ), | ||
| 5425 | FLAG_THEN = (1 << RES_THEN ), | ||
| 5426 | FLAG_ELIF = (1 << RES_ELIF ), | ||
| 5427 | FLAG_ELSE = (1 << RES_ELSE ), | ||
| 5428 | FLAG_FI = (1 << RES_FI ), | ||
| 5429 | # endif | ||
| 5430 | # if ENABLE_HUSH_LOOPS | ||
| 5431 | FLAG_FOR = (1 << RES_FOR ), | ||
| 5432 | FLAG_WHILE = (1 << RES_WHILE), | ||
| 5433 | FLAG_UNTIL = (1 << RES_UNTIL), | ||
| 5434 | FLAG_DO = (1 << RES_DO ), | ||
| 5435 | FLAG_DONE = (1 << RES_DONE ), | ||
| 5436 | FLAG_IN = (1 << RES_IN ), | ||
| 5437 | # endif | ||
| 5438 | # if ENABLE_HUSH_CASE | ||
| 5439 | FLAG_MATCH = (1 << RES_MATCH), | ||
| 5440 | FLAG_ESAC = (1 << RES_ESAC ), | ||
| 5441 | # endif | ||
| 5442 | FLAG_START = (1 << RES_XXXX ), | ||
| 5443 | }; | ||
| 5444 | |||
| 5445 | static const struct reserved_combo* match_reserved_word(o_string *word) | ||
| 5446 | { | ||
| 5447 | /* Mostly a list of accepted follow-up reserved words. | ||
| 5448 | * FLAG_END means we are done with the sequence, and are ready | ||
| 5449 | * to turn the compound list into a command. | ||
| 5450 | * FLAG_START means the word must start a new compound list. | ||
| 5451 | */ | ||
| 5452 | static const struct reserved_combo reserved_list[] = { | ||
| 5453 | # if ENABLE_HUSH_IF | ||
| 5454 | { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, | ||
| 5455 | { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START }, | ||
| 5456 | { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, | ||
| 5457 | { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN }, | ||
| 5458 | { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI }, | ||
| 5459 | { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, | ||
| 5460 | # endif | ||
| 5461 | # if ENABLE_HUSH_LOOPS | ||
| 5462 | { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, | ||
| 5463 | { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, | ||
| 5464 | { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, | ||
| 5465 | { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, | ||
| 5466 | { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE }, | ||
| 5467 | { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, | ||
| 5468 | # endif | ||
| 5469 | # if ENABLE_HUSH_CASE | ||
| 5470 | { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, | ||
| 5471 | { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, | ||
| 5472 | # endif | ||
| 5473 | }; | ||
| 5474 | const struct reserved_combo *r; | ||
| 5475 | |||
| 5476 | for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { | ||
| 5477 | if (strcmp(word->data, r->literal) == 0) | ||
| 5478 | return r; | ||
| 5479 | } | ||
| 5480 | return NULL; | ||
| 5481 | } | ||
| 5482 | /* Return 0: not a keyword, 1: keyword | ||
| 5483 | */ | ||
| 5484 | static int reserved_word(o_string *word, struct parse_context *ctx) | ||
| 5485 | { | ||
| 5486 | # if ENABLE_HUSH_CASE | ||
| 5487 | static const struct reserved_combo reserved_match = { | ||
| 5488 | "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC | ||
| 5489 | }; | ||
| 5490 | # endif | ||
| 5491 | const struct reserved_combo *r; | ||
| 5492 | |||
| 5493 | if (word->has_quoted_part) | ||
| 5494 | return 0; | ||
| 5495 | r = match_reserved_word(word); | ||
| 5496 | if (!r) | ||
| 5497 | return 0; | ||
| 5498 | |||
| 5499 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); | ||
| 5500 | # if ENABLE_HUSH_CASE | ||
| 5501 | if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) { | ||
| 5502 | /* "case word IN ..." - IN part starts first MATCH part */ | ||
| 5503 | r = &reserved_match; | ||
| 5504 | } else | ||
| 5505 | # endif | ||
| 5506 | if (r->flag == 0) { /* '!' */ | ||
| 5507 | if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ | ||
| 5508 | syntax_error("! ! command"); | ||
| 5509 | ctx->ctx_res_w = RES_SNTX; | ||
| 5510 | } | ||
| 5511 | ctx->ctx_inverted = 1; | ||
| 5512 | return 1; | ||
| 5513 | } | ||
| 5514 | if (r->flag & FLAG_START) { | ||
| 5515 | struct parse_context *old; | ||
| 5516 | |||
| 5517 | old = xmalloc(sizeof(*old)); | ||
| 5518 | debug_printf_parse("push stack %p\n", old); | ||
| 5519 | *old = *ctx; /* physical copy */ | ||
| 5520 | initialize_context(ctx); | ||
| 5521 | ctx->stack = old; | ||
| 5522 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { | ||
| 5523 | syntax_error_at(word->data); | ||
| 5524 | ctx->ctx_res_w = RES_SNTX; | ||
| 5525 | return 1; | ||
| 5526 | } else { | ||
| 5527 | /* "{...} fi" is ok. "{...} if" is not | ||
| 5528 | * Example: | ||
| 5529 | * if { echo foo; } then { echo bar; } fi */ | ||
| 5530 | if (ctx->command->group) | ||
| 5531 | done_pipe(ctx, PIPE_SEQ); | ||
| 5532 | } | ||
| 5533 | |||
| 5534 | ctx->ctx_res_w = r->res; | ||
| 5535 | ctx->old_flag = r->flag; | ||
| 5536 | word->o_assignment = r->assignment_flag; | ||
| 5537 | |||
| 5538 | if (ctx->old_flag & FLAG_END) { | ||
| 5539 | struct parse_context *old; | ||
| 5540 | |||
| 5541 | done_pipe(ctx, PIPE_SEQ); | ||
| 5542 | debug_printf_parse("pop stack %p\n", ctx->stack); | ||
| 5543 | old = ctx->stack; | ||
| 5544 | old->command->group = ctx->list_head; | ||
| 5545 | old->command->cmd_type = CMD_NORMAL; | ||
| 5546 | # if !BB_MMU | ||
| 5547 | o_addstr(&old->as_string, ctx->as_string.data); | ||
| 5548 | o_free_unsafe(&ctx->as_string); | ||
| 5549 | old->command->group_as_string = xstrdup(old->as_string.data); | ||
| 5550 | debug_printf_parse("pop, remembering as:'%s'\n", | ||
| 5551 | old->command->group_as_string); | ||
| 5552 | # endif | ||
| 5553 | *ctx = *old; /* physical copy */ | ||
| 5554 | free(old); | ||
| 5555 | } | ||
| 5556 | return 1; | ||
| 5557 | } | ||
| 5558 | #endif /* HAS_KEYWORDS */ | ||
| 5559 | |||
| 5560 | /* Word is complete, look at it and update parsing context. | ||
| 5561 | * Normal return is 0. Syntax errors return 1. | ||
| 5562 | * Note: on return, word is reset, but not o_free'd! | ||
| 5563 | */ | ||
| 5564 | static int done_word(o_string *word, struct parse_context *ctx) | ||
| 5565 | { | ||
| 5566 | struct command *command = ctx->command; | ||
| 5567 | |||
| 5568 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, command); | ||
| 5569 | if (word->length == 0 && !word->has_quoted_part) { | ||
| 5570 | debug_printf_parse("done_word return 0: true null, ignored\n"); | ||
| 5571 | return 0; | ||
| 5572 | } | ||
| 5573 | |||
| 5574 | if (ctx->pending_redirect) { | ||
| 5575 | /* We do not glob in e.g. >*.tmp case. bash seems to glob here | ||
| 5576 | * only if run as "bash", not "sh" */ | ||
| 5577 | /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html | ||
| 5578 | * "2.7 Redirection | ||
| 5579 | * ...the word that follows the redirection operator | ||
| 5580 | * shall be subjected to tilde expansion, parameter expansion, | ||
| 5581 | * command substitution, arithmetic expansion, and quote | ||
| 5582 | * removal. Pathname expansion shall not be performed | ||
| 5583 | * on the word by a non-interactive shell; an interactive | ||
| 5584 | * shell may perform it, but shall do so only when | ||
| 5585 | * the expansion would result in one word." | ||
| 5586 | */ | ||
| 5587 | ctx->pending_redirect->rd_filename = xstrdup(word->data); | ||
| 5588 | /* Cater for >\file case: | ||
| 5589 | * >\a creates file a; >\\a, >"\a", >"\\a" create file \a | ||
| 5590 | * Same with heredocs: | ||
| 5591 | * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H | ||
| 5592 | */ | ||
| 5593 | if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) { | ||
| 5594 | unbackslash(ctx->pending_redirect->rd_filename); | ||
| 5595 | /* Is it <<"HEREDOC"? */ | ||
| 5596 | if (word->has_quoted_part) { | ||
| 5597 | ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED; | ||
| 5598 | } | ||
| 5599 | } | ||
| 5600 | debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); | ||
| 5601 | ctx->pending_redirect = NULL; | ||
| 5602 | } else { | ||
| 5603 | /* If this word wasn't an assignment, next ones definitely | ||
| 5604 | * can't be assignments. Even if they look like ones. */ | ||
| 5605 | if (word->o_assignment != DEFINITELY_ASSIGNMENT | ||
| 5606 | && word->o_assignment != WORD_IS_KEYWORD | ||
| 5607 | ) { | ||
| 5608 | word->o_assignment = NOT_ASSIGNMENT; | ||
| 5609 | } else { | ||
| 5610 | if (word->o_assignment == DEFINITELY_ASSIGNMENT) | ||
| 5611 | command->assignment_cnt++; | ||
| 5612 | word->o_assignment = MAYBE_ASSIGNMENT; | ||
| 5613 | } | ||
| 5614 | |||
| 5615 | #if HAS_KEYWORDS | ||
| 5616 | # if ENABLE_HUSH_CASE | ||
| 5617 | if (ctx->ctx_dsemicolon | ||
| 5618 | && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */ | ||
| 5619 | ) { | ||
| 5620 | /* already done when ctx_dsemicolon was set to 1: */ | ||
| 5621 | /* ctx->ctx_res_w = RES_MATCH; */ | ||
| 5622 | ctx->ctx_dsemicolon = 0; | ||
| 5623 | } else | ||
| 5624 | # endif | ||
| 5625 | if (!command->argv /* if it's the first word... */ | ||
| 5626 | # if ENABLE_HUSH_LOOPS | ||
| 5627 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ | ||
| 5628 | && ctx->ctx_res_w != RES_IN | ||
| 5629 | # endif | ||
| 5630 | # if ENABLE_HUSH_CASE | ||
| 5631 | && ctx->ctx_res_w != RES_CASE | ||
| 5632 | # endif | ||
| 5633 | ) { | ||
| 5634 | debug_printf_parse("checking '%s' for reserved-ness\n", word->data); | ||
| 5635 | if (reserved_word(word, ctx)) { | ||
| 5636 | o_reset_to_empty_unquoted(word); | ||
| 5637 | debug_printf_parse("done_word return %d\n", | ||
| 5638 | (ctx->ctx_res_w == RES_SNTX)); | ||
| 5639 | return (ctx->ctx_res_w == RES_SNTX); | ||
| 5640 | } | ||
| 5641 | # ifdef CMD_SINGLEWORD_NOGLOB_COND | ||
| 5642 | if (strcmp(word->data, "export") == 0 | ||
| 5643 | # if ENABLE_HUSH_LOCAL | ||
| 5644 | || strcmp(word->data, "local") == 0 | ||
| 5645 | # endif | ||
| 5646 | ) { | ||
| 5647 | command->cmd_type = CMD_SINGLEWORD_NOGLOB_COND; | ||
| 5648 | } else | ||
| 5649 | # endif | ||
| 5650 | # if ENABLE_HUSH_BASH_COMPAT | ||
| 5651 | if (strcmp(word->data, "[[") == 0) { | ||
| 5652 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | ||
| 5653 | } | ||
| 5654 | /* fall through */ | ||
| 5655 | # endif | ||
| 5656 | } | ||
| 5657 | #endif | ||
| 5658 | if (command->group) { | ||
| 5659 | /* "{ echo foo; } echo bar" - bad */ | ||
| 5660 | syntax_error_at(word->data); | ||
| 5661 | debug_printf_parse("done_word return 1: syntax error, " | ||
| 5662 | "groups and arglists don't mix\n"); | ||
| 5663 | return 1; | ||
| 5664 | } | ||
| 5665 | if (word->has_quoted_part | ||
| 5666 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ | ||
| 5667 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) | ||
| 5668 | /* (otherwise it's known to be not empty and is already safe) */ | ||
| 5669 | ) { | ||
| 5670 | /* exclude "$@" - it can expand to no word despite "" */ | ||
| 5671 | char *p = word->data; | ||
| 5672 | while (p[0] == SPECIAL_VAR_SYMBOL | ||
| 5673 | && (p[1] & 0x7f) == '@' | ||
| 5674 | && p[2] == SPECIAL_VAR_SYMBOL | ||
| 5675 | ) { | ||
| 5676 | p += 3; | ||
| 5677 | } | ||
| 5678 | if (p == word->data || p[0] != '\0') { | ||
| 5679 | /* saw no "$@", or not only "$@" but some | ||
| 5680 | * real text is there too */ | ||
| 5681 | /* insert "empty variable" reference, this makes | ||
| 5682 | * e.g. "", $empty"" etc to not disappear */ | ||
| 5683 | o_addchr(word, SPECIAL_VAR_SYMBOL); | ||
| 5684 | o_addchr(word, SPECIAL_VAR_SYMBOL); | ||
| 5685 | } | ||
| 5686 | } | ||
| 5687 | command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); | ||
| 5688 | debug_print_strings("word appended to argv", command->argv); | ||
| 5689 | } | ||
| 5690 | |||
| 5691 | #if ENABLE_HUSH_LOOPS | ||
| 5692 | if (ctx->ctx_res_w == RES_FOR) { | ||
| 5693 | if (word->has_quoted_part | ||
| 5694 | || !is_well_formed_var_name(command->argv[0], '\0') | ||
| 5695 | ) { | ||
| 5696 | /* bash says just "not a valid identifier" */ | ||
| 5697 | syntax_error("not a valid identifier in for"); | ||
| 5698 | return 1; | ||
| 5699 | } | ||
| 5700 | /* Force FOR to have just one word (variable name) */ | ||
| 5701 | /* NB: basically, this makes hush see "for v in ..." | ||
| 5702 | * syntax as if it is "for v; in ...". FOR and IN become | ||
| 5703 | * two pipe structs in parse tree. */ | ||
| 5704 | done_pipe(ctx, PIPE_SEQ); | ||
| 5705 | } | ||
| 5706 | #endif | ||
| 5707 | #if ENABLE_HUSH_CASE | ||
| 5708 | /* Force CASE to have just one word */ | ||
| 5709 | if (ctx->ctx_res_w == RES_CASE) { | ||
| 5710 | done_pipe(ctx, PIPE_SEQ); | ||
| 5711 | } | ||
| 5712 | #endif | ||
| 5713 | |||
| 5714 | o_reset_to_empty_unquoted(word); | ||
| 5715 | |||
| 5716 | debug_printf_parse("done_word return 0\n"); | ||
| 5717 | return 0; | ||
| 5718 | } | ||
| 5719 | |||
| 5720 | |||
| 5721 | /* Peek ahead in the input to find out if we have a "&n" construct, | ||
| 5722 | * as in "2>&1", that represents duplicating a file descriptor. | ||
| 5723 | * Return: | ||
| 5724 | * REDIRFD_CLOSE if >&- "close fd" construct is seen, | ||
| 5725 | * REDIRFD_SYNTAX_ERR if syntax error, | ||
| 5726 | * REDIRFD_TO_FILE if no & was seen, | ||
| 5727 | * or the number found. | ||
| 5728 | */ | ||
| 5729 | #if BB_MMU | ||
| 5730 | #define parse_redir_right_fd(as_string, input) \ | ||
| 5731 | parse_redir_right_fd(input) | ||
| 5732 | #endif | ||
| 5733 | static int parse_redir_right_fd(o_string *as_string, struct in_str *input) | ||
| 5734 | { | ||
| 5735 | int ch, d, ok; | ||
| 5736 | |||
| 5737 | ch = i_peek(input); | ||
| 5738 | if (ch != '&') | ||
| 5739 | return REDIRFD_TO_FILE; | ||
| 5740 | |||
| 5741 | ch = i_getch(input); /* get the & */ | ||
| 5742 | nommu_addchr(as_string, ch); | ||
| 5743 | ch = i_peek(input); | ||
| 5744 | if (ch == '-') { | ||
| 5745 | ch = i_getch(input); | ||
| 5746 | nommu_addchr(as_string, ch); | ||
| 5747 | return REDIRFD_CLOSE; | ||
| 5748 | } | ||
| 5749 | d = 0; | ||
| 5750 | ok = 0; | ||
| 5751 | while (ch != EOF && isdigit(ch)) { | ||
| 5752 | d = d*10 + (ch-'0'); | ||
| 5753 | ok = 1; | ||
| 5754 | ch = i_getch(input); | ||
| 5755 | nommu_addchr(as_string, ch); | ||
| 5756 | ch = i_peek(input); | ||
| 5757 | } | ||
| 5758 | if (ok) return d; | ||
| 5759 | |||
| 5760 | //TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2) | ||
| 5761 | |||
| 5762 | bb_error_msg("ambiguous redirect"); | ||
| 5763 | return REDIRFD_SYNTAX_ERR; | ||
| 5764 | } | ||
| 5765 | |||
| 5766 | /* Return code is 0 normal, 1 if a syntax error is detected | ||
| 5767 | */ | ||
| 5768 | static int parse_redirect(struct parse_context *ctx, | ||
| 5769 | int fd, | ||
| 5770 | redir_type style, | ||
| 5771 | struct in_str *input) | ||
| 5772 | { | ||
| 5773 | struct command *command = ctx->command; | ||
| 5774 | struct redir_struct *redir; | ||
| 5775 | struct redir_struct **redirp; | ||
| 5776 | int dup_num; | ||
| 5777 | |||
| 5778 | dup_num = REDIRFD_TO_FILE; | ||
| 5779 | if (style != REDIRECT_HEREDOC) { | ||
| 5780 | /* Check for a '>&1' type redirect */ | ||
| 5781 | dup_num = parse_redir_right_fd(&ctx->as_string, input); | ||
| 5782 | if (dup_num == REDIRFD_SYNTAX_ERR) | ||
| 5783 | return 1; | ||
| 5784 | } else { | ||
| 5785 | int ch = i_peek(input); | ||
| 5786 | dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ | ||
| 5787 | if (dup_num) { /* <<-... */ | ||
| 5788 | ch = i_getch(input); | ||
| 5789 | nommu_addchr(&ctx->as_string, ch); | ||
| 5790 | ch = i_peek(input); | ||
| 5791 | } | ||
| 5792 | } | ||
| 5793 | |||
| 5794 | if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) { | ||
| 5795 | int ch = i_peek(input); | ||
| 5796 | if (ch == '|') { | ||
| 5797 | /* >|FILE redirect ("clobbering" >). | ||
| 5798 | * Since we do not support "set -o noclobber" yet, | ||
| 5799 | * >| and > are the same for now. Just eat |. | ||
| 5800 | */ | ||
| 5801 | ch = i_getch(input); | ||
| 5802 | nommu_addchr(&ctx->as_string, ch); | ||
| 5803 | } | ||
| 5804 | } | ||
| 5805 | |||
| 5806 | /* Create a new redir_struct and append it to the linked list */ | ||
| 5807 | redirp = &command->redirects; | ||
| 5808 | while ((redir = *redirp) != NULL) { | ||
| 5809 | redirp = &(redir->next); | ||
| 5810 | } | ||
| 5811 | *redirp = redir = xzalloc(sizeof(*redir)); | ||
| 5812 | /* redir->next = NULL; */ | ||
| 5813 | /* redir->rd_filename = NULL; */ | ||
| 5814 | redir->rd_type = style; | ||
| 5815 | redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd; | ||
| 5816 | |||
| 5817 | debug_printf_parse("redirect type %d %s\n", redir->rd_fd, | ||
| 5818 | redir_table[style].descrip); | ||
| 5819 | |||
| 5820 | redir->rd_dup = dup_num; | ||
| 5821 | if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) { | ||
| 5822 | /* Erik had a check here that the file descriptor in question | ||
| 5823 | * is legit; I postpone that to "run time" | ||
| 5824 | * A "-" representation of "close me" shows up as a -3 here */ | ||
| 5825 | debug_printf_parse("duplicating redirect '%d>&%d'\n", | ||
| 5826 | redir->rd_fd, redir->rd_dup); | ||
| 5827 | } else { | ||
| 5828 | /* Set ctx->pending_redirect, so we know what to do at the | ||
| 5829 | * end of the next parsed word. */ | ||
| 5830 | ctx->pending_redirect = redir; | ||
| 5831 | } | ||
| 5832 | return 0; | ||
| 5833 | } | ||
| 5834 | |||
| 5835 | /* If a redirect is immediately preceded by a number, that number is | ||
| 5836 | * supposed to tell which file descriptor to redirect. This routine | ||
| 5837 | * looks for such preceding numbers. In an ideal world this routine | ||
| 5838 | * needs to handle all the following classes of redirects... | ||
| 5839 | * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo | ||
| 5840 | * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo | ||
| 5841 | * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo | ||
| 5842 | * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo | ||
| 5843 | * | ||
| 5844 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html | ||
| 5845 | * "2.7 Redirection | ||
| 5846 | * ... If n is quoted, the number shall not be recognized as part of | ||
| 5847 | * the redirection expression. For example: | ||
| 5848 | * echo \2>a | ||
| 5849 | * writes the character 2 into file a" | ||
| 5850 | * We are getting it right by setting ->has_quoted_part on any \<char> | ||
| 5851 | * | ||
| 5852 | * A -1 return means no valid number was found, | ||
| 5853 | * the caller should use the appropriate default for this redirection. | ||
| 5854 | */ | ||
| 5855 | static int redirect_opt_num(o_string *o) | ||
| 5856 | { | ||
| 5857 | int num; | ||
| 5858 | |||
| 5859 | if (o->data == NULL) | ||
| 5860 | return -1; | ||
| 5861 | num = bb_strtou(o->data, NULL, 10); | ||
| 5862 | if (errno || num < 0) | ||
| 5863 | return -1; | ||
| 5864 | o_reset_to_empty_unquoted(o); | ||
| 5865 | return num; | ||
| 5866 | } | ||
| 5867 | |||
| 5868 | #if BB_MMU | ||
| 5869 | #define fetch_till_str(as_string, input, word, skip_tabs) \ | ||
| 5870 | fetch_till_str(input, word, skip_tabs) | ||
| 5871 | #endif | ||
| 5872 | static char *fetch_till_str(o_string *as_string, | ||
| 5873 | struct in_str *input, | ||
| 5874 | const char *word, | ||
| 5875 | int skip_tabs) | ||
| 5876 | { | ||
| 5877 | o_string heredoc = NULL_O_STRING; | ||
| 5878 | int past_EOL = 0; | ||
| 5879 | int ch; | ||
| 5880 | |||
| 5881 | goto jump_in; | ||
| 5882 | while (1) { | ||
| 5883 | ch = i_getch(input); | ||
| 5884 | nommu_addchr(as_string, ch); | ||
| 5885 | if (ch == '\n') { | ||
| 5886 | if (strcmp(heredoc.data + past_EOL, word) == 0) { | ||
| 5887 | heredoc.data[past_EOL] = '\0'; | ||
| 5888 | debug_printf_parse("parsed heredoc '%s'\n", heredoc.data); | ||
| 5889 | return heredoc.data; | ||
| 5890 | } | ||
| 5891 | do { | ||
| 5892 | o_addchr(&heredoc, ch); | ||
| 5893 | past_EOL = heredoc.length; | ||
| 5894 | jump_in: | ||
| 5895 | do { | ||
| 5896 | ch = i_getch(input); | ||
| 5897 | nommu_addchr(as_string, ch); | ||
| 5898 | } while (skip_tabs && ch == '\t'); | ||
| 5899 | } while (ch == '\n'); | ||
| 5900 | } | ||
| 5901 | if (ch == EOF) { | ||
| 5902 | o_free_unsafe(&heredoc); | ||
| 5903 | return NULL; | ||
| 5904 | } | ||
| 5905 | o_addchr(&heredoc, ch); | ||
| 5906 | nommu_addchr(as_string, ch); | ||
| 5907 | } | ||
| 5908 | } | ||
| 5909 | |||
| 5910 | /* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs | ||
| 5911 | * and load them all. There should be exactly heredoc_cnt of them. | ||
| 5912 | */ | ||
| 5913 | static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input) | ||
| 5914 | { | ||
| 5915 | struct pipe *pi = ctx->list_head; | ||
| 5916 | |||
| 5917 | while (pi && heredoc_cnt) { | ||
| 5918 | int i; | ||
| 5919 | struct command *cmd = pi->cmds; | ||
| 5920 | |||
| 5921 | debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n", | ||
| 5922 | pi->num_cmds, | ||
| 5923 | cmd->argv ? cmd->argv[0] : "NONE"); | ||
| 5924 | for (i = 0; i < pi->num_cmds; i++) { | ||
| 5925 | struct redir_struct *redir = cmd->redirects; | ||
| 5926 | |||
| 5927 | debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n", | ||
| 5928 | i, cmd->argv ? cmd->argv[0] : "NONE"); | ||
| 5929 | while (redir) { | ||
| 5930 | if (redir->rd_type == REDIRECT_HEREDOC) { | ||
| 5931 | char *p; | ||
| 5932 | |||
| 5933 | redir->rd_type = REDIRECT_HEREDOC2; | ||
| 5934 | /* redir->rd_dup is (ab)used to indicate <<- */ | ||
| 5935 | p = fetch_till_str(&ctx->as_string, input, | ||
| 5936 | redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS); | ||
| 5937 | if (!p) { | ||
| 5938 | syntax_error("unexpected EOF in here document"); | ||
| 5939 | return 1; | ||
| 5940 | } | ||
| 5941 | free(redir->rd_filename); | ||
| 5942 | redir->rd_filename = p; | ||
| 5943 | heredoc_cnt--; | ||
| 5944 | } | ||
| 5945 | redir = redir->next; | ||
| 5946 | } | ||
| 5947 | cmd++; | ||
| 5948 | } | ||
| 5949 | pi = pi->next; | ||
| 5950 | } | ||
| 5951 | #if 0 | ||
| 5952 | /* Should be 0. If it isn't, it's a parse error */ | ||
| 5953 | if (heredoc_cnt) | ||
| 5954 | bb_error_msg_and_die("heredoc BUG 2"); | ||
| 5955 | #endif | ||
| 5956 | return 0; | ||
| 5957 | } | ||
| 5958 | |||
| 5959 | |||
| 5960 | #if ENABLE_HUSH_TICK | ||
| 5961 | static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | ||
| 5962 | { | ||
| 5963 | pid_t pid; | ||
| 5964 | int channel[2]; | ||
| 5965 | # if !BB_MMU | ||
| 5966 | char **to_free = NULL; | ||
| 5967 | # endif | ||
| 5968 | |||
| 5969 | xpipe(channel); | ||
| 5970 | pid = BB_MMU ? xfork() : xvfork(); | ||
| 5971 | if (pid == 0) { /* child */ | ||
| 5972 | disable_restore_tty_pgrp_on_exit(); | ||
| 5973 | /* Process substitution is not considered to be usual | ||
| 5974 | * 'command execution'. | ||
| 5975 | * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. | ||
| 5976 | */ | ||
| 5977 | bb_signals(0 | ||
| 5978 | + (1 << SIGTSTP) | ||
| 5979 | + (1 << SIGTTIN) | ||
| 5980 | + (1 << SIGTTOU) | ||
| 5981 | , SIG_IGN); | ||
| 5982 | CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ | ||
| 5983 | close(channel[0]); /* NB: close _first_, then move fd! */ | ||
| 5984 | xmove_fd(channel[1], 1); | ||
| 5985 | /* Prevent it from trying to handle ctrl-z etc */ | ||
| 5986 | IF_HUSH_JOB(G.run_list_level = 1;) | ||
| 5987 | /* Awful hack for `trap` or $(trap). | ||
| 5988 | * | ||
| 5989 | * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html | ||
| 5990 | * contains an example where "trap" is executed in a subshell: | ||
| 5991 | * | ||
| 5992 | * save_traps=$(trap) | ||
| 5993 | * ... | ||
| 5994 | * eval "$save_traps" | ||
| 5995 | * | ||
| 5996 | * Standard does not say that "trap" in subshell shall print | ||
| 5997 | * parent shell's traps. It only says that its output | ||
| 5998 | * must have suitable form, but then, in the above example | ||
| 5999 | * (which is not supposed to be normative), it implies that. | ||
| 6000 | * | ||
| 6001 | * bash (and probably other shell) does implement it | ||
| 6002 | * (traps are reset to defaults, but "trap" still shows them), | ||
| 6003 | * but as a result, "trap" logic is hopelessly messed up: | ||
| 6004 | * | ||
| 6005 | * # trap | ||
| 6006 | * trap -- 'echo Ho' SIGWINCH <--- we have a handler | ||
| 6007 | * # (trap) <--- trap is in subshell - no output (correct, traps are reset) | ||
| 6008 | * # true | trap <--- trap is in subshell - no output (ditto) | ||
| 6009 | * # echo `true | trap` <--- in subshell - output (but traps are reset!) | ||
| 6010 | * trap -- 'echo Ho' SIGWINCH | ||
| 6011 | * # echo `(trap)` <--- in subshell in subshell - output | ||
| 6012 | * trap -- 'echo Ho' SIGWINCH | ||
| 6013 | * # echo `true | (trap)` <--- in subshell in subshell in subshell - output! | ||
| 6014 | * trap -- 'echo Ho' SIGWINCH | ||
| 6015 | * | ||
| 6016 | * The rules when to forget and when to not forget traps | ||
| 6017 | * get really complex and nonsensical. | ||
| 6018 | * | ||
| 6019 | * Our solution: ONLY bare $(trap) or `trap` is special. | ||
| 6020 | */ | ||
| 6021 | s = skip_whitespace(s); | ||
| 6022 | if (strncmp(s, "trap", 4) == 0 && (*skip_whitespace(s + 4) == '\0')) | ||
| 6023 | { | ||
| 6024 | static const char *const argv[] = { NULL, NULL }; | ||
| 6025 | builtin_trap((char**)argv); | ||
| 6026 | exit(0); /* not _exit() - we need to fflush */ | ||
| 6027 | } | ||
| 6028 | # if BB_MMU | ||
| 6029 | reset_traps_to_defaults(); | ||
| 6030 | parse_and_run_string(s); | ||
| 6031 | _exit(G.last_exitcode); | ||
| 6032 | # else | ||
| 6033 | /* We re-execute after vfork on NOMMU. This makes this script safe: | ||
| 6034 | * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG | ||
| 6035 | * huge=`cat BIG` # was blocking here forever | ||
| 6036 | * echo OK | ||
| 6037 | */ | ||
| 6038 | re_execute_shell(&to_free, | ||
| 6039 | s, | ||
| 6040 | G.global_argv[0], | ||
| 6041 | G.global_argv + 1, | ||
| 6042 | NULL); | ||
| 6043 | # endif | ||
| 6044 | } | ||
| 6045 | |||
| 6046 | /* parent */ | ||
| 6047 | *pid_p = pid; | ||
| 6048 | # if ENABLE_HUSH_FAST | ||
| 6049 | G.count_SIGCHLD++; | ||
| 6050 | //bb_error_msg("[%d] fork in generate_stream_from_string:" | ||
| 6051 | // " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", | ||
| 6052 | // getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); | ||
| 6053 | # endif | ||
| 6054 | enable_restore_tty_pgrp_on_exit(); | ||
| 6055 | # if !BB_MMU | ||
| 6056 | free(to_free); | ||
| 6057 | # endif | ||
| 6058 | close(channel[1]); | ||
| 6059 | close_on_exec_on(channel[0]); | ||
| 6060 | return xfdopen_for_read(channel[0]); | ||
| 6061 | } | ||
| 6062 | |||
| 6063 | /* Return code is exit status of the process that is run. */ | ||
| 6064 | static int process_command_subs(o_string *dest, const char *s) | ||
| 6065 | { | ||
| 6066 | FILE *fp; | ||
| 6067 | struct in_str pipe_str; | ||
| 6068 | pid_t pid; | ||
| 6069 | int status, ch, eol_cnt; | ||
| 6070 | |||
| 6071 | fp = generate_stream_from_string(s, &pid); | ||
| 6072 | |||
| 6073 | /* Now send results of command back into original context */ | ||
| 6074 | setup_file_in_str(&pipe_str, fp); | ||
| 6075 | eol_cnt = 0; | ||
| 6076 | while ((ch = i_getch(&pipe_str)) != EOF) { | ||
| 6077 | if (ch == '\n') { | ||
| 6078 | eol_cnt++; | ||
| 6079 | continue; | ||
| 6080 | } | ||
| 6081 | while (eol_cnt) { | ||
| 6082 | o_addchr(dest, '\n'); | ||
| 6083 | eol_cnt--; | ||
| 6084 | } | ||
| 6085 | o_addQchr(dest, ch); | ||
| 6086 | } | ||
| 6087 | |||
| 6088 | debug_printf("done reading from `cmd` pipe, closing it\n"); | ||
| 6089 | fclose(fp); | ||
| 6090 | /* We need to extract exitcode. Test case | ||
| 6091 | * "true; echo `sleep 1; false` $?" | ||
| 6092 | * should print 1 */ | ||
| 6093 | safe_waitpid(pid, &status, 0); | ||
| 6094 | debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status)); | ||
| 6095 | return WEXITSTATUS(status); | ||
| 6096 | } | ||
| 6097 | #endif /* ENABLE_HUSH_TICK */ | ||
| 6098 | |||
| 6099 | #if !ENABLE_HUSH_FUNCTIONS | ||
| 6100 | #define parse_group(dest, ctx, input, ch) \ | ||
| 6101 | parse_group(ctx, input, ch) | ||
| 6102 | #endif | ||
| 6103 | static int parse_group(o_string *dest, struct parse_context *ctx, | ||
| 6104 | struct in_str *input, int ch) | ||
| 6105 | { | ||
| 6106 | /* dest contains characters seen prior to ( or {. | ||
| 6107 | * Typically it's empty, but for function defs, | ||
| 6108 | * it contains function name (without '()'). */ | ||
| 6109 | struct pipe *pipe_list; | ||
| 6110 | int endch; | ||
| 6111 | struct command *command = ctx->command; | ||
| 6112 | |||
| 6113 | debug_printf_parse("parse_group entered\n"); | ||
| 6114 | #if ENABLE_HUSH_FUNCTIONS | ||
| 6115 | if (ch == '(' && !dest->has_quoted_part) { | ||
| 6116 | if (dest->length) | ||
| 6117 | if (done_word(dest, ctx)) | ||
| 6118 | return 1; | ||
| 6119 | if (!command->argv) | ||
| 6120 | goto skip; /* (... */ | ||
| 6121 | if (command->argv[1]) { /* word word ... (... */ | ||
| 6122 | syntax_error_unexpected_ch('('); | ||
| 6123 | return 1; | ||
| 6124 | } | ||
| 6125 | /* it is "word(..." or "word (..." */ | ||
| 6126 | do | ||
| 6127 | ch = i_getch(input); | ||
| 6128 | while (ch == ' ' || ch == '\t'); | ||
| 6129 | if (ch != ')') { | ||
| 6130 | syntax_error_unexpected_ch(ch); | ||
| 6131 | return 1; | ||
| 6132 | } | ||
| 6133 | nommu_addchr(&ctx->as_string, ch); | ||
| 6134 | do | ||
| 6135 | ch = i_getch(input); | ||
| 6136 | while (ch == ' ' || ch == '\t' || ch == '\n'); | ||
| 6137 | if (ch != '{') { | ||
| 6138 | syntax_error_unexpected_ch(ch); | ||
| 6139 | return 1; | ||
| 6140 | } | ||
| 6141 | nommu_addchr(&ctx->as_string, ch); | ||
| 6142 | command->cmd_type = CMD_FUNCDEF; | ||
| 6143 | goto skip; | ||
| 6144 | } | ||
| 6145 | #endif | ||
| 6146 | |||
| 6147 | #if 0 /* Prevented by caller */ | ||
| 6148 | if (command->argv /* word [word]{... */ | ||
| 6149 | || dest->length /* word{... */ | ||
| 6150 | || dest->has_quoted_part /* ""{... */ | ||
| 6151 | ) { | ||
| 6152 | syntax_error(NULL); | ||
| 6153 | debug_printf_parse("parse_group return 1: " | ||
| 6154 | "syntax error, groups and arglists don't mix\n"); | ||
| 6155 | return 1; | ||
| 6156 | } | ||
| 6157 | #endif | ||
| 6158 | |||
| 6159 | #if ENABLE_HUSH_FUNCTIONS | ||
| 6160 | skip: | ||
| 6161 | #endif | ||
| 6162 | endch = '}'; | ||
| 6163 | if (ch == '(') { | ||
| 6164 | endch = ')'; | ||
| 6165 | command->cmd_type = CMD_SUBSHELL; | ||
| 6166 | } else { | ||
| 6167 | /* bash does not allow "{echo...", requires whitespace */ | ||
| 6168 | ch = i_getch(input); | ||
| 6169 | if (ch != ' ' && ch != '\t' && ch != '\n') { | ||
| 6170 | syntax_error_unexpected_ch(ch); | ||
| 6171 | return 1; | ||
| 6172 | } | ||
| 6173 | nommu_addchr(&ctx->as_string, ch); | ||
| 6174 | } | ||
| 6175 | |||
| 6176 | { | ||
| 6177 | #if BB_MMU | ||
| 6178 | # define as_string NULL | ||
| 6179 | #else | ||
| 6180 | char *as_string = NULL; | ||
| 6181 | #endif | ||
| 6182 | pipe_list = parse_stream(&as_string, input, endch); | ||
| 6183 | #if !BB_MMU | ||
| 6184 | if (as_string) | ||
| 6185 | o_addstr(&ctx->as_string, as_string); | ||
| 6186 | #endif | ||
| 6187 | /* empty ()/{} or parse error? */ | ||
| 6188 | if (!pipe_list || pipe_list == ERR_PTR) { | ||
| 6189 | /* parse_stream already emitted error msg */ | ||
| 6190 | if (!BB_MMU) | ||
| 6191 | free(as_string); | ||
| 6192 | debug_printf_parse("parse_group return 1: " | ||
| 6193 | "parse_stream returned %p\n", pipe_list); | ||
| 6194 | return 1; | ||
| 6195 | } | ||
| 6196 | command->group = pipe_list; | ||
| 6197 | #if !BB_MMU | ||
| 6198 | as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */ | ||
| 6199 | command->group_as_string = as_string; | ||
| 6200 | debug_printf_parse("end of group, remembering as:'%s'\n", | ||
| 6201 | command->group_as_string); | ||
| 6202 | #endif | ||
| 6203 | #undef as_string | ||
| 6204 | } | ||
| 6205 | debug_printf_parse("parse_group return 0\n"); | ||
| 6206 | return 0; | ||
| 6207 | /* command remains "open", available for possible redirects */ | ||
| 6208 | } | ||
| 6209 | |||
| 6210 | #if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS | ||
| 6211 | /* Subroutines for copying $(...) and `...` things */ | ||
| 6212 | static void add_till_backquote(o_string *dest, struct in_str *input); | ||
| 6213 | /* '...' */ | ||
| 6214 | static void add_till_single_quote(o_string *dest, struct in_str *input) | ||
| 6215 | { | ||
| 6216 | while (1) { | ||
| 6217 | int ch = i_getch(input); | ||
| 6218 | if (ch == EOF) { | ||
| 6219 | syntax_error_unterm_ch('\''); | ||
| 6220 | /*xfunc_die(); - redundant */ | ||
| 6221 | } | ||
| 6222 | if (ch == '\'') | ||
| 6223 | return; | ||
| 6224 | o_addchr(dest, ch); | ||
| 6225 | } | ||
| 6226 | } | ||
| 6227 | /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ | ||
| 6228 | static void add_till_double_quote(o_string *dest, struct in_str *input) | ||
| 6229 | { | ||
| 6230 | while (1) { | ||
| 6231 | int ch = i_getch(input); | ||
| 6232 | if (ch == EOF) { | ||
| 6233 | syntax_error_unterm_ch('"'); | ||
| 6234 | /*xfunc_die(); - redundant */ | ||
| 6235 | } | ||
| 6236 | if (ch == '"') | ||
| 6237 | return; | ||
| 6238 | if (ch == '\\') { /* \x. Copy both chars. */ | ||
| 6239 | o_addchr(dest, ch); | ||
| 6240 | ch = i_getch(input); | ||
| 6241 | } | ||
| 6242 | o_addchr(dest, ch); | ||
| 6243 | if (ch == '`') { | ||
| 6244 | add_till_backquote(dest, input); | ||
| 6245 | o_addchr(dest, ch); | ||
| 6246 | continue; | ||
| 6247 | } | ||
| 6248 | //if (ch == '$') ... | ||
| 6249 | } | ||
| 6250 | } | ||
| 6251 | /* Process `cmd` - copy contents until "`" is seen. Complicated by | ||
| 6252 | * \` quoting. | ||
| 6253 | * "Within the backquoted style of command substitution, backslash | ||
| 6254 | * shall retain its literal meaning, except when followed by: '$', '`', or '\'. | ||
| 6255 | * The search for the matching backquote shall be satisfied by the first | ||
| 6256 | * backquote found without a preceding backslash; during this search, | ||
| 6257 | * if a non-escaped backquote is encountered within a shell comment, | ||
| 6258 | * a here-document, an embedded command substitution of the $(command) | ||
| 6259 | * form, or a quoted string, undefined results occur. A single-quoted | ||
| 6260 | * or double-quoted string that begins, but does not end, within the | ||
| 6261 | * "`...`" sequence produces undefined results." | ||
| 6262 | * Example Output | ||
| 6263 | * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST | ||
| 6264 | */ | ||
| 6265 | static void add_till_backquote(o_string *dest, struct in_str *input) | ||
| 6266 | { | ||
| 6267 | while (1) { | ||
| 6268 | int ch = i_getch(input); | ||
| 6269 | if (ch == EOF) { | ||
| 6270 | syntax_error_unterm_ch('`'); | ||
| 6271 | /*xfunc_die(); - redundant */ | ||
| 6272 | } | ||
| 6273 | if (ch == '`') | ||
| 6274 | return; | ||
| 6275 | if (ch == '\\') { | ||
| 6276 | /* \x. Copy both chars unless it is \` */ | ||
| 6277 | int ch2 = i_getch(input); | ||
| 6278 | if (ch2 == EOF) { | ||
| 6279 | syntax_error_unterm_ch('`'); | ||
| 6280 | /*xfunc_die(); - redundant */ | ||
| 6281 | } | ||
| 6282 | if (ch2 != '`' && ch2 != '$' && ch2 != '\\') | ||
| 6283 | o_addchr(dest, ch); | ||
| 6284 | ch = ch2; | ||
| 6285 | } | ||
| 6286 | o_addchr(dest, ch); | ||
| 6287 | } | ||
| 6288 | } | ||
| 6289 | /* Process $(cmd) - copy contents until ")" is seen. Complicated by | ||
| 6290 | * quoting and nested ()s. | ||
| 6291 | * "With the $(command) style of command substitution, all characters | ||
| 6292 | * following the open parenthesis to the matching closing parenthesis | ||
| 6293 | * constitute the command. Any valid shell script can be used for command, | ||
| 6294 | * except a script consisting solely of redirections which produces | ||
| 6295 | * unspecified results." | ||
| 6296 | * Example Output | ||
| 6297 | * echo $(echo '(TEST)' BEST) (TEST) BEST | ||
| 6298 | * echo $(echo 'TEST)' BEST) TEST) BEST | ||
| 6299 | * echo $(echo \(\(TEST\) BEST) ((TEST) BEST | ||
| 6300 | * | ||
| 6301 | * Also adapted to eat ${var%...} and $((...)) constructs, since ... part | ||
| 6302 | * can contain arbitrary constructs, just like $(cmd). | ||
| 6303 | * In bash compat mode, it needs to also be able to stop on ':' or '/' | ||
| 6304 | * for ${var:N[:M]} and ${var/P[/R]} parsing. | ||
| 6305 | */ | ||
| 6306 | #define DOUBLE_CLOSE_CHAR_FLAG 0x80 | ||
| 6307 | static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch) | ||
| 6308 | { | ||
| 6309 | int ch; | ||
| 6310 | char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; | ||
| 6311 | # if ENABLE_HUSH_BASH_COMPAT | ||
| 6312 | char end_char2 = end_ch >> 8; | ||
| 6313 | # endif | ||
| 6314 | end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); | ||
| 6315 | |||
| 6316 | while (1) { | ||
| 6317 | ch = i_getch(input); | ||
| 6318 | if (ch == EOF) { | ||
| 6319 | syntax_error_unterm_ch(end_ch); | ||
| 6320 | /*xfunc_die(); - redundant */ | ||
| 6321 | } | ||
| 6322 | if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { | ||
| 6323 | if (!dbl) | ||
| 6324 | break; | ||
| 6325 | /* we look for closing )) of $((EXPR)) */ | ||
| 6326 | if (i_peek(input) == end_ch) { | ||
| 6327 | i_getch(input); /* eat second ')' */ | ||
| 6328 | break; | ||
| 6329 | } | ||
| 6330 | } | ||
| 6331 | o_addchr(dest, ch); | ||
| 6332 | if (ch == '(' || ch == '{') { | ||
| 6333 | ch = (ch == '(' ? ')' : '}'); | ||
| 6334 | add_till_closing_bracket(dest, input, ch); | ||
| 6335 | o_addchr(dest, ch); | ||
| 6336 | continue; | ||
| 6337 | } | ||
| 6338 | if (ch == '\'') { | ||
| 6339 | add_till_single_quote(dest, input); | ||
| 6340 | o_addchr(dest, ch); | ||
| 6341 | continue; | ||
| 6342 | } | ||
| 6343 | if (ch == '"') { | ||
| 6344 | add_till_double_quote(dest, input); | ||
| 6345 | o_addchr(dest, ch); | ||
| 6346 | continue; | ||
| 6347 | } | ||
| 6348 | if (ch == '`') { | ||
| 6349 | add_till_backquote(dest, input); | ||
| 6350 | o_addchr(dest, ch); | ||
| 6351 | continue; | ||
| 6352 | } | ||
| 6353 | if (ch == '\\') { | ||
| 6354 | /* \x. Copy verbatim. Important for \(, \) */ | ||
| 6355 | ch = i_getch(input); | ||
| 6356 | if (ch == EOF) { | ||
| 6357 | syntax_error_unterm_ch(')'); | ||
| 6358 | /*xfunc_die(); - redundant */ | ||
| 6359 | } | ||
| 6360 | o_addchr(dest, ch); | ||
| 6361 | continue; | ||
| 6362 | } | ||
| 6363 | } | ||
| 6364 | return ch; | ||
| 6365 | } | ||
| 6366 | #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */ | ||
| 6367 | |||
| 6368 | /* Return code: 0 for OK, 1 for syntax error */ | ||
| 6369 | #if BB_MMU | ||
| 6370 | #define parse_dollar(as_string, dest, input) \ | ||
| 6371 | parse_dollar(dest, input) | ||
| 6372 | #define as_string NULL | ||
| 6373 | #endif | ||
| 6374 | static int parse_dollar(o_string *as_string, | ||
| 6375 | o_string *dest, | ||
| 6376 | struct in_str *input) | ||
| 6377 | { | ||
| 6378 | int ch = i_peek(input); /* first character after the $ */ | ||
| 6379 | unsigned char quote_mask = dest->o_escape ? 0x80 : 0; | ||
| 6380 | |||
| 6381 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); | ||
| 6382 | if (isalpha(ch)) { | ||
| 6383 | ch = i_getch(input); | ||
| 6384 | nommu_addchr(as_string, ch); | ||
| 6385 | make_var: | ||
| 6386 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6387 | while (1) { | ||
| 6388 | debug_printf_parse(": '%c'\n", ch); | ||
| 6389 | o_addchr(dest, ch | quote_mask); | ||
| 6390 | quote_mask = 0; | ||
| 6391 | ch = i_peek(input); | ||
| 6392 | if (!isalnum(ch) && ch != '_') | ||
| 6393 | break; | ||
| 6394 | ch = i_getch(input); | ||
| 6395 | nommu_addchr(as_string, ch); | ||
| 6396 | } | ||
| 6397 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6398 | } else if (isdigit(ch)) { | ||
| 6399 | make_one_char_var: | ||
| 6400 | ch = i_getch(input); | ||
| 6401 | nommu_addchr(as_string, ch); | ||
| 6402 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6403 | debug_printf_parse(": '%c'\n", ch); | ||
| 6404 | o_addchr(dest, ch | quote_mask); | ||
| 6405 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6406 | } else switch (ch) { | ||
| 6407 | case '$': /* pid */ | ||
| 6408 | case '!': /* last bg pid */ | ||
| 6409 | case '?': /* last exit code */ | ||
| 6410 | case '#': /* number of args */ | ||
| 6411 | case '*': /* args */ | ||
| 6412 | case '@': /* args */ | ||
| 6413 | goto make_one_char_var; | ||
| 6414 | case '{': { | ||
| 6415 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6416 | |||
| 6417 | ch = i_getch(input); /* eat '{' */ | ||
| 6418 | nommu_addchr(as_string, ch); | ||
| 6419 | |||
| 6420 | ch = i_getch(input); /* first char after '{' */ | ||
| 6421 | nommu_addchr(as_string, ch); | ||
| 6422 | /* It should be ${?}, or ${#var}, | ||
| 6423 | * or even ${?+subst} - operator acting on a special variable, | ||
| 6424 | * or the beginning of variable name. | ||
| 6425 | */ | ||
| 6426 | if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */ | ||
| 6427 | bad_dollar_syntax: | ||
| 6428 | syntax_error_unterm_str("${name}"); | ||
| 6429 | debug_printf_parse("parse_dollar return 1: unterminated ${name}\n"); | ||
| 6430 | return 1; | ||
| 6431 | } | ||
| 6432 | ch |= quote_mask; | ||
| 6433 | |||
| 6434 | /* It's possible to just call add_till_closing_bracket() at this point. | ||
| 6435 | * However, this regresses some of our testsuite cases | ||
| 6436 | * which check invalid constructs like ${%}. | ||
| 6437 | * Oh well... let's check that the var name part is fine... */ | ||
| 6438 | |||
| 6439 | while (1) { | ||
| 6440 | unsigned pos; | ||
| 6441 | |||
| 6442 | o_addchr(dest, ch); | ||
| 6443 | debug_printf_parse(": '%c'\n", ch); | ||
| 6444 | |||
| 6445 | ch = i_getch(input); | ||
| 6446 | nommu_addchr(as_string, ch); | ||
| 6447 | if (ch == '}') | ||
| 6448 | break; | ||
| 6449 | |||
| 6450 | if (!isalnum(ch) && ch != '_') { | ||
| 6451 | unsigned end_ch; | ||
| 6452 | unsigned char last_ch; | ||
| 6453 | /* handle parameter expansions | ||
| 6454 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 | ||
| 6455 | */ | ||
| 6456 | if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */ | ||
| 6457 | goto bad_dollar_syntax; | ||
| 6458 | |||
| 6459 | /* Eat everything until closing '}' (or ':') */ | ||
| 6460 | end_ch = '}'; | ||
| 6461 | if (ENABLE_HUSH_BASH_COMPAT | ||
| 6462 | && ch == ':' | ||
| 6463 | && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input)) | ||
| 6464 | ) { | ||
| 6465 | /* It's ${var:N[:M]} thing */ | ||
| 6466 | end_ch = '}' * 0x100 + ':'; | ||
| 6467 | } | ||
| 6468 | if (ENABLE_HUSH_BASH_COMPAT | ||
| 6469 | && ch == '/' | ||
| 6470 | ) { | ||
| 6471 | /* It's ${var/[/]pattern[/repl]} thing */ | ||
| 6472 | if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */ | ||
| 6473 | i_getch(input); | ||
| 6474 | nommu_addchr(as_string, '/'); | ||
| 6475 | ch = '\\'; | ||
| 6476 | } | ||
| 6477 | end_ch = '}' * 0x100 + '/'; | ||
| 6478 | } | ||
| 6479 | o_addchr(dest, ch); | ||
| 6480 | again: | ||
| 6481 | if (!BB_MMU) | ||
| 6482 | pos = dest->length; | ||
| 6483 | #if ENABLE_HUSH_DOLLAR_OPS | ||
| 6484 | last_ch = add_till_closing_bracket(dest, input, end_ch); | ||
| 6485 | #else | ||
| 6486 | #error Simple code to only allow ${var} is not implemented | ||
| 6487 | #endif | ||
| 6488 | if (as_string) { | ||
| 6489 | o_addstr(as_string, dest->data + pos); | ||
| 6490 | o_addchr(as_string, last_ch); | ||
| 6491 | } | ||
| 6492 | |||
| 6493 | if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) { | ||
| 6494 | /* close the first block: */ | ||
| 6495 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6496 | /* while parsing N from ${var:N[:M]} | ||
| 6497 | * or pattern from ${var/[/]pattern[/repl]} */ | ||
| 6498 | if ((end_ch & 0xff) == last_ch) { | ||
| 6499 | /* got ':' or '/'- parse the rest */ | ||
| 6500 | end_ch = '}'; | ||
| 6501 | goto again; | ||
| 6502 | } | ||
| 6503 | /* got '}' */ | ||
| 6504 | if (end_ch == '}' * 0x100 + ':') { | ||
| 6505 | /* it's ${var:N} - emulate :999999999 */ | ||
| 6506 | o_addstr(dest, "999999999"); | ||
| 6507 | } /* else: it's ${var/[/]pattern} */ | ||
| 6508 | } | ||
| 6509 | break; | ||
| 6510 | } | ||
| 6511 | } | ||
| 6512 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6513 | break; | ||
| 6514 | } | ||
| 6515 | #if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK | ||
| 6516 | case '(': { | ||
| 6517 | unsigned pos; | ||
| 6518 | |||
| 6519 | ch = i_getch(input); | ||
| 6520 | nommu_addchr(as_string, ch); | ||
| 6521 | # if ENABLE_SH_MATH_SUPPORT | ||
| 6522 | if (i_peek(input) == '(') { | ||
| 6523 | ch = i_getch(input); | ||
| 6524 | nommu_addchr(as_string, ch); | ||
| 6525 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6526 | o_addchr(dest, /*quote_mask |*/ '+'); | ||
| 6527 | if (!BB_MMU) | ||
| 6528 | pos = dest->length; | ||
| 6529 | add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG); | ||
| 6530 | if (as_string) { | ||
| 6531 | o_addstr(as_string, dest->data + pos); | ||
| 6532 | o_addchr(as_string, ')'); | ||
| 6533 | o_addchr(as_string, ')'); | ||
| 6534 | } | ||
| 6535 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6536 | break; | ||
| 6537 | } | ||
| 6538 | # endif | ||
| 6539 | # if ENABLE_HUSH_TICK | ||
| 6540 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6541 | o_addchr(dest, quote_mask | '`'); | ||
| 6542 | if (!BB_MMU) | ||
| 6543 | pos = dest->length; | ||
| 6544 | add_till_closing_bracket(dest, input, ')'); | ||
| 6545 | if (as_string) { | ||
| 6546 | o_addstr(as_string, dest->data + pos); | ||
| 6547 | o_addchr(as_string, ')'); | ||
| 6548 | } | ||
| 6549 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6550 | # endif | ||
| 6551 | break; | ||
| 6552 | } | ||
| 6553 | #endif | ||
| 6554 | case '_': | ||
| 6555 | ch = i_getch(input); | ||
| 6556 | nommu_addchr(as_string, ch); | ||
| 6557 | ch = i_peek(input); | ||
| 6558 | if (isalnum(ch)) { /* it's $_name or $_123 */ | ||
| 6559 | ch = '_'; | ||
| 6560 | goto make_var; | ||
| 6561 | } | ||
| 6562 | /* else: it's $_ */ | ||
| 6563 | /* TODO: $_ and $-: */ | ||
| 6564 | /* $_ Shell or shell script name; or last argument of last command | ||
| 6565 | * (if last command wasn't a pipe; if it was, bash sets $_ to ""); | ||
| 6566 | * but in command's env, set to full pathname used to invoke it */ | ||
| 6567 | /* $- Option flags set by set builtin or shell options (-i etc) */ | ||
| 6568 | default: | ||
| 6569 | o_addQchr(dest, '$'); | ||
| 6570 | } | ||
| 6571 | debug_printf_parse("parse_dollar return 0\n"); | ||
| 6572 | return 0; | ||
| 6573 | #undef as_string | ||
| 6574 | } | ||
| 6575 | |||
| 6576 | #if BB_MMU | ||
| 6577 | #define parse_stream_dquoted(as_string, dest, input, dquote_end) \ | ||
| 6578 | parse_stream_dquoted(dest, input, dquote_end) | ||
| 6579 | #define as_string NULL | ||
| 6580 | #endif | ||
| 6581 | static int parse_stream_dquoted(o_string *as_string, | ||
| 6582 | o_string *dest, | ||
| 6583 | struct in_str *input, | ||
| 6584 | int dquote_end) | ||
| 6585 | { | ||
| 6586 | int ch; | ||
| 6587 | int next; | ||
| 6588 | |||
| 6589 | again: | ||
| 6590 | ch = i_getch(input); | ||
| 6591 | if (ch != EOF) | ||
| 6592 | nommu_addchr(as_string, ch); | ||
| 6593 | if (ch == dquote_end) { /* may be only '"' or EOF */ | ||
| 6594 | if (dest->o_assignment == NOT_ASSIGNMENT) | ||
| 6595 | dest->o_escape ^= 1; | ||
| 6596 | debug_printf_parse("parse_stream_dquoted return 0\n"); | ||
| 6597 | return 0; | ||
| 6598 | } | ||
| 6599 | /* note: can't move it above ch == dquote_end check! */ | ||
| 6600 | if (ch == EOF) { | ||
| 6601 | syntax_error_unterm_ch('"'); | ||
| 6602 | /*xfunc_die(); - redundant */ | ||
| 6603 | } | ||
| 6604 | next = '\0'; | ||
| 6605 | if (ch != '\n') { | ||
| 6606 | next = i_peek(input); | ||
| 6607 | } | ||
| 6608 | debug_printf_parse("\" ch=%c (%d) escape=%d\n", | ||
| 6609 | ch, ch, dest->o_escape); | ||
| 6610 | if (ch == '\\') { | ||
| 6611 | if (next == EOF) { | ||
| 6612 | syntax_error("\\<eof>"); | ||
| 6613 | xfunc_die(); | ||
| 6614 | } | ||
| 6615 | /* bash: | ||
| 6616 | * "The backslash retains its special meaning [in "..."] | ||
| 6617 | * only when followed by one of the following characters: | ||
| 6618 | * $, `, ", \, or <newline>. A double quote may be quoted | ||
| 6619 | * within double quotes by preceding it with a backslash." | ||
| 6620 | */ | ||
| 6621 | if (strchr("$`\"\\\n", next) != NULL) { | ||
| 6622 | ch = i_getch(input); | ||
| 6623 | if (ch != '\n') { | ||
| 6624 | o_addqchr(dest, ch); | ||
| 6625 | nommu_addchr(as_string, ch); | ||
| 6626 | } | ||
| 6627 | } else { | ||
| 6628 | o_addqchr(dest, '\\'); | ||
| 6629 | nommu_addchr(as_string, '\\'); | ||
| 6630 | } | ||
| 6631 | goto again; | ||
| 6632 | } | ||
| 6633 | if (ch == '$') { | ||
| 6634 | if (parse_dollar(as_string, dest, input) != 0) { | ||
| 6635 | debug_printf_parse("parse_stream_dquoted return 1: " | ||
| 6636 | "parse_dollar returned non-0\n"); | ||
| 6637 | return 1; | ||
| 6638 | } | ||
| 6639 | goto again; | ||
| 6640 | } | ||
| 6641 | #if ENABLE_HUSH_TICK | ||
| 6642 | if (ch == '`') { | ||
| 6643 | //unsigned pos = dest->length; | ||
| 6644 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6645 | o_addchr(dest, 0x80 | '`'); | ||
| 6646 | add_till_backquote(dest, input); | ||
| 6647 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 6648 | //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); | ||
| 6649 | goto again; | ||
| 6650 | } | ||
| 6651 | #endif | ||
| 6652 | o_addQchr(dest, ch); | ||
| 6653 | if (ch == '=' | ||
| 6654 | && (dest->o_assignment == MAYBE_ASSIGNMENT | ||
| 6655 | || dest->o_assignment == WORD_IS_KEYWORD) | ||
| 6656 | && is_well_formed_var_name(dest->data, '=') | ||
| 6657 | ) { | ||
| 6658 | dest->o_assignment = DEFINITELY_ASSIGNMENT; | ||
| 6659 | } | ||
| 6660 | goto again; | ||
| 6661 | #undef as_string | ||
| 6662 | } | ||
| 6663 | |||
| 6664 | /* | ||
| 6665 | * Scan input until EOF or end_trigger char. | ||
| 6666 | * Return a list of pipes to execute, or NULL on EOF | ||
| 6667 | * or if end_trigger character is met. | ||
| 6668 | * On syntax error, exit is shell is not interactive, | ||
| 6669 | * reset parsing machinery and start parsing anew, | ||
| 6670 | * or return ERR_PTR. | ||
| 6671 | */ | ||
| 6672 | static struct pipe *parse_stream(char **pstring, | ||
| 6673 | struct in_str *input, | ||
| 6674 | int end_trigger) | ||
| 6675 | { | ||
| 6676 | struct parse_context ctx; | ||
| 6677 | o_string dest = NULL_O_STRING; | ||
| 6678 | int is_in_dquote; | ||
| 6679 | int heredoc_cnt; | ||
| 6680 | |||
| 6681 | /* Double-quote state is handled in the state variable is_in_dquote. | ||
| 6682 | * A single-quote triggers a bypass of the main loop until its mate is | ||
| 6683 | * found. When recursing, quote state is passed in via dest->o_escape. | ||
| 6684 | */ | ||
| 6685 | debug_printf_parse("parse_stream entered, end_trigger='%c'\n", | ||
| 6686 | end_trigger ? end_trigger : 'X'); | ||
| 6687 | debug_enter(); | ||
| 6688 | |||
| 6689 | /* If very first arg is "" or '', dest.data may end up NULL. | ||
| 6690 | * Preventing this: */ | ||
| 6691 | o_addchr(&dest, '\0'); | ||
| 6692 | dest.length = 0; | ||
| 6693 | |||
| 6694 | G.ifs = get_local_var_value("IFS"); | ||
| 6695 | if (G.ifs == NULL) | ||
| 6696 | G.ifs = defifs; | ||
| 6697 | |||
| 6698 | reset: | ||
| 6699 | #if ENABLE_HUSH_INTERACTIVE | ||
| 6700 | input->promptmode = 0; /* PS1 */ | ||
| 6701 | #endif | ||
| 6702 | /* dest.o_assignment = MAYBE_ASSIGNMENT; - already is */ | ||
| 6703 | initialize_context(&ctx); | ||
| 6704 | is_in_dquote = 0; | ||
| 6705 | heredoc_cnt = 0; | ||
| 6706 | while (1) { | ||
| 6707 | const char *is_ifs; | ||
| 6708 | const char *is_special; | ||
| 6709 | int ch; | ||
| 6710 | int next; | ||
| 6711 | int redir_fd; | ||
| 6712 | redir_type redir_style; | ||
| 6713 | |||
| 6714 | if (is_in_dquote) { | ||
| 6715 | /* dest.has_quoted_part = 1; - already is (see below) */ | ||
| 6716 | if (parse_stream_dquoted(&ctx.as_string, &dest, input, '"')) { | ||
| 6717 | goto parse_error; | ||
| 6718 | } | ||
| 6719 | /* We reached closing '"' */ | ||
| 6720 | is_in_dquote = 0; | ||
| 6721 | } | ||
| 6722 | ch = i_getch(input); | ||
| 6723 | debug_printf_parse(": ch=%c (%d) escape=%d\n", | ||
| 6724 | ch, ch, dest.o_escape); | ||
| 6725 | if (ch == EOF) { | ||
| 6726 | struct pipe *pi; | ||
| 6727 | |||
| 6728 | if (heredoc_cnt) { | ||
| 6729 | syntax_error_unterm_str("here document"); | ||
| 6730 | goto parse_error; | ||
| 6731 | } | ||
| 6732 | /* end_trigger == '}' case errors out earlier, | ||
| 6733 | * checking only ')' */ | ||
| 6734 | if (end_trigger == ')') { | ||
| 6735 | syntax_error_unterm_ch('('); /* exits */ | ||
| 6736 | /* goto parse_error; */ | ||
| 6737 | } | ||
| 6738 | |||
| 6739 | if (done_word(&dest, &ctx)) { | ||
| 6740 | goto parse_error; | ||
| 6741 | } | ||
| 6742 | o_free(&dest); | ||
| 6743 | done_pipe(&ctx, PIPE_SEQ); | ||
| 6744 | pi = ctx.list_head; | ||
| 6745 | /* If we got nothing... */ | ||
| 6746 | /* (this makes bare "&" cmd a no-op. | ||
| 6747 | * bash says: "syntax error near unexpected token '&'") */ | ||
| 6748 | if (pi->num_cmds == 0 | ||
| 6749 | IF_HAS_KEYWORDS( && pi->res_word == RES_NONE) | ||
| 6750 | ) { | ||
| 6751 | free_pipe_list(pi); | ||
| 6752 | pi = NULL; | ||
| 6753 | } | ||
| 6754 | #if !BB_MMU | ||
| 6755 | debug_printf_parse("as_string '%s'\n", ctx.as_string.data); | ||
| 6756 | if (pstring) | ||
| 6757 | *pstring = ctx.as_string.data; | ||
| 6758 | else | ||
| 6759 | o_free_unsafe(&ctx.as_string); | ||
| 6760 | #endif | ||
| 6761 | debug_leave(); | ||
| 6762 | debug_printf_parse("parse_stream return %p\n", pi); | ||
| 6763 | return pi; | ||
| 6764 | } | ||
| 6765 | nommu_addchr(&ctx.as_string, ch); | ||
| 6766 | |||
| 6767 | next = '\0'; | ||
| 6768 | if (ch != '\n') | ||
| 6769 | next = i_peek(input); | ||
| 6770 | |||
| 6771 | is_special = "{}<>;&|()#'" /* special outside of "str" */ | ||
| 6772 | "\\$\"" IF_HUSH_TICK("`"); /* always special */ | ||
| 6773 | /* Are { and } special here? */ | ||
| 6774 | if (ctx.command->argv /* word [word]{... - non-special */ | ||
| 6775 | || dest.length /* word{... - non-special */ | ||
| 6776 | || dest.has_quoted_part /* ""{... - non-special */ | ||
| 6777 | || (next != ';' /* }; - special */ | ||
| 6778 | && next != ')' /* }) - special */ | ||
| 6779 | && next != '&' /* }& and }&& ... - special */ | ||
| 6780 | && next != '|' /* }|| ... - special */ | ||
| 6781 | && !strchr(G.ifs, next) /* {word - non-special */ | ||
| 6782 | ) | ||
| 6783 | ) { | ||
| 6784 | /* They are not special, skip "{}" */ | ||
| 6785 | is_special += 2; | ||
| 6786 | } | ||
| 6787 | is_special = strchr(is_special, ch); | ||
| 6788 | is_ifs = strchr(G.ifs, ch); | ||
| 6789 | |||
| 6790 | if (!is_special && !is_ifs) { /* ordinary char */ | ||
| 6791 | ordinary_char: | ||
| 6792 | o_addQchr(&dest, ch); | ||
| 6793 | if ((dest.o_assignment == MAYBE_ASSIGNMENT | ||
| 6794 | || dest.o_assignment == WORD_IS_KEYWORD) | ||
| 6795 | && ch == '=' | ||
| 6796 | && is_well_formed_var_name(dest.data, '=') | ||
| 6797 | ) { | ||
| 6798 | dest.o_assignment = DEFINITELY_ASSIGNMENT; | ||
| 6799 | } | ||
| 6800 | continue; | ||
| 6801 | } | ||
| 6802 | |||
| 6803 | if (is_ifs) { | ||
| 6804 | if (done_word(&dest, &ctx)) { | ||
| 6805 | goto parse_error; | ||
| 6806 | } | ||
| 6807 | if (ch == '\n') { | ||
| 6808 | #if ENABLE_HUSH_CASE | ||
| 6809 | /* "case ... in <newline> word) ..." - | ||
| 6810 | * newlines are ignored (but ';' wouldn't be) */ | ||
| 6811 | if (ctx.command->argv == NULL | ||
| 6812 | && ctx.ctx_res_w == RES_MATCH | ||
| 6813 | ) { | ||
| 6814 | continue; | ||
| 6815 | } | ||
| 6816 | #endif | ||
| 6817 | /* Treat newline as a command separator. */ | ||
| 6818 | done_pipe(&ctx, PIPE_SEQ); | ||
| 6819 | debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt); | ||
| 6820 | if (heredoc_cnt) { | ||
| 6821 | if (fetch_heredocs(heredoc_cnt, &ctx, input)) { | ||
| 6822 | goto parse_error; | ||
| 6823 | } | ||
| 6824 | heredoc_cnt = 0; | ||
| 6825 | } | ||
| 6826 | dest.o_assignment = MAYBE_ASSIGNMENT; | ||
| 6827 | ch = ';'; | ||
| 6828 | /* note: if (is_ifs) continue; | ||
| 6829 | * will still trigger for us */ | ||
| 6830 | } | ||
| 6831 | } | ||
| 6832 | |||
| 6833 | /* "cmd}" or "cmd }..." without semicolon or &: | ||
| 6834 | * } is an ordinary char in this case, even inside { cmd; } | ||
| 6835 | * Pathological example: { ""}; } should exec "}" cmd | ||
| 6836 | */ | ||
| 6837 | if (ch == '}') { | ||
| 6838 | if (!IS_NULL_CMD(ctx.command) /* cmd } */ | ||
| 6839 | || dest.length != 0 /* word} */ | ||
| 6840 | || dest.has_quoted_part /* ""} */ | ||
| 6841 | ) { | ||
| 6842 | goto ordinary_char; | ||
| 6843 | } | ||
| 6844 | if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */ | ||
| 6845 | goto skip_end_trigger; | ||
| 6846 | /* else: } does terminate a group */ | ||
| 6847 | } | ||
| 6848 | |||
| 6849 | if (end_trigger && end_trigger == ch | ||
| 6850 | && (ch != ';' || heredoc_cnt == 0) | ||
| 6851 | #if ENABLE_HUSH_CASE | ||
| 6852 | && (ch != ')' | ||
| 6853 | || ctx.ctx_res_w != RES_MATCH | ||
| 6854 | || (!dest.has_quoted_part && strcmp(dest.data, "esac") == 0) | ||
| 6855 | ) | ||
| 6856 | #endif | ||
| 6857 | ) { | ||
| 6858 | if (heredoc_cnt) { | ||
| 6859 | /* This is technically valid: | ||
| 6860 | * { cat <<HERE; }; echo Ok | ||
| 6861 | * heredoc | ||
| 6862 | * heredoc | ||
| 6863 | * HERE | ||
| 6864 | * but we don't support this. | ||
| 6865 | * We require heredoc to be in enclosing {}/(), | ||
| 6866 | * if any. | ||
| 6867 | */ | ||
| 6868 | syntax_error_unterm_str("here document"); | ||
| 6869 | goto parse_error; | ||
| 6870 | } | ||
| 6871 | if (done_word(&dest, &ctx)) { | ||
| 6872 | goto parse_error; | ||
| 6873 | } | ||
| 6874 | done_pipe(&ctx, PIPE_SEQ); | ||
| 6875 | dest.o_assignment = MAYBE_ASSIGNMENT; | ||
| 6876 | /* Do we sit outside of any if's, loops or case's? */ | ||
| 6877 | if (!HAS_KEYWORDS | ||
| 6878 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) | ||
| 6879 | ) { | ||
| 6880 | o_free(&dest); | ||
| 6881 | #if !BB_MMU | ||
| 6882 | debug_printf_parse("as_string '%s'\n", ctx.as_string.data); | ||
| 6883 | if (pstring) | ||
| 6884 | *pstring = ctx.as_string.data; | ||
| 6885 | else | ||
| 6886 | o_free_unsafe(&ctx.as_string); | ||
| 6887 | #endif | ||
| 6888 | debug_leave(); | ||
| 6889 | debug_printf_parse("parse_stream return %p: " | ||
| 6890 | "end_trigger char found\n", | ||
| 6891 | ctx.list_head); | ||
| 6892 | return ctx.list_head; | ||
| 6893 | } | ||
| 6894 | } | ||
| 6895 | skip_end_trigger: | ||
| 6896 | if (is_ifs) | ||
| 6897 | continue; | ||
| 6898 | |||
| 6899 | /* Catch <, > before deciding whether this word is | ||
| 6900 | * an assignment. a=1 2>z b=2: b=2 is still assignment */ | ||
| 6901 | switch (ch) { | ||
| 6902 | case '>': | ||
| 6903 | redir_fd = redirect_opt_num(&dest); | ||
| 6904 | if (done_word(&dest, &ctx)) { | ||
| 6905 | goto parse_error; | ||
| 6906 | } | ||
| 6907 | redir_style = REDIRECT_OVERWRITE; | ||
| 6908 | if (next == '>') { | ||
| 6909 | redir_style = REDIRECT_APPEND; | ||
| 6910 | ch = i_getch(input); | ||
| 6911 | nommu_addchr(&ctx.as_string, ch); | ||
| 6912 | } | ||
| 6913 | #if 0 | ||
| 6914 | else if (next == '(') { | ||
| 6915 | syntax_error(">(process) not supported"); | ||
| 6916 | goto parse_error; | ||
| 6917 | } | ||
| 6918 | #endif | ||
| 6919 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) | ||
| 6920 | goto parse_error; | ||
| 6921 | continue; /* back to top of while (1) */ | ||
| 6922 | case '<': | ||
| 6923 | redir_fd = redirect_opt_num(&dest); | ||
| 6924 | if (done_word(&dest, &ctx)) { | ||
| 6925 | goto parse_error; | ||
| 6926 | } | ||
| 6927 | redir_style = REDIRECT_INPUT; | ||
| 6928 | if (next == '<') { | ||
| 6929 | redir_style = REDIRECT_HEREDOC; | ||
| 6930 | heredoc_cnt++; | ||
| 6931 | debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt); | ||
| 6932 | ch = i_getch(input); | ||
| 6933 | nommu_addchr(&ctx.as_string, ch); | ||
| 6934 | } else if (next == '>') { | ||
| 6935 | redir_style = REDIRECT_IO; | ||
| 6936 | ch = i_getch(input); | ||
| 6937 | nommu_addchr(&ctx.as_string, ch); | ||
| 6938 | } | ||
| 6939 | #if 0 | ||
| 6940 | else if (next == '(') { | ||
| 6941 | syntax_error("<(process) not supported"); | ||
| 6942 | goto parse_error; | ||
| 6943 | } | ||
| 6944 | #endif | ||
| 6945 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) | ||
| 6946 | goto parse_error; | ||
| 6947 | continue; /* back to top of while (1) */ | ||
| 6948 | } | ||
| 6949 | |||
| 6950 | if (dest.o_assignment == MAYBE_ASSIGNMENT | ||
| 6951 | /* check that we are not in word in "a=1 2>word b=1": */ | ||
| 6952 | && !ctx.pending_redirect | ||
| 6953 | ) { | ||
| 6954 | /* ch is a special char and thus this word | ||
| 6955 | * cannot be an assignment */ | ||
| 6956 | dest.o_assignment = NOT_ASSIGNMENT; | ||
| 6957 | } | ||
| 6958 | |||
| 6959 | /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ | ||
| 6960 | |||
| 6961 | switch (ch) { | ||
| 6962 | case '#': | ||
| 6963 | if (dest.length == 0) { | ||
| 6964 | while (1) { | ||
| 6965 | ch = i_peek(input); | ||
| 6966 | if (ch == EOF || ch == '\n') | ||
| 6967 | break; | ||
| 6968 | i_getch(input); | ||
| 6969 | /* note: we do not add it to &ctx.as_string */ | ||
| 6970 | } | ||
| 6971 | nommu_addchr(&ctx.as_string, '\n'); | ||
| 6972 | } else { | ||
| 6973 | o_addQchr(&dest, ch); | ||
| 6974 | } | ||
| 6975 | break; | ||
| 6976 | case '\\': | ||
| 6977 | if (next == EOF) { | ||
| 6978 | syntax_error("\\<eof>"); | ||
| 6979 | xfunc_die(); | ||
| 6980 | } | ||
| 6981 | ch = i_getch(input); | ||
| 6982 | if (ch != '\n') { | ||
| 6983 | o_addchr(&dest, '\\'); | ||
| 6984 | /*nommu_addchr(&ctx.as_string, '\\'); - already done */ | ||
| 6985 | o_addchr(&dest, ch); | ||
| 6986 | nommu_addchr(&ctx.as_string, ch); | ||
| 6987 | /* Example: echo Hello \2>file | ||
| 6988 | * we need to know that word 2 is quoted */ | ||
| 6989 | dest.has_quoted_part = 1; | ||
| 6990 | } | ||
| 6991 | #if !BB_MMU | ||
| 6992 | else { | ||
| 6993 | /* It's "\<newline>". Remove trailing '\' from ctx.as_string */ | ||
| 6994 | ctx.as_string.data[--ctx.as_string.length] = '\0'; | ||
| 6995 | } | ||
| 6996 | #endif | ||
| 6997 | break; | ||
| 6998 | case '$': | ||
| 6999 | if (parse_dollar(&ctx.as_string, &dest, input) != 0) { | ||
| 7000 | debug_printf_parse("parse_stream parse error: " | ||
| 7001 | "parse_dollar returned non-0\n"); | ||
| 7002 | goto parse_error; | ||
| 7003 | } | ||
| 7004 | break; | ||
| 7005 | case '\'': | ||
| 7006 | dest.has_quoted_part = 1; | ||
| 7007 | while (1) { | ||
| 7008 | ch = i_getch(input); | ||
| 7009 | if (ch == EOF) { | ||
| 7010 | syntax_error_unterm_ch('\''); | ||
| 7011 | /*xfunc_die(); - redundant */ | ||
| 7012 | } | ||
| 7013 | nommu_addchr(&ctx.as_string, ch); | ||
| 7014 | if (ch == '\'') | ||
| 7015 | break; | ||
| 7016 | o_addqchr(&dest, ch); | ||
| 7017 | } | ||
| 7018 | break; | ||
| 7019 | case '"': | ||
| 7020 | dest.has_quoted_part = 1; | ||
| 7021 | is_in_dquote ^= 1; /* invert */ | ||
| 7022 | if (dest.o_assignment == NOT_ASSIGNMENT) | ||
| 7023 | dest.o_escape ^= 1; | ||
| 7024 | break; | ||
| 7025 | #if ENABLE_HUSH_TICK | ||
| 7026 | case '`': { | ||
| 7027 | unsigned pos; | ||
| 7028 | |||
| 7029 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
| 7030 | o_addchr(&dest, '`'); | ||
| 7031 | pos = dest.length; | ||
| 7032 | add_till_backquote(&dest, input); | ||
| 7033 | # if !BB_MMU | ||
| 7034 | o_addstr(&ctx.as_string, dest.data + pos); | ||
| 7035 | o_addchr(&ctx.as_string, '`'); | ||
| 7036 | # endif | ||
| 7037 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
| 7038 | //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos); | ||
| 7039 | break; | ||
| 7040 | } | ||
| 7041 | #endif | ||
| 7042 | case ';': | ||
| 7043 | #if ENABLE_HUSH_CASE | ||
| 7044 | case_semi: | ||
| 7045 | #endif | ||
| 7046 | if (done_word(&dest, &ctx)) { | ||
| 7047 | goto parse_error; | ||
| 7048 | } | ||
| 7049 | done_pipe(&ctx, PIPE_SEQ); | ||
| 7050 | #if ENABLE_HUSH_CASE | ||
| 7051 | /* Eat multiple semicolons, detect | ||
| 7052 | * whether it means something special */ | ||
| 7053 | while (1) { | ||
| 7054 | ch = i_peek(input); | ||
| 7055 | if (ch != ';') | ||
| 7056 | break; | ||
| 7057 | ch = i_getch(input); | ||
| 7058 | nommu_addchr(&ctx.as_string, ch); | ||
| 7059 | if (ctx.ctx_res_w == RES_CASE_BODY) { | ||
| 7060 | ctx.ctx_dsemicolon = 1; | ||
| 7061 | ctx.ctx_res_w = RES_MATCH; | ||
| 7062 | break; | ||
| 7063 | } | ||
| 7064 | } | ||
| 7065 | #endif | ||
| 7066 | new_cmd: | ||
| 7067 | /* We just finished a cmd. New one may start | ||
| 7068 | * with an assignment */ | ||
| 7069 | dest.o_assignment = MAYBE_ASSIGNMENT; | ||
| 7070 | break; | ||
| 7071 | case '&': | ||
| 7072 | if (done_word(&dest, &ctx)) { | ||
| 7073 | goto parse_error; | ||
| 7074 | } | ||
| 7075 | if (next == '&') { | ||
| 7076 | ch = i_getch(input); | ||
| 7077 | nommu_addchr(&ctx.as_string, ch); | ||
| 7078 | done_pipe(&ctx, PIPE_AND); | ||
| 7079 | } else { | ||
| 7080 | done_pipe(&ctx, PIPE_BG); | ||
| 7081 | } | ||
| 7082 | goto new_cmd; | ||
| 7083 | case '|': | ||
| 7084 | if (done_word(&dest, &ctx)) { | ||
| 7085 | goto parse_error; | ||
| 7086 | } | ||
| 7087 | #if ENABLE_HUSH_CASE | ||
| 7088 | if (ctx.ctx_res_w == RES_MATCH) | ||
| 7089 | break; /* we are in case's "word | word)" */ | ||
| 7090 | #endif | ||
| 7091 | if (next == '|') { /* || */ | ||
| 7092 | ch = i_getch(input); | ||
| 7093 | nommu_addchr(&ctx.as_string, ch); | ||
| 7094 | done_pipe(&ctx, PIPE_OR); | ||
| 7095 | } else { | ||
| 7096 | /* we could pick up a file descriptor choice here | ||
| 7097 | * with redirect_opt_num(), but bash doesn't do it. | ||
| 7098 | * "echo foo 2| cat" yields "foo 2". */ | ||
| 7099 | done_command(&ctx); | ||
| 7100 | #if !BB_MMU | ||
| 7101 | o_reset_to_empty_unquoted(&ctx.as_string); | ||
| 7102 | #endif | ||
| 7103 | } | ||
| 7104 | goto new_cmd; | ||
| 7105 | case '(': | ||
| 7106 | #if ENABLE_HUSH_CASE | ||
| 7107 | /* "case... in [(]word)..." - skip '(' */ | ||
| 7108 | if (ctx.ctx_res_w == RES_MATCH | ||
| 7109 | && ctx.command->argv == NULL /* not (word|(... */ | ||
| 7110 | && dest.length == 0 /* not word(... */ | ||
| 7111 | && dest.has_quoted_part == 0 /* not ""(... */ | ||
| 7112 | ) { | ||
| 7113 | continue; | ||
| 7114 | } | ||
| 7115 | #endif | ||
| 7116 | case '{': | ||
| 7117 | if (parse_group(&dest, &ctx, input, ch) != 0) { | ||
| 7118 | goto parse_error; | ||
| 7119 | } | ||
| 7120 | goto new_cmd; | ||
| 7121 | case ')': | ||
| 7122 | #if ENABLE_HUSH_CASE | ||
| 7123 | if (ctx.ctx_res_w == RES_MATCH) | ||
| 7124 | goto case_semi; | ||
| 7125 | #endif | ||
| 7126 | case '}': | ||
| 7127 | /* proper use of this character is caught by end_trigger: | ||
| 7128 | * if we see {, we call parse_group(..., end_trigger='}') | ||
| 7129 | * and it will match } earlier (not here). */ | ||
| 7130 | syntax_error_unexpected_ch(ch); | ||
| 7131 | goto parse_error; | ||
| 7132 | default: | ||
| 7133 | if (HUSH_DEBUG) | ||
| 7134 | bb_error_msg_and_die("BUG: unexpected %c\n", ch); | ||
| 7135 | } | ||
| 7136 | } /* while (1) */ | ||
| 7137 | |||
| 7138 | parse_error: | ||
| 7139 | { | ||
| 7140 | struct parse_context *pctx; | ||
| 7141 | IF_HAS_KEYWORDS(struct parse_context *p2;) | ||
| 7142 | |||
| 7143 | /* Clean up allocated tree. | ||
| 7144 | * Sample for finding leaks on syntax error recovery path. | ||
| 7145 | * Run it from interactive shell, watch pmap `pidof hush`. | ||
| 7146 | * while if false; then false; fi; do break; fi | ||
| 7147 | * Samples to catch leaks at execution: | ||
| 7148 | * while if (true | {true;}); then echo ok; fi; do break; done | ||
| 7149 | * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done | ||
| 7150 | */ | ||
| 7151 | pctx = &ctx; | ||
| 7152 | do { | ||
| 7153 | /* Update pipe/command counts, | ||
| 7154 | * otherwise freeing may miss some */ | ||
| 7155 | done_pipe(pctx, PIPE_SEQ); | ||
| 7156 | debug_printf_clean("freeing list %p from ctx %p\n", | ||
| 7157 | pctx->list_head, pctx); | ||
| 7158 | debug_print_tree(pctx->list_head, 0); | ||
| 7159 | free_pipe_list(pctx->list_head); | ||
| 7160 | debug_printf_clean("freed list %p\n", pctx->list_head); | ||
| 7161 | #if !BB_MMU | ||
| 7162 | o_free_unsafe(&pctx->as_string); | ||
| 7163 | #endif | ||
| 7164 | IF_HAS_KEYWORDS(p2 = pctx->stack;) | ||
| 7165 | if (pctx != &ctx) { | ||
| 7166 | free(pctx); | ||
| 7167 | } | ||
| 7168 | IF_HAS_KEYWORDS(pctx = p2;) | ||
| 7169 | } while (HAS_KEYWORDS && pctx); | ||
| 7170 | /* Free text, clear all dest fields */ | ||
| 7171 | o_free(&dest); | ||
| 7172 | /* If we are not in top-level parse, we return, | ||
| 7173 | * our caller will propagate error. | ||
| 7174 | */ | ||
| 7175 | if (end_trigger != ';') { | ||
| 7176 | #if !BB_MMU | ||
| 7177 | if (pstring) | ||
| 7178 | *pstring = NULL; | ||
| 7179 | #endif | ||
| 7180 | debug_leave(); | ||
| 7181 | return ERR_PTR; | ||
| 7182 | } | ||
| 7183 | /* Discard cached input, force prompt */ | ||
| 7184 | input->p = NULL; | ||
| 7185 | IF_HUSH_INTERACTIVE(input->promptme = 1;) | ||
| 7186 | goto reset; | ||
| 7187 | } | ||
| 7188 | } | ||
| 7189 | |||
| 7190 | /* Executing from string: eval, sh -c '...' | ||
| 7191 | * or from file: /etc/profile, . file, sh <script>, sh (intereactive) | ||
| 7192 | * end_trigger controls how often we stop parsing | ||
| 7193 | * NUL: parse all, execute, return | ||
| 7194 | * ';': parse till ';' or newline, execute, repeat till EOF | ||
| 7195 | */ | ||
| 7196 | static void parse_and_run_stream(struct in_str *inp, int end_trigger) | ||
| 7197 | { | ||
| 7198 | /* Why we need empty flag? | ||
| 7199 | * An obscure corner case "false; ``; echo $?": | ||
| 7200 | * empty command in `` should still set $? to 0. | ||
| 7201 | * But we can't just set $? to 0 at the start, | ||
| 7202 | * this breaks "false; echo `echo $?`" case. | ||
| 7203 | */ | ||
| 7204 | bool empty = 1; | ||
| 7205 | while (1) { | ||
| 7206 | struct pipe *pipe_list; | ||
| 7207 | |||
| 7208 | pipe_list = parse_stream(NULL, inp, end_trigger); | ||
| 7209 | if (!pipe_list) { /* EOF */ | ||
| 7210 | if (empty) | ||
| 7211 | G.last_exitcode = 0; | ||
| 7212 | break; | ||
| 7213 | } | ||
| 7214 | debug_print_tree(pipe_list, 0); | ||
| 7215 | debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); | ||
| 7216 | run_and_free_list(pipe_list); | ||
| 7217 | empty = 0; | ||
| 7218 | } | ||
| 7219 | } | ||
| 7220 | |||
| 7221 | static void parse_and_run_string(const char *s) | ||
| 7222 | { | ||
| 7223 | struct in_str input; | ||
| 7224 | setup_string_in_str(&input, s); | ||
| 7225 | parse_and_run_stream(&input, '\0'); | ||
| 7226 | } | ||
| 7227 | |||
| 7228 | static void parse_and_run_file(FILE *f) | ||
| 7229 | { | ||
| 7230 | struct in_str input; | ||
| 7231 | setup_file_in_str(&input, f); | ||
| 7232 | parse_and_run_stream(&input, ';'); | ||
| 7233 | } | ||
| 7234 | |||
| 7235 | /* Called a few times only (or even once if "sh -c") */ | 7235 | /* Called a few times only (or even once if "sh -c") */ |
| 7236 | static void init_sigmasks(void) | 7236 | static void init_sigmasks(void) |
| 7237 | { | 7237 | { |
