| --- busybox-1.18.4/shell/hush.c |
| +++ busybox-1.18.4-hush/shell/hush.c |
| @@ -427,6 +427,15 @@ enum { |
| /* Used for initialization: o_string foo = NULL_O_STRING; */ |
| #define NULL_O_STRING { NULL } |
| |
| +#ifndef debug_printf_parse |
| +static const char *const assignment_flag[] = { |
| + "MAYBE_ASSIGNMENT", |
| + "DEFINITELY_ASSIGNMENT", |
| + "NOT_ASSIGNMENT", |
| + "WORD_IS_KEYWORD", |
| +}; |
| +#endif |
| + |
| /* I can almost use ordinary FILE*. Is open_memstream() universally |
| * available? Where is it documented? */ |
| typedef struct in_str { |
| @@ -2885,24 +2894,24 @@ static const struct reserved_combo* matc |
| */ |
| static const struct reserved_combo reserved_list[] = { |
| # if ENABLE_HUSH_IF |
| - { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, |
| - { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START }, |
| - { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, |
| - { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN }, |
| - { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI }, |
| - { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, |
| + { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, |
| + { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START }, |
| + { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, |
| + { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN }, |
| + { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI }, |
| + { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, |
| # endif |
| # if ENABLE_HUSH_LOOPS |
| - { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, |
| - { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, |
| - { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, |
| - { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, |
| - { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE }, |
| - { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, |
| + { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, |
| + { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, |
| + { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START }, |
| + { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, |
| + { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE }, |
| + { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, |
| # endif |
| # if ENABLE_HUSH_CASE |
| - { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, |
| - { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, |
| + { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, |
| + { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, |
| # endif |
| }; |
| const struct reserved_combo *r; |
| @@ -2968,6 +2977,7 @@ static int reserved_word(o_string *word, |
| ctx->ctx_res_w = r->res; |
| ctx->old_flag = r->flag; |
| word->o_assignment = r->assignment_flag; |
| + debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); |
| |
| if (ctx->old_flag & FLAG_END) { |
| struct parse_context *old; |
| @@ -3034,18 +3044,6 @@ static int done_word(o_string *word, str |
| debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); |
| ctx->pending_redirect = NULL; |
| } else { |
| - /* If this word wasn't an assignment, next ones definitely |
| - * can't be assignments. Even if they look like ones. */ |
| - if (word->o_assignment != DEFINITELY_ASSIGNMENT |
| - && word->o_assignment != WORD_IS_KEYWORD |
| - ) { |
| - word->o_assignment = NOT_ASSIGNMENT; |
| - } else { |
| - if (word->o_assignment == DEFINITELY_ASSIGNMENT) |
| - command->assignment_cnt++; |
| - word->o_assignment = MAYBE_ASSIGNMENT; |
| - } |
| - |
| #if HAS_KEYWORDS |
| # if ENABLE_HUSH_CASE |
| if (ctx->ctx_dsemicolon |
| @@ -3065,8 +3063,9 @@ static int done_word(o_string *word, str |
| && ctx->ctx_res_w != RES_CASE |
| # endif |
| ) { |
| - debug_printf_parse("checking '%s' for reserved-ness\n", word->data); |
| - if (reserved_word(word, ctx)) { |
| + int reserved = reserved_word(word, ctx); |
| + debug_printf_parse("checking for reserved-ness: %d\n", reserved); |
| + if (reserved) { |
| o_reset_to_empty_unquoted(word); |
| debug_printf_parse("done_word return %d\n", |
| (ctx->ctx_res_w == RES_SNTX)); |
| @@ -3087,6 +3086,23 @@ static int done_word(o_string *word, str |
| "groups and arglists don't mix\n"); |
| return 1; |
| } |
| + |
| + /* If this word wasn't an assignment, next ones definitely |
| + * can't be assignments. Even if they look like ones. */ |
| + if (word->o_assignment != DEFINITELY_ASSIGNMENT |
| + && word->o_assignment != WORD_IS_KEYWORD |
| + ) { |
| + word->o_assignment = NOT_ASSIGNMENT; |
| + } else { |
| + if (word->o_assignment == DEFINITELY_ASSIGNMENT) { |
| + command->assignment_cnt++; |
| + debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt); |
| + } |
| + debug_printf_parse("word->o_assignment was:'%s'\n", assignment_flag[word->o_assignment]); |
| + word->o_assignment = MAYBE_ASSIGNMENT; |
| + } |
| + debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]); |
| + |
| if (word->has_quoted_part |
| /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ |
| && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) |
| @@ -4105,6 +4121,7 @@ static struct pipe *parse_stream(char ** |
| && is_well_formed_var_name(dest.data, '=') |
| ) { |
| dest.o_assignment = DEFINITELY_ASSIGNMENT; |
| + debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); |
| } |
| continue; |
| } |
| @@ -4154,6 +4171,7 @@ static struct pipe *parse_stream(char ** |
| heredoc_cnt = 0; |
| } |
| dest.o_assignment = MAYBE_ASSIGNMENT; |
| + debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); |
| ch = ';'; |
| /* note: if (is_blank) continue; |
| * will still trigger for us */ |
| @@ -4203,6 +4221,7 @@ static struct pipe *parse_stream(char ** |
| } |
| done_pipe(&ctx, PIPE_SEQ); |
| dest.o_assignment = MAYBE_ASSIGNMENT; |
| + debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); |
| /* Do we sit outside of any if's, loops or case's? */ |
| if (!HAS_KEYWORDS |
| IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) |
| @@ -4309,6 +4328,7 @@ static struct pipe *parse_stream(char ** |
| /* ch is a special char and thus this word |
| * cannot be an assignment */ |
| dest.o_assignment = NOT_ASSIGNMENT; |
| + debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); |
| } |
| |
| /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ |
| @@ -4406,6 +4426,7 @@ static struct pipe *parse_stream(char ** |
| /* We just finished a cmd. New one may start |
| * with an assignment */ |
| dest.o_assignment = MAYBE_ASSIGNMENT; |
| + debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]); |
| break; |
| case '&': |
| if (done_word(&dest, &ctx)) { |
| @@ -7292,7 +7313,10 @@ static int run_list(struct pipe *pi) |
| #endif |
| #if ENABLE_HUSH_LOOPS |
| /* Beware of "while false; true; do ..."! */ |
| - if (pi->next && pi->next->res_word == RES_DO) { |
| + if (pi->next |
| + && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE) |
| + /* (the second check above is needed for "while ...; do \n done" case) */ |
| + ) { |
| if (rword == RES_WHILE) { |
| if (rcode) { |
| /* "while false; do...done" - exitcode 0 */ |
| --- busybox-1.18.4/shell/hush_test/hush-misc/while3.right |
| +++ busybox-1.18.4-hush/shell/hush_test/hush-misc/while3.right |
| @@ -0,0 +1 @@ |
| +OK:0 |
| --- busybox-1.18.4/shell/hush_test/hush-misc/while3.tests |
| +++ busybox-1.18.4-hush/shell/hush_test/hush-misc/while3.tests |
| @@ -0,0 +1,4 @@ |
| +while false; do |
| + # bash will require at least ":" here... |
| +done |
| +echo OK:$? |