diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-14 04:32:29 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-14 04:32:29 +0000 |
| commit | 17f02e79f407ff2311255981c191a1bf8b3a7ae0 (patch) | |
| tree | 417a580a6aaf5d9844b2aaf1059fe89e6cb14751 /shell | |
| parent | 3177ba08522fd49292f37deecc59db0f75b046fd (diff) | |
| download | busybox-w32-17f02e79f407ff2311255981c191a1bf8b3a7ae0.tar.gz busybox-w32-17f02e79f407ff2311255981c191a1bf8b3a7ae0.tar.bz2 busybox-w32-17f02e79f407ff2311255981c191a1bf8b3a7ae0.zip | |
hush: add case statement support. It is incomplete and disabled for now.
costs ~300 bytes when enabled.
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 192 |
1 files changed, 147 insertions, 45 deletions
diff --git a/shell/hush.c b/shell/hush.c index aab53c336..47e53f8c1 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -73,6 +73,9 @@ | |||
| 73 | 73 | ||
| 74 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ | 74 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ |
| 75 | 75 | ||
| 76 | // TEMP | ||
| 77 | #define ENABLE_HUSH_CASE 0 | ||
| 78 | |||
| 76 | 79 | ||
| 77 | #if !BB_MMU && ENABLE_HUSH_TICK | 80 | #if !BB_MMU && ENABLE_HUSH_TICK |
| 78 | //#undef ENABLE_HUSH_TICK | 81 | //#undef ENABLE_HUSH_TICK |
| @@ -262,22 +265,29 @@ typedef enum { | |||
| 262 | typedef enum { | 265 | typedef enum { |
| 263 | RES_NONE = 0, | 266 | RES_NONE = 0, |
| 264 | #if ENABLE_HUSH_IF | 267 | #if ENABLE_HUSH_IF |
| 265 | RES_IF = 1, | 268 | RES_IF , |
| 266 | RES_THEN = 2, | 269 | RES_THEN , |
| 267 | RES_ELIF = 3, | 270 | RES_ELIF , |
| 268 | RES_ELSE = 4, | 271 | RES_ELSE , |
| 269 | RES_FI = 5, | 272 | RES_FI , |
| 270 | #endif | 273 | #endif |
| 271 | #if ENABLE_HUSH_LOOPS | 274 | #if ENABLE_HUSH_LOOPS |
| 272 | RES_FOR = 6, | 275 | RES_FOR , |
| 273 | RES_WHILE = 7, | 276 | RES_WHILE , |
| 274 | RES_UNTIL = 8, | 277 | RES_UNTIL , |
| 275 | RES_DO = 9, | 278 | RES_DO , |
| 276 | RES_DONE = 10, | 279 | RES_DONE , |
| 277 | RES_IN = 11, | 280 | RES_IN , |
| 278 | #endif | 281 | #endif |
| 279 | RES_XXXX = 12, | 282 | #if ENABLE_HUSH_CASE |
| 280 | RES_SNTX = 13 | 283 | RES_CASE , |
| 284 | /* two pseudo-keywords support contrived "case" syntax: */ | ||
| 285 | RES_MATCH , /* "word)" */ | ||
| 286 | RES_CASEI , /* "this command is inside CASE" */ | ||
| 287 | RES_ESAC , | ||
| 288 | #endif | ||
| 289 | RES_XXXX , | ||
| 290 | RES_SNTX | ||
| 281 | } reserved_style; | 291 | } reserved_style; |
| 282 | 292 | ||
| 283 | /* This holds pointers to the various results of parsing */ | 293 | /* This holds pointers to the various results of parsing */ |
| @@ -289,6 +299,9 @@ struct p_context { | |||
| 289 | #if HAS_KEYWORDS | 299 | #if HAS_KEYWORDS |
| 290 | smallint ctx_res_w; | 300 | smallint ctx_res_w; |
| 291 | smallint ctx_inverted; /* "! cmd | cmd" */ | 301 | smallint ctx_inverted; /* "! cmd | cmd" */ |
| 302 | #if ENABLE_HUSH_CASE | ||
| 303 | smallint ctx_dsemicolon; /* ";;" seen */ | ||
| 304 | #endif | ||
| 292 | int old_flag; /* bitmask of FLAG_xxx, for figuring out valid reserved words */ | 305 | int old_flag; /* bitmask of FLAG_xxx, for figuring out valid reserved words */ |
| 293 | struct p_context *stack; | 306 | struct p_context *stack; |
| 294 | #endif | 307 | #endif |
| @@ -350,7 +363,7 @@ struct variable { | |||
| 350 | 363 | ||
| 351 | typedef struct { | 364 | typedef struct { |
| 352 | char *data; | 365 | char *data; |
| 353 | int length; | 366 | int length; /* position where data is appended */ |
| 354 | int maxlen; | 367 | int maxlen; |
| 355 | /* Misnomer! it's not "quoting", it's "protection against globbing"! | 368 | /* Misnomer! it's not "quoting", it's "protection against globbing"! |
| 356 | * (by prepending \ to *, ?, [ and to \ too) */ | 369 | * (by prepending \ to *, ?, [ and to \ too) */ |
| @@ -1955,6 +1968,12 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
| 1955 | [RES_DONE ] = "DONE" , | 1968 | [RES_DONE ] = "DONE" , |
| 1956 | [RES_IN ] = "IN" , | 1969 | [RES_IN ] = "IN" , |
| 1957 | #endif | 1970 | #endif |
| 1971 | #if ENABLE_HUSH_CASE | ||
| 1972 | [RES_CASE ] = "CASE" , | ||
| 1973 | [RES_MATCH] = "MATCH", | ||
| 1974 | [RES_CASEI] = "CASEI", | ||
| 1975 | [RES_ESAC ] = "ESAC" , | ||
| 1976 | #endif | ||
| 1958 | [RES_XXXX ] = "XXXX" , | 1977 | [RES_XXXX ] = "XXXX" , |
| 1959 | [RES_SNTX ] = "SNTX" , | 1978 | [RES_SNTX ] = "SNTX" , |
| 1960 | }; | 1979 | }; |
| @@ -2003,6 +2022,9 @@ static int run_list(struct pipe *pi) | |||
| 2003 | char **for_list = NULL; | 2022 | char **for_list = NULL; |
| 2004 | int flag_rep = 0; | 2023 | int flag_rep = 0; |
| 2005 | #endif | 2024 | #endif |
| 2025 | #if ENABLE_HUSH_CASE | ||
| 2026 | char *case_word = NULL; | ||
| 2027 | #endif | ||
| 2006 | int flag_skip = 1; | 2028 | int flag_skip = 1; |
| 2007 | int rcode = 0; /* probably for gcc only */ | 2029 | int rcode = 0; /* probably for gcc only */ |
| 2008 | int flag_restore = 0; | 2030 | int flag_restore = 0; |
| @@ -2019,17 +2041,20 @@ static int run_list(struct pipe *pi) | |||
| 2019 | #if ENABLE_HUSH_LOOPS | 2041 | #if ENABLE_HUSH_LOOPS |
| 2020 | /* check syntax for "for" */ | 2042 | /* check syntax for "for" */ |
| 2021 | for (rpipe = pi; rpipe; rpipe = rpipe->next) { | 2043 | for (rpipe = pi; rpipe; rpipe = rpipe->next) { |
| 2022 | if ((rpipe->res_word == RES_IN || rpipe->res_word == RES_FOR) | 2044 | if (rpipe->res_word != RES_FOR && rpipe->res_word != RES_IN) |
| 2023 | && (rpipe->next == NULL) | 2045 | continue; |
| 2024 | ) { | 2046 | /* current word is FOR or IN (BOLD in comments below) */ |
| 2025 | syntax("malformed for"); /* no IN or no commands after IN */ | 2047 | if (rpipe->next == NULL) { |
| 2048 | syntax("malformed for"); | ||
| 2026 | debug_printf_exec("run_list lvl %d return 1\n", run_list_level); | 2049 | debug_printf_exec("run_list lvl %d return 1\n", run_list_level); |
| 2027 | return 1; | 2050 | return 1; |
| 2028 | } | 2051 | } |
| 2029 | if (/* Extra statement after IN: "for a in a b; echo Hi; do ...; done" ? */ | 2052 | /* "FOR v; do ..." and "for v IN a b; do..." are ok */ |
| 2030 | (rpipe->res_word == RES_IN && rpipe->next->res_word == RES_IN && rpipe->next->progs[0].argv != NULL) | 2053 | if (rpipe->next->res_word == RES_DO) |
| 2031 | /* FOR not followed by IN or DO ("for var; do..." case)? */ | 2054 | continue; |
| 2032 | || (rpipe->res_word == RES_FOR && (rpipe->next->res_word != RES_IN && rpipe->next->res_word != RES_DO)) | 2055 | /* next word is not "do". It must be "in" then ("FOR v in ...") */ |
| 2056 | if (rpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */ | ||
| 2057 | || rpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */ | ||
| 2033 | ) { | 2058 | ) { |
| 2034 | syntax("malformed for"); | 2059 | syntax("malformed for"); |
| 2035 | debug_printf_exec("run_list lvl %d return 1\n", run_list_level); | 2060 | debug_printf_exec("run_list lvl %d return 1\n", run_list_level); |
| @@ -2136,8 +2161,8 @@ static int run_list(struct pipe *pi) | |||
| 2136 | /* create list of variable values */ | 2161 | /* create list of variable values */ |
| 2137 | debug_print_strings("for_list made from", vals); | 2162 | debug_print_strings("for_list made from", vals); |
| 2138 | for_list = expand_strvec_to_strvec(vals); | 2163 | for_list = expand_strvec_to_strvec(vals); |
| 2139 | debug_print_strings("for_list", for_list); | ||
| 2140 | for_lcur = for_list; | 2164 | for_lcur = for_list; |
| 2165 | debug_print_strings("for_list", for_list); | ||
| 2141 | for_varname = pi->progs->argv[0]; | 2166 | for_varname = pi->progs->argv[0]; |
| 2142 | pi->progs->argv[0] = NULL; | 2167 | pi->progs->argv[0] = NULL; |
| 2143 | flag_rep = 1; | 2168 | flag_rep = 1; |
| @@ -2169,6 +2194,26 @@ static int run_list(struct pipe *pi) | |||
| 2169 | } | 2194 | } |
| 2170 | } | 2195 | } |
| 2171 | #endif | 2196 | #endif |
| 2197 | #if ENABLE_HUSH_CASE | ||
| 2198 | if (rword == RES_CASE) { | ||
| 2199 | case_word = pi->progs->argv[0]; | ||
| 2200 | continue; | ||
| 2201 | } | ||
| 2202 | if (rword == RES_MATCH) { | ||
| 2203 | if (case_word) { | ||
| 2204 | next_if_code = strcmp(case_word, pi->progs->argv[0]); | ||
| 2205 | if (next_if_code == 0) | ||
| 2206 | case_word = NULL; | ||
| 2207 | continue; | ||
| 2208 | } | ||
| 2209 | break; | ||
| 2210 | } | ||
| 2211 | if (rword == RES_CASEI) { | ||
| 2212 | if (next_if_code != 0) | ||
| 2213 | continue; | ||
| 2214 | } | ||
| 2215 | #endif | ||
| 2216 | |||
| 2172 | if (pi->num_progs == 0) | 2217 | if (pi->num_progs == 0) |
| 2173 | continue; | 2218 | continue; |
| 2174 | debug_printf_exec(": run_pipe with %d members\n", pi->num_progs); | 2219 | debug_printf_exec(": run_pipe with %d members\n", pi->num_progs); |
| @@ -2193,8 +2238,7 @@ static int run_list(struct pipe *pi) | |||
| 2193 | rcode = checkjobs_and_fg_shell(pi); | 2238 | rcode = checkjobs_and_fg_shell(pi); |
| 2194 | } else | 2239 | } else |
| 2195 | #endif | 2240 | #endif |
| 2196 | { | 2241 | { /* this one just waits for completion */ |
| 2197 | /* this one just waits for completion */ | ||
| 2198 | rcode = checkjobs(pi); | 2242 | rcode = checkjobs(pi); |
| 2199 | } | 2243 | } |
| 2200 | debug_printf_exec(": checkjobs returned %d\n", rcode); | 2244 | debug_printf_exec(": checkjobs returned %d\n", rcode); |
| @@ -2217,12 +2261,13 @@ static int run_list(struct pipe *pi) | |||
| 2217 | skip_more_for_this_rword = rword; | 2261 | skip_more_for_this_rword = rword; |
| 2218 | } | 2262 | } |
| 2219 | checkjobs(NULL); | 2263 | checkjobs(NULL); |
| 2220 | } | 2264 | } /* for (pi) */ |
| 2221 | 2265 | ||
| 2222 | #if ENABLE_HUSH_JOB | 2266 | #if ENABLE_HUSH_JOB |
| 2223 | if (ctrl_z_flag) { | 2267 | if (ctrl_z_flag) { |
| 2224 | /* ctrl-Z forked somewhere in the past, we are the child, | 2268 | /* ctrl-Z forked somewhere in the past, we are the child, |
| 2225 | * and now we completed running the list. Exit. */ | 2269 | * and now we completed running the list. Exit. */ |
| 2270 | //TODO: _exit? | ||
| 2226 | exit(rcode); | 2271 | exit(rcode); |
| 2227 | } | 2272 | } |
| 2228 | ret: | 2273 | ret: |
| @@ -2821,6 +2866,10 @@ static int reserved_word(const o_string *word, struct p_context *ctx) | |||
| 2821 | FLAG_DONE = (1 << RES_DONE ), | 2866 | FLAG_DONE = (1 << RES_DONE ), |
| 2822 | FLAG_IN = (1 << RES_IN ), | 2867 | FLAG_IN = (1 << RES_IN ), |
| 2823 | #endif | 2868 | #endif |
| 2869 | #if ENABLE_HUSH_CASE | ||
| 2870 | FLAG_MATCH = (1 << RES_MATCH), | ||
| 2871 | FLAG_ESAC = (1 << RES_ESAC ), | ||
| 2872 | #endif | ||
| 2824 | FLAG_START = (1 << RES_XXXX ), | 2873 | FLAG_START = (1 << RES_XXXX ), |
| 2825 | }; | 2874 | }; |
| 2826 | /* Mostly a list of accepted follow-up reserved words. | 2875 | /* Mostly a list of accepted follow-up reserved words. |
| @@ -2843,16 +2892,30 @@ static int reserved_word(const o_string *word, struct p_context *ctx) | |||
| 2843 | { "until", RES_UNTIL, FLAG_DO | FLAG_START }, | 2892 | { "until", RES_UNTIL, FLAG_DO | FLAG_START }, |
| 2844 | { "in", RES_IN, FLAG_DO }, | 2893 | { "in", RES_IN, FLAG_DO }, |
| 2845 | { "do", RES_DO, FLAG_DONE }, | 2894 | { "do", RES_DO, FLAG_DONE }, |
| 2846 | { "done", RES_DONE, FLAG_END } | 2895 | { "done", RES_DONE, FLAG_END }, |
| 2896 | #endif | ||
| 2897 | #if ENABLE_HUSH_CASE | ||
| 2898 | { "case", RES_CASE, FLAG_MATCH | FLAG_START }, | ||
| 2899 | { "esac", RES_ESAC, FLAG_END }, | ||
| 2847 | #endif | 2900 | #endif |
| 2848 | }; | 2901 | }; |
| 2849 | 2902 | #if ENABLE_HUSH_CASE | |
| 2903 | static const struct reserved_combo reserved_match = { | ||
| 2904 | "" /* "match" */, RES_MATCH, FLAG_MATCH | FLAG_ESAC | ||
| 2905 | }; | ||
| 2906 | #endif | ||
| 2850 | const struct reserved_combo *r; | 2907 | const struct reserved_combo *r; |
| 2851 | 2908 | ||
| 2852 | for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { | 2909 | for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { |
| 2853 | if (strcmp(word->data, r->literal) != 0) | 2910 | if (strcmp(word->data, r->literal) != 0) |
| 2854 | continue; | 2911 | continue; |
| 2855 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); | 2912 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); |
| 2913 | #if ENABLE_HUSH_CASE | ||
| 2914 | if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE) | ||
| 2915 | /* "case word IN ..." - IN part starts first match part */ | ||
| 2916 | r = &reserved_match; | ||
| 2917 | else | ||
| 2918 | #endif | ||
| 2856 | if (r->flag == 0) { /* '!' */ | 2919 | if (r->flag == 0) { /* '!' */ |
| 2857 | if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ | 2920 | if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ |
| 2858 | syntax(NULL); | 2921 | syntax(NULL); |
| @@ -2864,13 +2927,6 @@ static int reserved_word(const o_string *word, struct p_context *ctx) | |||
| 2864 | if (r->flag & FLAG_START) { | 2927 | if (r->flag & FLAG_START) { |
| 2865 | struct p_context *new; | 2928 | struct p_context *new; |
| 2866 | debug_printf("push stack\n"); | 2929 | debug_printf("push stack\n"); |
| 2867 | #if ENABLE_HUSH_LOOPS | ||
| 2868 | if (ctx->ctx_res_w == RES_IN || ctx->ctx_res_w == RES_FOR) { | ||
| 2869 | syntax("malformed for"); /* example: 'for if' */ | ||
| 2870 | ctx->ctx_res_w = RES_SNTX; | ||
| 2871 | return 1; | ||
| 2872 | } | ||
| 2873 | #endif | ||
| 2874 | new = xmalloc(sizeof(*new)); | 2930 | new = xmalloc(sizeof(*new)); |
| 2875 | *new = *ctx; /* physical copy */ | 2931 | *new = *ctx; /* physical copy */ |
| 2876 | initialize_context(ctx); | 2932 | initialize_context(ctx); |
| @@ -2924,12 +2980,20 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
| 2924 | word->o_assignment = NOT_ASSIGNMENT; | 2980 | word->o_assignment = NOT_ASSIGNMENT; |
| 2925 | debug_printf("word stored in rd_filename: '%s'\n", word->data); | 2981 | debug_printf("word stored in rd_filename: '%s'\n", word->data); |
| 2926 | } else { | 2982 | } else { |
| 2927 | if (child->group) { /* TODO: example how to trigger? */ | 2983 | // if (child->group) { /* TODO: example how to trigger? */ |
| 2928 | syntax(NULL); | 2984 | // syntax(NULL); |
| 2929 | debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n"); | 2985 | // debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n"); |
| 2930 | return 1; | 2986 | // return 1; |
| 2931 | } | 2987 | // } |
| 2932 | #if HAS_KEYWORDS | 2988 | #if HAS_KEYWORDS |
| 2989 | #if ENABLE_HUSH_CASE | ||
| 2990 | if (ctx->ctx_dsemicolon) { | ||
| 2991 | /* already done when ctx_dsemicolon was set to 1 */ | ||
| 2992 | /* ctx->ctx_res_w = RES_MATCH; */ | ||
| 2993 | ctx->ctx_dsemicolon = 0; | ||
| 2994 | } else | ||
| 2995 | #endif | ||
| 2996 | |||
| 2933 | if (!child->argv /* if it's the first word... */ | 2997 | if (!child->argv /* if it's the first word... */ |
| 2934 | #if ENABLE_HUSH_LOOPS | 2998 | #if ENABLE_HUSH_LOOPS |
| 2935 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ | 2999 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ |
| @@ -2984,6 +3048,12 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
| 2984 | done_pipe(ctx, PIPE_SEQ); | 3048 | done_pipe(ctx, PIPE_SEQ); |
| 2985 | } | 3049 | } |
| 2986 | #endif | 3050 | #endif |
| 3051 | #if ENABLE_HUSH_CASE | ||
| 3052 | /* Force CASE to have just one word */ | ||
| 3053 | if (ctx->ctx_res_w == RES_CASE) { | ||
| 3054 | done_pipe(ctx, PIPE_SEQ); | ||
| 3055 | } | ||
| 3056 | #endif | ||
| 2987 | debug_printf_parse("done_word return 0\n"); | 3057 | debug_printf_parse("done_word return 0\n"); |
| 2988 | return 0; | 3058 | return 0; |
| 2989 | } | 3059 | } |
| @@ -3056,6 +3126,10 @@ static void done_pipe(struct p_context *ctx, pipe_style type) | |||
| 3056 | || ctx->ctx_res_w == RES_IN) | 3126 | || ctx->ctx_res_w == RES_IN) |
| 3057 | ctx->ctx_res_w = RES_NONE; | 3127 | ctx->ctx_res_w = RES_NONE; |
| 3058 | #endif | 3128 | #endif |
| 3129 | #if ENABLE_HUSH_CASE | ||
| 3130 | if (ctx->ctx_res_w == RES_MATCH) | ||
| 3131 | ctx->ctx_res_w = RES_CASEI; | ||
| 3132 | #endif | ||
| 3059 | /* Create the memory for child, roughly: | 3133 | /* Create the memory for child, roughly: |
| 3060 | * ctx->pipe->progs = new struct child_prog; | 3134 | * ctx->pipe->progs = new struct child_prog; |
| 3061 | * ctx->pipe->progs[0].family = ctx->pipe; | 3135 | * ctx->pipe->progs[0].family = ctx->pipe; |
| @@ -3458,10 +3532,9 @@ static int handle_dollar(o_string *dest, struct in_str *input) | |||
| 3458 | } | 3532 | } |
| 3459 | 3533 | ||
| 3460 | /* Scan input, call done_word() whenever full IFS delimited word was seen. | 3534 | /* Scan input, call done_word() whenever full IFS delimited word was seen. |
| 3461 | * call done_pipe if '\n' was seen (and end_trigger != NULL) | 3535 | * Call done_pipe if '\n' was seen (and end_trigger != NULL). |
| 3462 | * Return if (non-quoted) char in end_trigger was seen; or on parse error. */ | 3536 | * Return code is 0 if end_trigger char is met, |
| 3463 | /* Return code is 0 if end_trigger char is met, | 3537 | * -1 on EOF (but if end_trigger == NULL then return 0), |
| 3464 | * -1 on EOF (but if end_trigger == NULL then return 0) | ||
| 3465 | * 1 for syntax error */ | 3538 | * 1 for syntax error */ |
| 3466 | static int parse_stream(o_string *dest, struct p_context *ctx, | 3539 | static int parse_stream(o_string *dest, struct p_context *ctx, |
| 3467 | struct in_str *input, const char *end_trigger) | 3540 | struct in_str *input, const char *end_trigger) |
| @@ -3664,8 +3737,26 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
| 3664 | setup_redirect(ctx, redir_fd, redir_style, input); | 3737 | setup_redirect(ctx, redir_fd, redir_style, input); |
| 3665 | break; | 3738 | break; |
| 3666 | case ';': | 3739 | case ';': |
| 3740 | #if ENABLE_HUSH_CASE | ||
| 3741 | case_semi: | ||
| 3742 | #endif | ||
| 3667 | done_word(dest, ctx); | 3743 | done_word(dest, ctx); |
| 3668 | done_pipe(ctx, PIPE_SEQ); | 3744 | done_pipe(ctx, PIPE_SEQ); |
| 3745 | #if ENABLE_HUSH_CASE | ||
| 3746 | /* Eat multiple semicolons, detect | ||
| 3747 | * whether it means something special */ | ||
| 3748 | while (1) { | ||
| 3749 | ch = i_peek(input); | ||
| 3750 | if (ch != ';') | ||
| 3751 | break; | ||
| 3752 | i_getch(input); | ||
| 3753 | if (ctx->ctx_res_w == RES_CASEI) { | ||
| 3754 | ctx->ctx_dsemicolon = 1; | ||
| 3755 | ctx->ctx_res_w = RES_MATCH; | ||
| 3756 | break; | ||
| 3757 | } | ||
| 3758 | } | ||
| 3759 | #endif | ||
| 3669 | break; | 3760 | break; |
| 3670 | case '&': | 3761 | case '&': |
| 3671 | done_word(dest, ctx); | 3762 | done_word(dest, ctx); |
| @@ -3689,6 +3780,13 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
| 3689 | } | 3780 | } |
| 3690 | break; | 3781 | break; |
| 3691 | case '(': | 3782 | case '(': |
| 3783 | #if ENABLE_HUSH_CASE | ||
| 3784 | if (dest->length == 0 // && argv[0] == NULL | ||
| 3785 | && ctx->ctx_res_w == RES_MATCH | ||
| 3786 | ) { | ||
| 3787 | continue; | ||
| 3788 | } | ||
| 3789 | #endif | ||
| 3692 | case '{': | 3790 | case '{': |
| 3693 | if (parse_group(dest, ctx, input, ch) != 0) { | 3791 | if (parse_group(dest, ctx, input, ch) != 0) { |
| 3694 | debug_printf_parse("parse_stream return 1: parse_group returned non-0\n"); | 3792 | debug_printf_parse("parse_stream return 1: parse_group returned non-0\n"); |
| @@ -3696,6 +3794,10 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
| 3696 | } | 3794 | } |
| 3697 | break; | 3795 | break; |
| 3698 | case ')': | 3796 | case ')': |
| 3797 | #if ENABLE_HUSH_CASE | ||
| 3798 | if (ctx->ctx_res_w == RES_MATCH) | ||
| 3799 | goto case_semi; | ||
| 3800 | #endif | ||
| 3699 | case '}': | 3801 | case '}': |
| 3700 | /* proper use of this character is caught by end_trigger */ | 3802 | /* proper use of this character is caught by end_trigger */ |
| 3701 | syntax("unexpected } or )"); | 3803 | syntax("unexpected } or )"); |
