From bb929517a86092481ed8547e9f247c1b58bc4745 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Thu, 16 Apr 2009 10:59:40 +0000 Subject: [PATCH] hush: fix "if { echo foo; } then { echo bar; } fi" parsing function old new delta done_word 728 793 +65 parse_stream 2084 2098 +14 --- shell/hush.c | 43 +++++++++++-------- .../hush-parsing/groups_and_keywords1.right | 11 +++++ .../hush-parsing/groups_and_keywords1.tests | 10 +++++ shell/hush_test/hush-vars/param_glob.tests | 2 +- 4 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 shell/hush_test/hush-parsing/groups_and_keywords1.right create mode 100755 shell/hush_test/hush-parsing/groups_and_keywords1.tests diff --git a/shell/hush.c b/shell/hush.c index a5d5741da..21cea32a1 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -4154,6 +4154,8 @@ static const struct reserved_combo* match_reserved_word(o_string *word) } return NULL; } +/* Return 0: not a keyword, 1: keyword + */ static int reserved_word(o_string *word, struct parse_context *ctx) { #if ENABLE_HUSH_CASE @@ -4163,6 +4165,8 @@ static int reserved_word(o_string *word, struct parse_context *ctx) #endif const struct reserved_combo *r; + if (word->o_quoted) + return 0; r = match_reserved_word(word); if (!r) return 0; @@ -4177,13 +4181,14 @@ static int reserved_word(o_string *word, struct parse_context *ctx) if (r->flag == 0) { /* '!' */ if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ syntax_error("! ! command"); - IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;) + ctx->ctx_res_w = RES_SNTX; } ctx->ctx_inverted = 1; return 1; } if (r->flag & FLAG_START) { struct parse_context *old; + old = xmalloc(sizeof(*old)); debug_printf_parse("push stack %p\n", old); *old = *ctx; /* physical copy */ @@ -4193,11 +4198,21 @@ static int reserved_word(o_string *word, struct parse_context *ctx) syntax_error_at(word->data); ctx->ctx_res_w = RES_SNTX; return 1; + } else { + /* "{...} fi" is ok. "{...} if" is not + * Example: + * if { echo foo; } then { echo bar; } fi */ + if (ctx->command->group) + done_pipe(ctx, PIPE_SEQ); } + ctx->ctx_res_w = r->res; ctx->old_flag = r->flag; + word->o_assignment = r->assignment_flag; + if (ctx->old_flag & FLAG_END) { struct parse_context *old; + done_pipe(ctx, PIPE_SEQ); debug_printf_parse("pop stack %p\n", ctx->stack); old = ctx->stack; @@ -4213,7 +4228,6 @@ static int reserved_word(o_string *word, struct parse_context *ctx) *ctx = *old; /* physical copy */ free(old); } - word->o_assignment = r->assignment_flag; return 1; } #endif @@ -4273,19 +4287,6 @@ static int done_word(o_string *word, struct parse_context *ctx) word->o_assignment = MAYBE_ASSIGNMENT; } - if (command->group) { - /* "{ echo foo; } echo bar" - bad */ - /* NB: bash allows e.g.: - * if true; then { echo foo; } fi - * while if false; then false; fi do break; done - * and disallows: - * while if false; then false; fi; do; break; done - * TODO? */ - syntax_error_at(word->data); - debug_printf_parse("done_word return 1: syntax error, " - "groups and arglists don't mix\n"); - return 1; - } #if HAS_KEYWORDS # if ENABLE_HUSH_CASE if (ctx->ctx_dsemicolon @@ -4311,6 +4312,13 @@ static int done_word(o_string *word, struct parse_context *ctx) } } #endif + if (command->group) { + /* "{ echo foo; } echo bar" - bad */ + syntax_error_at(word->data); + debug_printf_parse("done_word return 1: syntax error, " + "groups and arglists don't mix\n"); + return 1; + } if (word->o_quoted /* word had "xx" or 'xx' at least as part of it. */ /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) @@ -4720,7 +4728,8 @@ static int parse_group(o_string *dest, struct parse_context *ctx, #if ENABLE_HUSH_FUNCTIONS if (ch == '(' && !dest->o_quoted) { if (dest->length) - done_word(dest, ctx); + if (done_word(dest, ctx)) + return 1; if (!command->argv) goto skip; /* (... */ if (command->argv[1]) { /* word word ... (... */ @@ -4778,10 +4787,10 @@ static int parse_group(o_string *dest, struct parse_context *ctx, #endif /* empty ()/{} or parse error? */ if (!pipe_list || pipe_list == ERR_PTR) { + /* parse_stream already emitted error msg */ #if !BB_MMU free(as_string); #endif - syntax_error(NULL); debug_printf_parse("parse_group return 1: " "parse_stream returned %p\n", pipe_list); return 1; diff --git a/shell/hush_test/hush-parsing/groups_and_keywords1.right b/shell/hush_test/hush-parsing/groups_and_keywords1.right new file mode 100644 index 000000000..4c46650dc --- /dev/null +++ b/shell/hush_test/hush-parsing/groups_and_keywords1.right @@ -0,0 +1,11 @@ +Semicolons after } can be omitted 1: +foo +bar +Semicolons after } can be omitted 2: +foo +bar +Semicolons after fi can be omitted: +foo +bar +baz +Done:0 diff --git a/shell/hush_test/hush-parsing/groups_and_keywords1.tests b/shell/hush_test/hush-parsing/groups_and_keywords1.tests new file mode 100755 index 000000000..01944d714 --- /dev/null +++ b/shell/hush_test/hush-parsing/groups_and_keywords1.tests @@ -0,0 +1,10 @@ +echo "Semicolons after } can be omitted 1:" +if { echo foo; } then { echo bar; } fi + +echo "Semicolons after } can be omitted 2:" +while { echo foo; } do { echo bar; break; } done + +echo "Semicolons after fi can be omitted:" +while if echo foo; then echo bar; fi do echo baz; break; done + +echo Done:$? diff --git a/shell/hush_test/hush-vars/param_glob.tests b/shell/hush_test/hush-vars/param_glob.tests index 801d58ee7..0173fd771 100755 --- a/shell/hush_test/hush-vars/param_glob.tests +++ b/shell/hush_test/hush-vars/param_glob.tests @@ -1,5 +1,5 @@ if test $# = 0; then - #BUG in builtin_exec! will glob param! + # UNFIXED BUG in builtin_exec! will glob param! #exec "$THIS_SH" "$0" 'param_glob.t*' "$THIS_SH" "$0" 'param_glob.t*' exit -- 2.25.1