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 | |
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>
-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 | { |