diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-03 16:49:04 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-03 16:49:04 +0000 |
commit | b6e6556b31e76afdbe956886e45b1168d4a49de6 (patch) | |
tree | 91e5660b6751ecd8a28661ccc8f84419cd3b13ad /shell | |
parent | 240c255d8b349f06c94cb3afc24c85b6c38b6608 (diff) | |
download | busybox-w32-b6e6556b31e76afdbe956886e45b1168d4a49de6.tar.gz busybox-w32-b6e6556b31e76afdbe956886e45b1168d4a49de6.tar.bz2 busybox-w32-b6e6556b31e76afdbe956886e45b1168d4a49de6.zip |
hush: improve parse_stream: does not require parsing context struct;
cleans up on syntax errors (we used to leak memory in this case);
much simplified interface to the rest of hush.
function old new delta
parse_stream 1204 1447 +243
done_word 658 669 +11
static_get 22 28 +6
builtin_source 84 89 +5
parse_and_run_file 27 30 +3
parse_and_run_string 31 27 -4
builtin_eval 55 50 -5
hush_main 991 985 -6
free_pipe_list 39 31 -8
free_pipe 210 189 -21
expand_variables 2242 2199 -43
parse_and_run_stream 289 153 -136
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/7 up/down: 268/-223) Total: 45 bytes
Diffstat (limited to 'shell')
-rw-r--r-- | shell/hush.c | 496 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/syntax_err_negate.right | 2 |
2 files changed, 268 insertions, 230 deletions
diff --git a/shell/hush.c b/shell/hush.c index aa05e3a83..e0c1fde6b 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -217,24 +217,24 @@ void xxfree(void *ptr); | |||
217 | void *xxmalloc(int lineno, size_t size) | 217 | void *xxmalloc(int lineno, size_t size) |
218 | { | 218 | { |
219 | void *ptr = xmalloc((size + 0xff) & ~0xff); | 219 | void *ptr = xmalloc((size + 0xff) & ~0xff); |
220 | fprintf(stderr, "line %d: malloc %p\n", lineno, ptr); | 220 | fdprintf(2, "line %d: malloc %p\n", lineno, ptr); |
221 | return ptr; | 221 | return ptr; |
222 | } | 222 | } |
223 | void *xxrealloc(int lineno, void *ptr, size_t size) | 223 | void *xxrealloc(int lineno, void *ptr, size_t size) |
224 | { | 224 | { |
225 | ptr = xrealloc(ptr, (size + 0xff) & ~0xff); | 225 | ptr = xrealloc(ptr, (size + 0xff) & ~0xff); |
226 | fprintf(stderr, "line %d: realloc %p\n", lineno, ptr); | 226 | fdprintf(2, "line %d: realloc %p\n", lineno, ptr); |
227 | return ptr; | 227 | return ptr; |
228 | } | 228 | } |
229 | char *xxstrdup(int lineno, const char *str) | 229 | char *xxstrdup(int lineno, const char *str) |
230 | { | 230 | { |
231 | char *ptr = xstrdup(str); | 231 | char *ptr = xstrdup(str); |
232 | fprintf(stderr, "line %d: strdup %p\n", lineno, ptr); | 232 | fdprintf(2, "line %d: strdup %p\n", lineno, ptr); |
233 | return ptr; | 233 | return ptr; |
234 | } | 234 | } |
235 | void xxfree(void *ptr) | 235 | void xxfree(void *ptr) |
236 | { | 236 | { |
237 | fprintf(stderr, "free %p\n", ptr); | 237 | fdprintf(2, "free %p\n", ptr); |
238 | free(ptr); | 238 | free(ptr); |
239 | } | 239 | } |
240 | #define xmalloc(s) xxmalloc(__LINE__, s) | 240 | #define xmalloc(s) xxmalloc(__LINE__, s) |
@@ -244,12 +244,13 @@ void xxfree(void *ptr) | |||
244 | #endif | 244 | #endif |
245 | 245 | ||
246 | 246 | ||
247 | #define ERR_PTR ((void*)(long)1) | ||
248 | |||
247 | static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="HUSH_VER_STR; | 249 | static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="HUSH_VER_STR; |
248 | 250 | ||
249 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 251 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
250 | 252 | ||
251 | #define SPECIAL_VAR_SYMBOL 3 | 253 | #define SPECIAL_VAR_SYMBOL 3 |
252 | #define PARSEFLAG_EXIT_FROM_LOOP 1 | ||
253 | 254 | ||
254 | typedef enum redir_type { | 255 | typedef enum redir_type { |
255 | REDIRECT_INPUT = 1, | 256 | REDIRECT_INPUT = 1, |
@@ -1209,8 +1210,10 @@ static void arith_set_local_var(const char *name, const char *val, int flags) | |||
1209 | static int static_get(struct in_str *i) | 1210 | static int static_get(struct in_str *i) |
1210 | { | 1211 | { |
1211 | int ch = *i->p++; | 1212 | int ch = *i->p++; |
1212 | if (ch == '\0') return EOF; | 1213 | if (ch != '\0') |
1213 | return ch; | 1214 | return ch; |
1215 | i->p--; | ||
1216 | return EOF; | ||
1214 | } | 1217 | } |
1215 | 1218 | ||
1216 | static int static_peek(struct in_str *i) | 1219 | static int static_peek(struct in_str *i) |
@@ -2147,18 +2150,18 @@ static void restore_redirects(int squirrel[]) | |||
2147 | #define free_pipe_list(head, indent) free_pipe_list(head) | 2150 | #define free_pipe_list(head, indent) free_pipe_list(head) |
2148 | #define free_pipe(pi, indent) free_pipe(pi) | 2151 | #define free_pipe(pi, indent) free_pipe(pi) |
2149 | #endif | 2152 | #endif |
2150 | static int free_pipe_list(struct pipe *head, int indent); | 2153 | static void free_pipe_list(struct pipe *head, int indent); |
2151 | 2154 | ||
2152 | /* return code is the exit status of the pipe */ | 2155 | /* return code is the exit status of the pipe */ |
2153 | static int free_pipe(struct pipe *pi, int indent) | 2156 | static void free_pipe(struct pipe *pi, int indent) |
2154 | { | 2157 | { |
2155 | char **p; | 2158 | char **p; |
2156 | struct command *command; | 2159 | struct command *command; |
2157 | struct redir_struct *r, *rnext; | 2160 | struct redir_struct *r, *rnext; |
2158 | int a, i, ret_code = 0; | 2161 | int a, i; |
2159 | 2162 | ||
2160 | if (pi->stopped_cmds > 0) | 2163 | if (pi->stopped_cmds > 0) |
2161 | return ret_code; | 2164 | return; |
2162 | debug_printf_clean("%s run pipe: (pid %d)\n", indenter(indent), getpid()); | 2165 | debug_printf_clean("%s run pipe: (pid %d)\n", indenter(indent), getpid()); |
2163 | for (i = 0; i < pi->num_cmds; i++) { | 2166 | for (i = 0; i < pi->num_cmds; i++) { |
2164 | command = &pi->cmds[i]; | 2167 | command = &pi->cmds[i]; |
@@ -2169,12 +2172,13 @@ static int free_pipe(struct pipe *pi, int indent) | |||
2169 | } | 2172 | } |
2170 | free_strings(command->argv); | 2173 | free_strings(command->argv); |
2171 | command->argv = NULL; | 2174 | command->argv = NULL; |
2172 | } else if (command->group) { | 2175 | } |
2176 | /* not "else if": on syntax error, we may have both! */ | ||
2177 | if (command->group) { | ||
2173 | debug_printf_clean("%s begin group (grp_type:%d)\n", indenter(indent), command->grp_type); | 2178 | debug_printf_clean("%s begin group (grp_type:%d)\n", indenter(indent), command->grp_type); |
2174 | ret_code = free_pipe_list(command->group, indent+3); | 2179 | free_pipe_list(command->group, indent+3); |
2175 | debug_printf_clean("%s end group\n", indenter(indent)); | 2180 | debug_printf_clean("%s end group\n", indenter(indent)); |
2176 | } else { | 2181 | command->group = NULL; |
2177 | debug_printf_clean("%s (nil)\n", indenter(indent)); | ||
2178 | } | 2182 | } |
2179 | for (r = command->redirects; r; r = rnext) { | 2183 | for (r = command->redirects; r; r = rnext) { |
2180 | debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->rd_type].descrip); | 2184 | debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->rd_type].descrip); |
@@ -2199,25 +2203,22 @@ static int free_pipe(struct pipe *pi, int indent) | |||
2199 | free(pi->cmdtext); | 2203 | free(pi->cmdtext); |
2200 | pi->cmdtext = NULL; | 2204 | pi->cmdtext = NULL; |
2201 | #endif | 2205 | #endif |
2202 | return ret_code; | ||
2203 | } | 2206 | } |
2204 | 2207 | ||
2205 | static int free_pipe_list(struct pipe *head, int indent) | 2208 | static void free_pipe_list(struct pipe *head, int indent) |
2206 | { | 2209 | { |
2207 | int rcode = 0; /* if list has no members */ | ||
2208 | struct pipe *pi, *next; | 2210 | struct pipe *pi, *next; |
2209 | 2211 | ||
2210 | for (pi = head; pi; pi = next) { | 2212 | for (pi = head; pi; pi = next) { |
2211 | #if HAS_KEYWORDS | 2213 | #if HAS_KEYWORDS |
2212 | debug_printf_clean("%s pipe reserved mode %d\n", indenter(indent), pi->res_word); | 2214 | debug_printf_clean("%s pipe reserved mode %d\n", indenter(indent), pi->res_word); |
2213 | #endif | 2215 | #endif |
2214 | rcode = free_pipe(pi, indent); | 2216 | free_pipe(pi, indent); |
2215 | debug_printf_clean("%s pipe followup code %d\n", indenter(indent), pi->followup); | 2217 | debug_printf_clean("%s pipe followup code %d\n", indenter(indent), pi->followup); |
2216 | next = pi->next; | 2218 | next = pi->next; |
2217 | /*pi->next = NULL;*/ | 2219 | /*pi->next = NULL;*/ |
2218 | free(pi); | 2220 | free(pi); |
2219 | } | 2221 | } |
2220 | return rcode; | ||
2221 | } | 2222 | } |
2222 | 2223 | ||
2223 | 2224 | ||
@@ -3412,7 +3413,6 @@ static void done_pipe(struct parse_context *ctx, pipe_style type) | |||
3412 | new_p = new_pipe(); | 3413 | new_p = new_pipe(); |
3413 | ctx->pipe->next = new_p; | 3414 | ctx->pipe->next = new_p; |
3414 | ctx->pipe = new_p; | 3415 | ctx->pipe = new_p; |
3415 | ctx->command = NULL; /* needed! */ | ||
3416 | /* RES_THEN, RES_DO etc are "sticky" - | 3416 | /* RES_THEN, RES_DO etc are "sticky" - |
3417 | * they remain set for commands inside if/while. | 3417 | * they remain set for commands inside if/while. |
3418 | * This is used to control execution. | 3418 | * This is used to control execution. |
@@ -3428,6 +3428,7 @@ static void done_pipe(struct parse_context *ctx, pipe_style type) | |||
3428 | if (ctx->ctx_res_w == RES_MATCH) | 3428 | if (ctx->ctx_res_w == RES_MATCH) |
3429 | ctx->ctx_res_w = RES_CASEI; | 3429 | ctx->ctx_res_w = RES_CASEI; |
3430 | #endif | 3430 | #endif |
3431 | ctx->command = NULL; /* trick done_command below */ | ||
3431 | /* Create the memory for command, roughly: | 3432 | /* Create the memory for command, roughly: |
3432 | * ctx->pipe->cmds = new struct command; | 3433 | * ctx->pipe->cmds = new struct command; |
3433 | * ctx->command = &ctx->pipe->cmds[0]; | 3434 | * ctx->command = &ctx->pipe->cmds[0]; |
@@ -3542,21 +3543,21 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3542 | #endif | 3543 | #endif |
3543 | if (r->flag == 0) { /* '!' */ | 3544 | if (r->flag == 0) { /* '!' */ |
3544 | if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ | 3545 | if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ |
3545 | syntax(NULL); | 3546 | syntax("! ! command"); |
3546 | IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;) | 3547 | IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;) |
3547 | } | 3548 | } |
3548 | ctx->ctx_inverted = 1; | 3549 | ctx->ctx_inverted = 1; |
3549 | return 1; | 3550 | return 1; |
3550 | } | 3551 | } |
3551 | if (r->flag & FLAG_START) { | 3552 | if (r->flag & FLAG_START) { |
3552 | struct parse_context *new; | 3553 | struct parse_context *old; |
3553 | debug_printf("push stack\n"); | 3554 | old = xmalloc(sizeof(*old)); |
3554 | new = xmalloc(sizeof(*new)); | 3555 | debug_printf_parse("push stack %p\n", old); |
3555 | *new = *ctx; /* physical copy */ | 3556 | *old = *ctx; /* physical copy */ |
3556 | initialize_context(ctx); | 3557 | initialize_context(ctx); |
3557 | ctx->stack = new; | 3558 | ctx->stack = old; |
3558 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { | 3559 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { |
3559 | syntax(NULL); | 3560 | syntax(word->data); |
3560 | ctx->ctx_res_w = RES_SNTX; | 3561 | ctx->ctx_res_w = RES_SNTX; |
3561 | return 1; | 3562 | return 1; |
3562 | } | 3563 | } |
@@ -3564,8 +3565,8 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3564 | ctx->old_flag = r->flag; | 3565 | ctx->old_flag = r->flag; |
3565 | if (ctx->old_flag & FLAG_END) { | 3566 | if (ctx->old_flag & FLAG_END) { |
3566 | struct parse_context *old; | 3567 | struct parse_context *old; |
3567 | debug_printf("pop stack\n"); | ||
3568 | done_pipe(ctx, PIPE_SEQ); | 3568 | done_pipe(ctx, PIPE_SEQ); |
3569 | debug_printf_parse("pop stack %p\n", ctx->stack); | ||
3569 | old = ctx->stack; | 3570 | old = ctx->stack; |
3570 | old->command->group = ctx->list_head; | 3571 | old->command->group = ctx->list_head; |
3571 | old->command->grp_type = GRP_NORMAL; | 3572 | old->command->grp_type = GRP_NORMAL; |
@@ -3577,10 +3578,10 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
3577 | } | 3578 | } |
3578 | #endif | 3579 | #endif |
3579 | 3580 | ||
3580 | //TODO: many, many callers don't check error from done_word() | ||
3581 | |||
3582 | /* Word is complete, look at it and update parsing context. | 3581 | /* Word is complete, look at it and update parsing context. |
3583 | * Normal return is 0. Syntax errors return 1. */ | 3582 | * Normal return is 0. Syntax errors return 1. |
3583 | * Note: on return, word is reset, but not o_free'd! | ||
3584 | */ | ||
3584 | static int done_word(o_string *word, struct parse_context *ctx) | 3585 | static int done_word(o_string *word, struct parse_context *ctx) |
3585 | { | 3586 | { |
3586 | struct command *command = ctx->command; | 3587 | struct command *command = ctx->command; |
@@ -3610,10 +3611,14 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3610 | debug_printf("word stored in rd_filename: '%s'\n", word->data); | 3611 | debug_printf("word stored in rd_filename: '%s'\n", word->data); |
3611 | } else { | 3612 | } else { |
3612 | /* "{ echo foo; } echo bar" - bad */ | 3613 | /* "{ echo foo; } echo bar" - bad */ |
3613 | /* NB: bash allows e.g. "if true; then { echo foo; } fi". TODO? */ | 3614 | /* NB: bash allows e.g.: |
3615 | * if true; then { echo foo; } fi | ||
3616 | * while if false; then false; fi do break; done | ||
3617 | * TODO? */ | ||
3614 | if (command->group) { | 3618 | if (command->group) { |
3615 | syntax(NULL); | 3619 | syntax(word->data); |
3616 | debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n"); | 3620 | debug_printf_parse("done_word return 1: syntax error, " |
3621 | "groups and arglists don't mix\n"); | ||
3617 | return 1; | 3622 | return 1; |
3618 | } | 3623 | } |
3619 | #if HAS_KEYWORDS | 3624 | #if HAS_KEYWORDS |
@@ -3716,8 +3721,7 @@ static int redirect_opt_num(o_string *o) | |||
3716 | return num; | 3721 | return num; |
3717 | } | 3722 | } |
3718 | 3723 | ||
3719 | static int parse_stream(o_string *dest, struct parse_context *ctx, | 3724 | static struct pipe *parse_stream(struct in_str *input, int end_trigger); |
3720 | struct in_str *input0, int end_trigger); | ||
3721 | 3725 | ||
3722 | #if ENABLE_HUSH_TICK | 3726 | #if ENABLE_HUSH_TICK |
3723 | static FILE *generate_stream_from_list(struct pipe *head) | 3727 | static FILE *generate_stream_from_list(struct pipe *head) |
@@ -3767,24 +3771,25 @@ static int process_command_subs(o_string *dest, | |||
3767 | struct in_str *input) | 3771 | struct in_str *input) |
3768 | { | 3772 | { |
3769 | int retcode, ch, eol_cnt; | 3773 | int retcode, ch, eol_cnt; |
3770 | o_string result = NULL_O_STRING; | 3774 | struct pipe *pipe_list; |
3771 | struct parse_context inner; | ||
3772 | FILE *p; | 3775 | FILE *p; |
3773 | struct in_str pipe_str; | 3776 | struct in_str pipe_str; |
3774 | 3777 | ||
3775 | /* Recursion to generate command */ | 3778 | /* Recursion to generate command */ |
3776 | retcode = parse_stream(&result, &inner, input, '\0'); | 3779 | pipe_list = parse_stream(input, '\0'); |
3777 | if (retcode != 0) | 3780 | if (pipe_list == NULL) |
3778 | return retcode; /* syntax error or EOF */ | 3781 | return 0; /* EOF: empty `cmd`: ``, ` ` etc */ |
3779 | o_free(&result); | 3782 | if (pipe_list == ERR_PTR) |
3780 | 3783 | return 1; /* parse error. can this really happen? */ | |
3781 | p = generate_stream_from_list(inner.list_head); | 3784 | |
3785 | p = generate_stream_from_list(pipe_list); | ||
3786 | free_pipe_list(pipe_list, /* indent: */ 0); | ||
3782 | if (p == NULL) | 3787 | if (p == NULL) |
3783 | return 1; | 3788 | return 1; |
3784 | close_on_exec_on(fileno(p)); | 3789 | close_on_exec_on(fileno(p)); |
3785 | setup_file_in_str(&pipe_str, p); | ||
3786 | 3790 | ||
3787 | /* Now send results of command back into original context */ | 3791 | /* Now send results of command back into original context */ |
3792 | setup_file_in_str(&pipe_str, p); | ||
3788 | eol_cnt = 0; | 3793 | eol_cnt = 0; |
3789 | while ((ch = i_getch(&pipe_str)) != EOF) { | 3794 | while ((ch = i_getch(&pipe_str)) != EOF) { |
3790 | if (ch == '\n') { | 3795 | if (ch == '\n') { |
@@ -3805,7 +3810,6 @@ static int process_command_subs(o_string *dest, | |||
3805 | * echo `echo Hi; exec 1>&-; sleep 2` | 3810 | * echo `echo Hi; exec 1>&-; sleep 2` |
3806 | */ | 3811 | */ |
3807 | retcode = fclose(p); | 3812 | retcode = fclose(p); |
3808 | free_pipe_list(inner.list_head, /* indent: */ 0); | ||
3809 | debug_printf("closed FILE from child, retcode=%d\n", retcode); | 3813 | debug_printf("closed FILE from child, retcode=%d\n", retcode); |
3810 | return retcode; | 3814 | return retcode; |
3811 | } | 3815 | } |
@@ -3817,9 +3821,8 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
3817 | /* dest contains characters seen prior to ( or {. | 3821 | /* dest contains characters seen prior to ( or {. |
3818 | * Typically it's empty, but for function defs, | 3822 | * Typically it's empty, but for function defs, |
3819 | * it contains function name (without '()'). */ | 3823 | * it contains function name (without '()'). */ |
3820 | int rcode; | 3824 | struct pipe *pipe_list; |
3821 | int endch; | 3825 | int endch; |
3822 | struct parse_context sub; | ||
3823 | struct command *command = ctx->command; | 3826 | struct command *command = ctx->command; |
3824 | 3827 | ||
3825 | debug_printf_parse("parse_group entered\n"); | 3828 | debug_printf_parse("parse_group entered\n"); |
@@ -3845,12 +3848,16 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
3845 | endch = ')'; | 3848 | endch = ')'; |
3846 | command->grp_type = GRP_SUBSHELL; | 3849 | command->grp_type = GRP_SUBSHELL; |
3847 | } | 3850 | } |
3848 | rcode = parse_stream(dest, &sub, input, endch); | 3851 | pipe_list = parse_stream(input, endch); |
3849 | if (rcode == 0) { | 3852 | /* empty ()/{} or parse error? */ |
3850 | command->group = sub.list_head; | 3853 | if (!pipe_list || pipe_list == ERR_PTR) { |
3854 | syntax(NULL); | ||
3855 | debug_printf_parse("parse_group return 1: parse_stream returned %p\n", pipe_list); | ||
3856 | return 1; | ||
3851 | } | 3857 | } |
3852 | debug_printf_parse("parse_group return %d\n", rcode); | 3858 | command->group = pipe_list; |
3853 | return rcode; | 3859 | debug_printf_parse("parse_group return 0\n"); |
3860 | return 0; | ||
3854 | /* command remains "open", available for possible redirects */ | 3861 | /* command remains "open", available for possible redirects */ |
3855 | } | 3862 | } |
3856 | 3863 | ||
@@ -4211,17 +4218,18 @@ static int parse_stream_dquoted(o_string *dest, struct in_str *input, int dquote | |||
4211 | goto again; | 4218 | goto again; |
4212 | } | 4219 | } |
4213 | 4220 | ||
4214 | /* Initalize ctx (i.e. caller does not need to do that). | 4221 | /* |
4215 | * Scan input, call done_word() whenever full IFS delimited word was seen. | 4222 | * Scan input until EOF or end_trigger char. |
4216 | * Call done_pipe if '\n' was seen (and end_trigger != NULL). | 4223 | * Return a list of pipes to execute, or NULL on EOF |
4217 | * Return code is 0 if end_trigger char is met, | 4224 | * or if end_trigger character is met. |
4218 | * -1 on EOF (but if end_trigger == NULL then return 0), | 4225 | * On syntax error, exit is shell is not interactive, |
4219 | * 1 for syntax error | 4226 | * reset parsing machinery and start parsing anew, |
4220 | * Net result is a list of pipes in ctx->list_head. | 4227 | * or return ERR_PTR. |
4221 | */ | 4228 | */ |
4222 | static int parse_stream(o_string *dest, struct parse_context *ctx, | 4229 | static struct pipe *parse_stream(struct in_str *input, int end_trigger) |
4223 | struct in_str *input, int end_trigger) | ||
4224 | { | 4230 | { |
4231 | struct parse_context ctx; | ||
4232 | o_string dest = NULL_O_STRING; | ||
4225 | int ch, m; | 4233 | int ch, m; |
4226 | int redir_fd; | 4234 | int redir_fd; |
4227 | redir_type redir_style; | 4235 | redir_type redir_style; |
@@ -4232,18 +4240,22 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4232 | * A single-quote triggers a bypass of the main loop until its mate is | 4240 | * A single-quote triggers a bypass of the main loop until its mate is |
4233 | * found. When recursing, quote state is passed in via dest->o_escape. */ | 4241 | * found. When recursing, quote state is passed in via dest->o_escape. */ |
4234 | 4242 | ||
4235 | debug_printf_parse("parse_stream entered, end_trigger='%c' " | 4243 | debug_printf_parse("parse_stream entered, end_trigger='%c'\n", |
4236 | "dest->o_assignment:%d\n", end_trigger ? : 'X' | 4244 | end_trigger ? : 'X'); |
4237 | , dest->o_assignment); | ||
4238 | 4245 | ||
4239 | dest->o_assignment = MAYBE_ASSIGNMENT; | 4246 | reset: |
4240 | initialize_context(ctx); | 4247 | #if ENABLE_HUSH_INTERACTIVE |
4241 | is_in_dquote = dest->o_escape; | 4248 | input->promptmode = 0; /* PS1 */ |
4249 | #endif | ||
4250 | /* dest.o_assignment = MAYBE_ASSIGNMENT; - already is */ | ||
4251 | initialize_context(&ctx); | ||
4252 | is_in_dquote = 0; | ||
4242 | while (1) { | 4253 | while (1) { |
4243 | if (is_in_dquote) { | 4254 | if (is_in_dquote) { |
4244 | if (parse_stream_dquoted(dest, input, '"')) | 4255 | if (parse_stream_dquoted(&dest, input, '"')) { |
4245 | return 1; /* propagate parse error */ | 4256 | goto parse_error; |
4246 | /* If we're here, we reached closing '"' */ | 4257 | } |
4258 | /* We reached closing '"' */ | ||
4247 | is_in_dquote = 0; | 4259 | is_in_dquote = 0; |
4248 | } | 4260 | } |
4249 | m = CHAR_IFS; | 4261 | m = CHAR_IFS; |
@@ -4256,15 +4268,15 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4256 | } | 4268 | } |
4257 | } | 4269 | } |
4258 | debug_printf_parse(": ch=%c (%d) m=%d escape=%d\n", | 4270 | debug_printf_parse(": ch=%c (%d) m=%d escape=%d\n", |
4259 | ch, ch, m, dest->o_escape); | 4271 | ch, ch, m, dest.o_escape); |
4260 | if (m == CHAR_ORDINARY) { | 4272 | if (m == CHAR_ORDINARY) { |
4261 | o_addQchr(dest, ch); | 4273 | o_addQchr(&dest, ch); |
4262 | if ((dest->o_assignment == MAYBE_ASSIGNMENT | 4274 | if ((dest.o_assignment == MAYBE_ASSIGNMENT |
4263 | || dest->o_assignment == WORD_IS_KEYWORD) | 4275 | || dest.o_assignment == WORD_IS_KEYWORD) |
4264 | && ch == '=' | 4276 | && ch == '=' |
4265 | && is_assignment(dest->data) | 4277 | && is_assignment(dest.data) |
4266 | ) { | 4278 | ) { |
4267 | dest->o_assignment = DEFINITELY_ASSIGNMENT; | 4279 | dest.o_assignment = DEFINITELY_ASSIGNMENT; |
4268 | } | 4280 | } |
4269 | continue; | 4281 | continue; |
4270 | } | 4282 | } |
@@ -4272,25 +4284,40 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4272 | /* m is SPECIAL ($,`), IFS, or ORDINARY_IF_QUOTED (*,#) */ | 4284 | /* m is SPECIAL ($,`), IFS, or ORDINARY_IF_QUOTED (*,#) */ |
4273 | 4285 | ||
4274 | if (m == CHAR_IFS) { | 4286 | if (m == CHAR_IFS) { |
4275 | if (ch == EOF) | 4287 | if (ch == EOF) { |
4276 | goto ret_EOF; | 4288 | struct pipe *pi; |
4277 | if (done_word(dest, ctx)) { | 4289 | if (done_word(&dest, &ctx)) { |
4278 | debug_printf_parse("parse_stream return 1: done_word!=0\n"); | 4290 | goto parse_error; |
4279 | return 1; | 4291 | } |
4292 | o_free(&dest); | ||
4293 | done_pipe(&ctx, PIPE_SEQ); | ||
4294 | /* If we got nothing... */ | ||
4295 | pi = ctx.list_head; | ||
4296 | if (pi->num_cmds == 0 | ||
4297 | && pi->res_word == RES_NONE | ||
4298 | ) { | ||
4299 | free_pipe_list(pi, 0); | ||
4300 | pi = NULL; | ||
4301 | } | ||
4302 | debug_printf_parse("parse_stream return %p\n", pi); | ||
4303 | return pi; | ||
4304 | } | ||
4305 | if (done_word(&dest, &ctx)) { | ||
4306 | goto parse_error; | ||
4280 | } | 4307 | } |
4281 | if (ch == '\n') { | 4308 | if (ch == '\n') { |
4282 | #if ENABLE_HUSH_CASE | 4309 | #if ENABLE_HUSH_CASE |
4283 | /* "case ... in <newline> word) ..." - | 4310 | /* "case ... in <newline> word) ..." - |
4284 | * newlines are ignored (but ';' wouldn't be) */ | 4311 | * newlines are ignored (but ';' wouldn't be) */ |
4285 | if (ctx->command->argv == NULL | 4312 | if (ctx.command->argv == NULL |
4286 | && ctx->ctx_res_w == RES_MATCH | 4313 | && ctx.ctx_res_w == RES_MATCH |
4287 | ) { | 4314 | ) { |
4288 | continue; | 4315 | continue; |
4289 | } | 4316 | } |
4290 | #endif | 4317 | #endif |
4291 | /* Treat newline as a command separator. */ | 4318 | /* Treat newline as a command separator. */ |
4292 | done_pipe(ctx, PIPE_SEQ); | 4319 | done_pipe(&ctx, PIPE_SEQ); |
4293 | dest->o_assignment = MAYBE_ASSIGNMENT; | 4320 | dest.o_assignment = MAYBE_ASSIGNMENT; |
4294 | ch = ';'; | 4321 | ch = ';'; |
4295 | /* note: if (m == CHAR_IFS) continue; | 4322 | /* note: if (m == CHAR_IFS) continue; |
4296 | * will still trigger for us */ | 4323 | * will still trigger for us */ |
@@ -4298,15 +4325,20 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4298 | } | 4325 | } |
4299 | if (end_trigger && end_trigger == ch) { | 4326 | if (end_trigger && end_trigger == ch) { |
4300 | //TODO: disallow "{ cmd }" without semicolon | 4327 | //TODO: disallow "{ cmd }" without semicolon |
4301 | done_word(dest, ctx); | 4328 | if (done_word(&dest, &ctx)) { |
4302 | done_pipe(ctx, PIPE_SEQ); | 4329 | goto parse_error; |
4303 | dest->o_assignment = MAYBE_ASSIGNMENT; | 4330 | } |
4331 | done_pipe(&ctx, PIPE_SEQ); | ||
4332 | dest.o_assignment = MAYBE_ASSIGNMENT; | ||
4304 | /* Do we sit outside of any if's, loops or case's? */ | 4333 | /* Do we sit outside of any if's, loops or case's? */ |
4305 | if (!HAS_KEYWORDS | 4334 | if (!HAS_KEYWORDS |
4306 | IF_HAS_KEYWORDS(|| (ctx->ctx_res_w == RES_NONE && ctx->old_flag == 0)) | 4335 | IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) |
4307 | ) { | 4336 | ) { |
4308 | debug_printf_parse("parse_stream return 0: end_trigger char found\n"); | 4337 | debug_printf_parse("parse_stream return %p: " |
4309 | return 0; | 4338 | "end_trigger char found\n", |
4339 | ctx.list_head); | ||
4340 | o_free(&dest); | ||
4341 | return ctx.list_head; | ||
4310 | } | 4342 | } |
4311 | } | 4343 | } |
4312 | if (m == CHAR_IFS) | 4344 | if (m == CHAR_IFS) |
@@ -4314,15 +4346,15 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4314 | 4346 | ||
4315 | /* m is SPECIAL (e.g. $,`) or ORDINARY_IF_QUOTED (*,#) */ | 4347 | /* m is SPECIAL (e.g. $,`) or ORDINARY_IF_QUOTED (*,#) */ |
4316 | 4348 | ||
4317 | if (dest->o_assignment == MAYBE_ASSIGNMENT) { | 4349 | if (dest.o_assignment == MAYBE_ASSIGNMENT) { |
4318 | /* ch is a special char and thus this word | 4350 | /* ch is a special char and thus this word |
4319 | * cannot be an assignment */ | 4351 | * cannot be an assignment */ |
4320 | dest->o_assignment = NOT_ASSIGNMENT; | 4352 | dest.o_assignment = NOT_ASSIGNMENT; |
4321 | } | 4353 | } |
4322 | 4354 | ||
4323 | switch (ch) { | 4355 | switch (ch) { |
4324 | case '#': | 4356 | case '#': |
4325 | if (dest->length == 0) { | 4357 | if (dest.length == 0) { |
4326 | while (1) { | 4358 | while (1) { |
4327 | ch = i_peek(input); | 4359 | ch = i_peek(input); |
4328 | if (ch == EOF || ch == '\n') | 4360 | if (ch == EOF || ch == '\n') |
@@ -4330,61 +4362,61 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4330 | i_getch(input); | 4362 | i_getch(input); |
4331 | } | 4363 | } |
4332 | } else { | 4364 | } else { |
4333 | o_addQchr(dest, ch); | 4365 | o_addQchr(&dest, ch); |
4334 | } | 4366 | } |
4335 | break; | 4367 | break; |
4336 | case '\\': | 4368 | case '\\': |
4337 | if (next == EOF) { | 4369 | if (next == EOF) { |
4338 | syntax("\\<eof>"); | 4370 | syntax("\\<eof>"); |
4339 | debug_printf_parse("parse_stream return 1: \\<eof>\n"); | 4371 | goto parse_error; |
4340 | return 1; | ||
4341 | } | 4372 | } |
4342 | o_addchr(dest, '\\'); | 4373 | o_addchr(&dest, '\\'); |
4343 | o_addchr(dest, i_getch(input)); | 4374 | o_addchr(&dest, i_getch(input)); |
4344 | break; | 4375 | break; |
4345 | case '$': | 4376 | case '$': |
4346 | if (handle_dollar(dest, input) != 0) { | 4377 | if (handle_dollar(&dest, input) != 0) { |
4347 | debug_printf_parse("parse_stream return 1: handle_dollar returned non-0\n"); | 4378 | debug_printf_parse("parse_stream parse error: handle_dollar returned non-0\n"); |
4348 | return 1; | 4379 | goto parse_error; |
4349 | } | 4380 | } |
4350 | break; | 4381 | break; |
4351 | case '\'': | 4382 | case '\'': |
4352 | dest->nonnull = 1; | 4383 | dest.nonnull = 1; |
4353 | while (1) { | 4384 | while (1) { |
4354 | ch = i_getch(input); | 4385 | ch = i_getch(input); |
4355 | if (ch == EOF) { | 4386 | if (ch == EOF) { |
4356 | syntax("unterminated '"); | 4387 | syntax("unterminated '"); |
4357 | debug_printf_parse("parse_stream return 1: unterminated '\n"); | 4388 | goto parse_error; |
4358 | return 1; | ||
4359 | } | 4389 | } |
4360 | if (ch == '\'') | 4390 | if (ch == '\'') |
4361 | break; | 4391 | break; |
4362 | if (dest->o_assignment == NOT_ASSIGNMENT) | 4392 | if (dest.o_assignment == NOT_ASSIGNMENT) |
4363 | o_addqchr(dest, ch); | 4393 | o_addqchr(&dest, ch); |
4364 | else | 4394 | else |
4365 | o_addchr(dest, ch); | 4395 | o_addchr(&dest, ch); |
4366 | } | 4396 | } |
4367 | break; | 4397 | break; |
4368 | case '"': | 4398 | case '"': |
4369 | dest->nonnull = 1; | 4399 | dest.nonnull = 1; |
4370 | is_in_dquote ^= 1; /* invert */ | 4400 | is_in_dquote ^= 1; /* invert */ |
4371 | if (dest->o_assignment == NOT_ASSIGNMENT) | 4401 | if (dest.o_assignment == NOT_ASSIGNMENT) |
4372 | dest->o_escape ^= 1; | 4402 | dest.o_escape ^= 1; |
4373 | break; | 4403 | break; |
4374 | #if ENABLE_HUSH_TICK | 4404 | #if ENABLE_HUSH_TICK |
4375 | case '`': { | 4405 | case '`': { |
4376 | //int pos = dest->length; | 4406 | //int pos = dest.length; |
4377 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 4407 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
4378 | o_addchr(dest, '`'); | 4408 | o_addchr(&dest, '`'); |
4379 | add_till_backquote(dest, input); | 4409 | add_till_backquote(&dest, input); |
4380 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 4410 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
4381 | //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); | 4411 | //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos); |
4382 | break; | 4412 | break; |
4383 | } | 4413 | } |
4384 | #endif | 4414 | #endif |
4385 | case '>': | 4415 | case '>': |
4386 | redir_fd = redirect_opt_num(dest); | 4416 | redir_fd = redirect_opt_num(&dest); |
4387 | done_word(dest, ctx); | 4417 | if (done_word(&dest, &ctx)) { |
4418 | goto parse_error; | ||
4419 | } | ||
4388 | redir_style = REDIRECT_OVERWRITE; | 4420 | redir_style = REDIRECT_OVERWRITE; |
4389 | if (next == '>') { | 4421 | if (next == '>') { |
4390 | redir_style = REDIRECT_APPEND; | 4422 | redir_style = REDIRECT_APPEND; |
@@ -4393,15 +4425,16 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4393 | #if 0 | 4425 | #if 0 |
4394 | else if (next == '(') { | 4426 | else if (next == '(') { |
4395 | syntax(">(process) not supported"); | 4427 | syntax(">(process) not supported"); |
4396 | debug_printf_parse("parse_stream return 1: >(process) not supported\n"); | 4428 | goto parse_error; |
4397 | return 1; | ||
4398 | } | 4429 | } |
4399 | #endif | 4430 | #endif |
4400 | setup_redirect(ctx, redir_fd, redir_style, input); | 4431 | setup_redirect(&ctx, redir_fd, redir_style, input); |
4401 | break; | 4432 | break; |
4402 | case '<': | 4433 | case '<': |
4403 | redir_fd = redirect_opt_num(dest); | 4434 | redir_fd = redirect_opt_num(&dest); |
4404 | done_word(dest, ctx); | 4435 | if (done_word(&dest, &ctx)) { |
4436 | goto parse_error; | ||
4437 | } | ||
4405 | redir_style = REDIRECT_INPUT; | 4438 | redir_style = REDIRECT_INPUT; |
4406 | if (next == '<') { | 4439 | if (next == '<') { |
4407 | redir_style = REDIRECT_HEREIS; | 4440 | redir_style = REDIRECT_HEREIS; |
@@ -4413,18 +4446,19 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4413 | #if 0 | 4446 | #if 0 |
4414 | else if (next == '(') { | 4447 | else if (next == '(') { |
4415 | syntax("<(process) not supported"); | 4448 | syntax("<(process) not supported"); |
4416 | debug_printf_parse("parse_stream return 1: <(process) not supported\n"); | 4449 | goto parse_error; |
4417 | return 1; | ||
4418 | } | 4450 | } |
4419 | #endif | 4451 | #endif |
4420 | setup_redirect(ctx, redir_fd, redir_style, input); | 4452 | setup_redirect(&ctx, redir_fd, redir_style, input); |
4421 | break; | 4453 | break; |
4422 | case ';': | 4454 | case ';': |
4423 | #if ENABLE_HUSH_CASE | 4455 | #if ENABLE_HUSH_CASE |
4424 | case_semi: | 4456 | case_semi: |
4425 | #endif | 4457 | #endif |
4426 | done_word(dest, ctx); | 4458 | if (done_word(&dest, &ctx)) { |
4427 | done_pipe(ctx, PIPE_SEQ); | 4459 | goto parse_error; |
4460 | } | ||
4461 | done_pipe(&ctx, PIPE_SEQ); | ||
4428 | #if ENABLE_HUSH_CASE | 4462 | #if ENABLE_HUSH_CASE |
4429 | /* Eat multiple semicolons, detect | 4463 | /* Eat multiple semicolons, detect |
4430 | * whether it means something special */ | 4464 | * whether it means something special */ |
@@ -4433,9 +4467,9 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4433 | if (ch != ';') | 4467 | if (ch != ';') |
4434 | break; | 4468 | break; |
4435 | i_getch(input); | 4469 | i_getch(input); |
4436 | if (ctx->ctx_res_w == RES_CASEI) { | 4470 | if (ctx.ctx_res_w == RES_CASEI) { |
4437 | ctx->ctx_dsemicolon = 1; | 4471 | ctx.ctx_dsemicolon = 1; |
4438 | ctx->ctx_res_w = RES_MATCH; | 4472 | ctx.ctx_res_w = RES_MATCH; |
4439 | break; | 4473 | break; |
4440 | } | 4474 | } |
4441 | } | 4475 | } |
@@ -4443,51 +4477,55 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4443 | new_cmd: | 4477 | new_cmd: |
4444 | /* We just finished a cmd. New one may start | 4478 | /* We just finished a cmd. New one may start |
4445 | * with an assignment */ | 4479 | * with an assignment */ |
4446 | dest->o_assignment = MAYBE_ASSIGNMENT; | 4480 | dest.o_assignment = MAYBE_ASSIGNMENT; |
4447 | break; | 4481 | break; |
4448 | case '&': | 4482 | case '&': |
4449 | done_word(dest, ctx); | 4483 | if (done_word(&dest, &ctx)) { |
4484 | goto parse_error; | ||
4485 | } | ||
4450 | if (next == '&') { | 4486 | if (next == '&') { |
4451 | i_getch(input); | 4487 | i_getch(input); |
4452 | done_pipe(ctx, PIPE_AND); | 4488 | done_pipe(&ctx, PIPE_AND); |
4453 | } else { | 4489 | } else { |
4454 | done_pipe(ctx, PIPE_BG); | 4490 | done_pipe(&ctx, PIPE_BG); |
4455 | } | 4491 | } |
4456 | goto new_cmd; | 4492 | goto new_cmd; |
4457 | case '|': | 4493 | case '|': |
4458 | done_word(dest, ctx); | 4494 | if (done_word(&dest, &ctx)) { |
4495 | goto parse_error; | ||
4496 | } | ||
4459 | #if ENABLE_HUSH_CASE | 4497 | #if ENABLE_HUSH_CASE |
4460 | if (ctx->ctx_res_w == RES_MATCH) | 4498 | if (ctx.ctx_res_w == RES_MATCH) |
4461 | break; /* we are in case's "word | word)" */ | 4499 | break; /* we are in case's "word | word)" */ |
4462 | #endif | 4500 | #endif |
4463 | if (next == '|') { /* || */ | 4501 | if (next == '|') { /* || */ |
4464 | i_getch(input); | 4502 | i_getch(input); |
4465 | done_pipe(ctx, PIPE_OR); | 4503 | done_pipe(&ctx, PIPE_OR); |
4466 | } else { | 4504 | } else { |
4467 | /* we could pick up a file descriptor choice here | 4505 | /* we could pick up a file descriptor choice here |
4468 | * with redirect_opt_num(), but bash doesn't do it. | 4506 | * with redirect_opt_num(), but bash doesn't do it. |
4469 | * "echo foo 2| cat" yields "foo 2". */ | 4507 | * "echo foo 2| cat" yields "foo 2". */ |
4470 | done_command(ctx); | 4508 | done_command(&ctx); |
4471 | } | 4509 | } |
4472 | goto new_cmd; | 4510 | goto new_cmd; |
4473 | case '(': | 4511 | case '(': |
4474 | #if ENABLE_HUSH_CASE | 4512 | #if ENABLE_HUSH_CASE |
4475 | /* "case... in [(]word)..." - skip '(' */ | 4513 | /* "case... in [(]word)..." - skip '(' */ |
4476 | if (ctx->ctx_res_w == RES_MATCH | 4514 | if (ctx.ctx_res_w == RES_MATCH |
4477 | && ctx->command->argv == NULL /* not (word|(... */ | 4515 | && ctx.command->argv == NULL /* not (word|(... */ |
4478 | && dest->length == 0 /* not word(... */ | 4516 | && dest.length == 0 /* not word(... */ |
4479 | && dest->nonnull == 0 /* not ""(... */ | 4517 | && dest.nonnull == 0 /* not ""(... */ |
4480 | ) { | 4518 | ) { |
4481 | continue; | 4519 | continue; |
4482 | } | 4520 | } |
4483 | #endif | 4521 | #endif |
4484 | #if ENABLE_HUSH_FUNCTIONS | 4522 | #if ENABLE_HUSH_FUNCTIONS |
4485 | if (dest->length != 0 /* not just () but word() */ | 4523 | if (dest.length != 0 /* not just () but word() */ |
4486 | && dest->nonnull == 0 /* not a"b"c() */ | 4524 | && dest.nonnull == 0 /* not a"b"c() */ |
4487 | && ctx->command->argv == NULL /* it's the first word */ | 4525 | && ctx.command->argv == NULL /* it's the first word */ |
4488 | //TODO: "func ( ) {...}" - note spaces - is valid format too in bash | 4526 | //TODO: "func ( ) {...}" - note spaces - is valid format too in bash |
4489 | && i_peek(input) == ')' | 4527 | && i_peek(input) == ')' |
4490 | && !match_reserved_word(dest) | 4528 | && !match_reserved_word(&dest) |
4491 | ) { | 4529 | ) { |
4492 | bb_error_msg("seems like a function definition"); | 4530 | bb_error_msg("seems like a function definition"); |
4493 | i_getch(input); | 4531 | i_getch(input); |
@@ -4497,21 +4535,19 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4497 | } while (ch == ' ' || ch == '\n'); | 4535 | } while (ch == ' ' || ch == '\n'); |
4498 | if (ch != '{') { | 4536 | if (ch != '{') { |
4499 | syntax("was expecting {"); | 4537 | syntax("was expecting {"); |
4500 | debug_printf_parse("parse_stream return 1\n"); | 4538 | goto parse_error; |
4501 | return 1; | ||
4502 | } | 4539 | } |
4503 | ch = 'F'; /* magic value */ | 4540 | ch = 'F'; /* magic value */ |
4504 | } | 4541 | } |
4505 | #endif | 4542 | #endif |
4506 | case '{': | 4543 | case '{': |
4507 | if (parse_group(dest, ctx, input, ch) != 0) { | 4544 | if (parse_group(&dest, &ctx, input, ch) != 0) { |
4508 | debug_printf_parse("parse_stream return 1: parse_group returned non-0\n"); | 4545 | goto parse_error; |
4509 | return 1; | ||
4510 | } | 4546 | } |
4511 | goto new_cmd; | 4547 | goto new_cmd; |
4512 | case ')': | 4548 | case ')': |
4513 | #if ENABLE_HUSH_CASE | 4549 | #if ENABLE_HUSH_CASE |
4514 | if (ctx->ctx_res_w == RES_MATCH) | 4550 | if (ctx.ctx_res_w == RES_MATCH) |
4515 | goto case_semi; | 4551 | goto case_semi; |
4516 | #endif | 4552 | #endif |
4517 | case '}': | 4553 | case '}': |
@@ -4519,23 +4555,51 @@ static int parse_stream(o_string *dest, struct parse_context *ctx, | |||
4519 | * if we see {, we call parse_group(..., end_trigger='}') | 4555 | * if we see {, we call parse_group(..., end_trigger='}') |
4520 | * and it will match } earlier (not here). */ | 4556 | * and it will match } earlier (not here). */ |
4521 | syntax("unexpected } or )"); | 4557 | syntax("unexpected } or )"); |
4522 | debug_printf_parse("parse_stream return 1: unexpected '}'\n"); | 4558 | goto parse_error; |
4523 | return 1; | ||
4524 | default: | 4559 | default: |
4525 | if (HUSH_DEBUG) | 4560 | if (HUSH_DEBUG) |
4526 | bb_error_msg_and_die("BUG: unexpected %c\n", ch); | 4561 | bb_error_msg_and_die("BUG: unexpected %c\n", ch); |
4527 | } | 4562 | } |
4528 | } /* while (1) */ | 4563 | } /* while (1) */ |
4529 | 4564 | ||
4530 | /* Non-error returns */ | 4565 | parse_error: |
4531 | ret_EOF: | 4566 | { |
4532 | debug_printf_parse("parse_stream return %d\n", -(end_trigger != NULL)); | 4567 | struct parse_context *pctx, *p2; |
4533 | done_word(dest, ctx); | 4568 | |
4534 | done_pipe(ctx, PIPE_SEQ); | 4569 | /* Clean up allocated tree. |
4535 | if (end_trigger) { | 4570 | * Samples for finding leaks on syntax error recovery path. |
4536 | return -1; /* EOF found while expecting end_trigger */ | 4571 | * Execute them from interactive shell and watch pmap `pidof hush`. |
4572 | * while if false; then false; fi do break; done (bash accepts it) | ||
4573 | * while if false; then false; fi; do break; fi | ||
4574 | */ | ||
4575 | pctx = &ctx; | ||
4576 | do { | ||
4577 | /* Update pipe/command counts, | ||
4578 | * otherwise freeing may miss some */ | ||
4579 | done_pipe(pctx, PIPE_SEQ); | ||
4580 | debug_printf_clean("freeing list %p from ctx %p\n", | ||
4581 | pctx->list_head, pctx); | ||
4582 | debug_print_tree(pctx->list_head, 0); | ||
4583 | free_pipe_list(pctx->list_head, 0); | ||
4584 | debug_printf_clean("freed list %p\n", pctx->list_head); | ||
4585 | p2 = pctx->stack; | ||
4586 | if (pctx != &ctx) { | ||
4587 | free(pctx); | ||
4588 | } | ||
4589 | pctx = p2; | ||
4590 | } while (pctx); | ||
4591 | /* Free text, clear all dest fields */ | ||
4592 | o_free(&dest); | ||
4593 | /* If we are not in top-level parse, we return, | ||
4594 | * our caller will propagate error. | ||
4595 | */ | ||
4596 | if (end_trigger != ';') | ||
4597 | return ERR_PTR; | ||
4598 | /* Discard cached input, force prompt */ | ||
4599 | input->p = NULL; | ||
4600 | input->promptme = 1; | ||
4601 | goto reset; | ||
4537 | } | 4602 | } |
4538 | return 0; | ||
4539 | } | 4603 | } |
4540 | 4604 | ||
4541 | static void set_in_charmap(const char *set, int code) | 4605 | static void set_in_charmap(const char *set, int code) |
@@ -4565,71 +4629,41 @@ static void update_charmap(void) | |||
4565 | set_in_charmap(G.ifs, CHAR_IFS); /* are ordinary if quoted */ | 4629 | set_in_charmap(G.ifs, CHAR_IFS); /* are ordinary if quoted */ |
4566 | } | 4630 | } |
4567 | 4631 | ||
4568 | /* Most recursion does not come through here, the exception is | 4632 | /* Execiting from string: eval, sh -c '...' |
4569 | * from builtin_source() and builtin_eval() */ | 4633 | * or from file: /etc/profile, . file, sh <script>, sh (intereactive) |
4570 | static int parse_and_run_stream(struct in_str *inp, int parse_flag) | 4634 | * end_trigger controls how often we stop parsing |
4635 | * NUL: parse all, execute, return | ||
4636 | * ';': parse till ';' or newline, execute, repeat till EOF | ||
4637 | */ | ||
4638 | static void parse_and_run_stream(struct in_str *inp, int end_trigger) | ||
4571 | { | 4639 | { |
4572 | struct parse_context ctx; | 4640 | while (1) { |
4573 | o_string temp = NULL_O_STRING; | 4641 | struct pipe *pipe_list; |
4574 | int rcode; | ||
4575 | 4642 | ||
4576 | do { | ||
4577 | update_charmap(); | 4643 | update_charmap(); |
4578 | #if ENABLE_HUSH_INTERACTIVE | 4644 | |
4579 | inp->promptmode = 0; /* PS1 */ | 4645 | pipe_list = parse_stream(inp, end_trigger); |
4580 | #endif | 4646 | if (!pipe_list) /* EOF */ |
4581 | /* We will stop & execute after each ';' or '\n'. | 4647 | break; |
4582 | * Example: "sleep 9999; echo TEST" + ctrl-C: | 4648 | debug_print_tree(pipe_list, 0); |
4583 | * TEST should be printed */ | 4649 | debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); |
4584 | temp.o_assignment = MAYBE_ASSIGNMENT; | 4650 | run_and_free_list(pipe_list); |
4585 | rcode = parse_stream(&temp, &ctx, inp, ';'); | 4651 | /* Loop on syntax errors, return on EOF: */ |
4586 | debug_printf_parse("rcode %d ctx.old_flag %x\n", rcode, ctx.old_flag); | 4652 | } |
4587 | #if HAS_KEYWORDS | ||
4588 | if (rcode != 1 && ctx.old_flag != 0) { | ||
4589 | syntax(NULL); | ||
4590 | } | ||
4591 | #endif | ||
4592 | if (rcode != 1 IF_HAS_KEYWORDS(&& ctx.old_flag == 0)) { | ||
4593 | debug_print_tree(ctx.list_head, 0); | ||
4594 | debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); | ||
4595 | run_and_free_list(ctx.list_head); | ||
4596 | } else { | ||
4597 | /* We arrive here also if rcode == 1 (error in parse_stream) */ | ||
4598 | #if HAS_KEYWORDS | ||
4599 | if (ctx.old_flag != 0) { | ||
4600 | free(ctx.stack); | ||
4601 | o_reset(&temp); | ||
4602 | } | ||
4603 | #endif | ||
4604 | /*temp.nonnull = 0; - o_free does it below */ | ||
4605 | /*temp.o_escape = 0; - o_free does it below */ | ||
4606 | free_pipe_list(ctx.list_head, /* indent: */ 0); | ||
4607 | /* Discard all unprocessed line input, force prompt on */ | ||
4608 | inp->p = NULL; | ||
4609 | #if ENABLE_HUSH_INTERACTIVE | ||
4610 | inp->promptme = 1; | ||
4611 | #endif | ||
4612 | } | ||
4613 | o_free(&temp); | ||
4614 | /* loop on syntax errors, return on EOF: */ | ||
4615 | } while (rcode != -1 && !(parse_flag & PARSEFLAG_EXIT_FROM_LOOP)); | ||
4616 | return 0; | ||
4617 | } | 4653 | } |
4618 | 4654 | ||
4619 | static int parse_and_run_string(const char *s, int parse_flag) | 4655 | static void parse_and_run_string(const char *s) |
4620 | { | 4656 | { |
4621 | struct in_str input; | 4657 | struct in_str input; |
4622 | setup_string_in_str(&input, s); | 4658 | setup_string_in_str(&input, s); |
4623 | return parse_and_run_stream(&input, parse_flag); | 4659 | parse_and_run_stream(&input, '\0'); |
4624 | } | 4660 | } |
4625 | 4661 | ||
4626 | static int parse_and_run_file(FILE *f) | 4662 | static void parse_and_run_file(FILE *f) |
4627 | { | 4663 | { |
4628 | int rcode; | ||
4629 | struct in_str input; | 4664 | struct in_str input; |
4630 | setup_file_in_str(&input, f); | 4665 | setup_file_in_str(&input, f); |
4631 | rcode = parse_and_run_stream(&input, 0 /* parse_flag */); | 4666 | parse_and_run_stream(&input, ';'); |
4632 | return rcode; | ||
4633 | } | 4667 | } |
4634 | 4668 | ||
4635 | #if ENABLE_HUSH_JOB | 4669 | #if ENABLE_HUSH_JOB |
@@ -4753,7 +4787,7 @@ int hush_main(int argc, char **argv) | |||
4753 | optind--; | 4787 | optind--; |
4754 | } /* else -c 'script' PAR0 PAR1: $0 is PAR0 */ | 4788 | } /* else -c 'script' PAR0 PAR1: $0 is PAR0 */ |
4755 | G.global_argc = argc - optind; | 4789 | G.global_argc = argc - optind; |
4756 | opt = parse_and_run_string(optarg, 0 /* parse_flag */); | 4790 | parse_and_run_string(optarg); |
4757 | goto final_return; | 4791 | goto final_return; |
4758 | case 'i': | 4792 | case 'i': |
4759 | /* Well, we cannot just declare interactiveness, | 4793 | /* Well, we cannot just declare interactiveness, |
@@ -4851,14 +4885,14 @@ int hush_main(int argc, char **argv) | |||
4851 | #endif | 4885 | #endif |
4852 | 4886 | ||
4853 | if (argv[optind] == NULL) { | 4887 | if (argv[optind] == NULL) { |
4854 | opt = parse_and_run_file(stdin); | 4888 | parse_and_run_file(stdin); |
4855 | } else { | 4889 | } else { |
4856 | debug_printf("\nrunning script '%s'\n", argv[optind]); | 4890 | debug_printf("\nrunning script '%s'\n", argv[optind]); |
4857 | G.global_argv = argv + optind; | 4891 | G.global_argv = argv + optind; |
4858 | G.global_argc = argc - optind; | 4892 | G.global_argc = argc - optind; |
4859 | input = xfopen_for_read(argv[optind]); | 4893 | input = xfopen_for_read(argv[optind]); |
4860 | fcntl(fileno(input), F_SETFD, FD_CLOEXEC); | 4894 | fcntl(fileno(input), F_SETFD, FD_CLOEXEC); |
4861 | opt = parse_and_run_file(input); | 4895 | parse_and_run_file(input); |
4862 | } | 4896 | } |
4863 | 4897 | ||
4864 | final_return: | 4898 | final_return: |
@@ -4876,7 +4910,7 @@ int hush_main(int argc, char **argv) | |||
4876 | free(tmp); | 4910 | free(tmp); |
4877 | } | 4911 | } |
4878 | #endif | 4912 | #endif |
4879 | hush_exit(opt ? opt : G.last_return_code); | 4913 | hush_exit(G.last_return_code); |
4880 | } | 4914 | } |
4881 | 4915 | ||
4882 | 4916 | ||
@@ -5000,7 +5034,11 @@ static int builtin_eval(char **argv) | |||
5000 | 5034 | ||
5001 | if (argv[1]) { | 5035 | if (argv[1]) { |
5002 | char *str = expand_strvec_to_string(argv + 1); | 5036 | char *str = expand_strvec_to_string(argv + 1); |
5003 | parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP); | 5037 | /* bash: |
5038 | * eval "echo Hi; done" ("done" is syntax error): | ||
5039 | * "echo Hi" will not execute too. | ||
5040 | */ | ||
5041 | parse_and_run_string(str); | ||
5004 | free(str); | 5042 | free(str); |
5005 | rcode = G.last_return_code; | 5043 | rcode = G.last_return_code; |
5006 | } | 5044 | } |
@@ -5011,8 +5049,9 @@ static int builtin_cd(char **argv) | |||
5011 | { | 5049 | { |
5012 | const char *newdir; | 5050 | const char *newdir; |
5013 | if (argv[1] == NULL) { | 5051 | if (argv[1] == NULL) { |
5014 | // bash does nothing (exitcode 0) if HOME is ""; if it's unset, | 5052 | /* bash does nothing (exitcode 0) if HOME is ""; if it's unset, |
5015 | // bash says "bash: cd: HOME not set" and does nothing (exitcode 1) | 5053 | * bash says "bash: cd: HOME not set" and does nothing (exitcode 1) |
5054 | */ | ||
5016 | newdir = getenv("HOME") ? : "/"; | 5055 | newdir = getenv("HOME") ? : "/"; |
5017 | } else | 5056 | } else |
5018 | newdir = argv[1]; | 5057 | newdir = argv[1]; |
@@ -5309,7 +5348,6 @@ static int builtin_shift(char **argv) | |||
5309 | static int builtin_source(char **argv) | 5348 | static int builtin_source(char **argv) |
5310 | { | 5349 | { |
5311 | FILE *input; | 5350 | FILE *input; |
5312 | int status; | ||
5313 | 5351 | ||
5314 | if (argv[1] == NULL) | 5352 | if (argv[1] == NULL) |
5315 | return EXIT_FAILURE; | 5353 | return EXIT_FAILURE; |
@@ -5326,9 +5364,9 @@ static int builtin_source(char **argv) | |||
5326 | /* XXX argv and argc are broken; need to save old G.global_argv | 5364 | /* XXX argv and argc are broken; need to save old G.global_argv |
5327 | * (pointer only is OK!) on this stack frame, | 5365 | * (pointer only is OK!) on this stack frame, |
5328 | * set G.global_argv=argv+1, recurse, and restore. */ | 5366 | * set G.global_argv=argv+1, recurse, and restore. */ |
5329 | status = parse_and_run_file(input); | 5367 | parse_and_run_file(input); |
5330 | fclose(input); | 5368 | fclose(input); |
5331 | return status; | 5369 | return G.last_return_code; |
5332 | } | 5370 | } |
5333 | 5371 | ||
5334 | static int builtin_umask(char **argv) | 5372 | static int builtin_umask(char **argv) |
diff --git a/shell/hush_test/hush-misc/syntax_err_negate.right b/shell/hush_test/hush-misc/syntax_err_negate.right index d1e7654f5..8c7010629 100644 --- a/shell/hush_test/hush-misc/syntax_err_negate.right +++ b/shell/hush_test/hush-misc/syntax_err_negate.right | |||
@@ -1,2 +1,2 @@ | |||
1 | bash 3.2 fails this | 1 | bash 3.2 fails this |
2 | hush: syntax error | 2 | hush: syntax error: ! ! command |