summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2010-09-05 14:50:59 +0200
committerDenys Vlasenko <dvlasenk@redhat.com>2010-09-05 14:50:59 +0200
commitb36abf2dfcb47cc0ca659c916b3efef9f757ea0c (patch)
treeb6df2d914b46ce369c600c317e9e792f9930b80d
parent38292b68c962b9d470fa4e577020749c8c69226d (diff)
downloadbusybox-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.c4084
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
2518static 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 2521static void free_pipe(struct pipe *pi)
2521static 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
2523static 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
2584static 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
2603static 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 */
2616static 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
2644static 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
2709static 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
2724struct reserved_combo {
2725 char literal[6];
2726 unsigned char res;
2727 unsigned char assignment_flag;
2728 int flag;
2729};
2730enum {
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
2754static 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 */
2793static 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 */
2873static 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
3042static 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 */
3077static 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 */
3164static 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
3181static 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 */
3222static 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
3269static 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
3274static 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
3283static 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 */
3392static void add_till_backquote(o_string *dest, struct in_str *input);
3393/* '...' */
3394static 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? */
3408static 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 */
3445static 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
3487static 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
3554static 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
2528static int parse_stream_dquoted(o_string *as_string, 3761static 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 */
3852static 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: */
4374static char *expand_string_to_string(const char *str);
4375static 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
5310static 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 */
5318static 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
5343static 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
5350static 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
5358static 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. */
5462static 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
3466static void setup_heredoc(struct redir_struct *redir) 5498static 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
3626static void free_pipe_list(struct pipe *head);
3627
3628/* Return code is the exit status of the pipe */
3629static 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
3692static 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
3709static 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
3714static struct pipe *parse_stream(char **pstring,
3715 struct in_str *input,
3716 int end_trigger);
3717static void parse_and_run_string(const char *s);
3718
3719
3720static char *find_in_path(const char *arg) 5657static 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
4485static int redirect_and_varexp_helper(char ***new_env_p, struct variable **old_vars_p, struct command *command, int squirrel[3], char **argv_expanded) 6422static 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
5294static 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 */
5307static 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
5335static 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
5400static 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
5415struct reserved_combo {
5416 char literal[6];
5417 unsigned char res;
5418 unsigned char assignment_flag;
5419 int flag;
5420};
5421enum {
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
5445static 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 */
5484static 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 */
5564static 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
5733static 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 */
5768static 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 */
5855static 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
5872static 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 */
5913static 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
5961static 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. */
6064static 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
6103static 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 */
6212static void add_till_backquote(o_string *dest, struct in_str *input);
6213/* '...' */
6214static 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? */
6228static 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 */
6265static 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
6307static 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
6374static 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
6581static 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 */
6672static 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 */
7196static 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
7221static 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
7228static 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") */
7236static void init_sigmasks(void) 7236static void init_sigmasks(void)
7237{ 7237{