hush: fix "if { echo foo; } then { echo bar; } fi" parsing
authorDenis Vlasenko <vda.linux@googlemail.com>
Thu, 16 Apr 2009 10:59:40 +0000 (10:59 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Thu, 16 Apr 2009 10:59:40 +0000 (10:59 -0000)
function                                             old     new   delta
done_word                                            728     793     +65
parse_stream                                        2084    2098     +14

shell/hush.c
shell/hush_test/hush-parsing/groups_and_keywords1.right [new file with mode: 0644]
shell/hush_test/hush-parsing/groups_and_keywords1.tests [new file with mode: 0755]
shell/hush_test/hush-vars/param_glob.tests

index a5d5741da162a6c2716c5e5043d8c8a73501f3c5..21cea32a1943f116ef5c0a00f22d920118bc955d 100644 (file)
@@ -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 (file)
index 0000000..4c46650
--- /dev/null
@@ -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 (executable)
index 0000000..01944d7
--- /dev/null
@@ -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:$?
index 801d58ee75c045776cfe60e00a9474d28a9e9c00..0173fd771a97a126ae59e88f81b68c27688bcde6 100755 (executable)
@@ -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