aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-07-14 04:32:29 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-07-14 04:32:29 +0000
commit17f02e79f407ff2311255981c191a1bf8b3a7ae0 (patch)
tree417a580a6aaf5d9844b2aaf1059fe89e6cb14751
parent3177ba08522fd49292f37deecc59db0f75b046fd (diff)
downloadbusybox-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.
-rw-r--r--shell/hush.c192
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 {
262typedef enum { 265typedef 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
351typedef struct { 364typedef 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 */
3466static int parse_stream(o_string *dest, struct p_context *ctx, 3539static 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 )");