[git commit] hush: allow { cmd } to not be terminated by semicolon in some cases

Denys Vlasenko vda.linux at googlemail.com
Fri Nov 4 17:46:14 UTC 2016


commit: https://git.busybox.net/busybox/commit/?id=672a55e606fc50e4ffe7810bd0d9cd1cf9b980a3
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 shell/ash_test/ash-misc/group_in_braces.right   |  5 ++++
 shell/ash_test/ash-misc/group_in_braces.tests   | 11 +++++++++
 shell/hush.c                                    | 32 ++++++++++++++++++++-----
 shell/hush_test/hush-misc/group_in_braces.right |  5 ++++
 shell/hush_test/hush-misc/group_in_braces.tests | 11 +++++++++
 5 files changed, 58 insertions(+), 6 deletions(-)

diff --git a/shell/ash_test/ash-misc/group_in_braces.right b/shell/ash_test/ash-misc/group_in_braces.right
new file mode 100644
index 0000000..a706449
--- /dev/null
+++ b/shell/ash_test/ash-misc/group_in_braces.right
@@ -0,0 +1,5 @@
+Zero:0
+Zero:0
+Zero:0
+Zero:0
+Zero:0
diff --git a/shell/ash_test/ash-misc/group_in_braces.tests b/shell/ash_test/ash-misc/group_in_braces.tests
new file mode 100755
index 0000000..f6571c3
--- /dev/null
+++ b/shell/ash_test/ash-misc/group_in_braces.tests
@@ -0,0 +1,11 @@
+# Test cases where { cmd } does not require semicolon after "cmd"
+(exit 2); { { true; } }
+echo Zero:$?
+(exit 2); {(true)}
+echo Zero:$?
+(exit 2); { true | { true; } }
+echo Zero:$?
+(exit 2); { while false; do :; done }
+echo Zero:$?
+(exit 2); { case a in b) ;; esac }
+echo Zero:$?
diff --git a/shell/hush.c b/shell/hush.c
index 7c2f157..336de75 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -3915,12 +3915,17 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
 		command->cmd_type = CMD_SUBSHELL;
 	} else {
 		/* bash does not allow "{echo...", requires whitespace */
-		ch = i_getch(input);
-		if (ch != ' ' && ch != '\t' && ch != '\n') {
+		ch = i_peek(input);
+		if (ch != ' ' && ch != '\t' && ch != '\n'
+		 && ch != '('	/* but "{(..." is allowed (without whitespace) */
+		) {
 			syntax_error_unexpected_ch(ch);
 			return 1;
 		}
-		nommu_addchr(&ctx->as_string, ch);
+		if (ch != '(') {
+			ch = i_getch(input);
+			nommu_addchr(&ctx->as_string, ch);
+		}
 	}
 
 	{
@@ -4575,6 +4580,7 @@ static struct pipe *parse_stream(char **pstring,
 		 || dest.has_quoted_part     /* ""{... - non-special */
 		 || (next != ';'             /* }; - special */
 		    && next != ')'           /* }) - special */
+		    && next != '('           /* {( - special */
 		    && next != '&'           /* }& and }&& ... - special */
 		    && next != '|'           /* }|| ... - special */
 		    && !strchr(defifs, next) /* {word - non-special */
@@ -4657,17 +4663,31 @@ static struct pipe *parse_stream(char **pstring,
 		 * Pathological example: { ""}; } should exec "}" cmd
 		 */
 		if (ch == '}') {
-			if (!IS_NULL_CMD(ctx.command) /* cmd } */
-			 || dest.length != 0 /* word} */
+			if (dest.length != 0 /* word} */
 			 || dest.has_quoted_part    /* ""} */
 			) {
 				goto ordinary_char;
 			}
+			if (!IS_NULL_CMD(ctx.command)) { /* cmd } */
+				/* Generally, there should be semicolon: "cmd; }"
+				 * However, bash allows to omit it if "cmd" is
+				 * a group. Examples:
+				 * { { echo 1; } }
+				 * {(echo 1)}
+				 * { echo 0 >&2 | { echo 1; } }
+				 * { while false; do :; done }
+				 * { case a in b) ;; esac }
+				 */
+				if (ctx.command->group)
+					goto term_group;
+				goto ordinary_char;
+			}
 			if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
+				/* Can't be an end of {cmd}, skip the check */
 				goto skip_end_trigger;
 			/* else: } does terminate a group */
 		}
-
+ term_group:
 		if (end_trigger && end_trigger == ch
 		 && (ch != ';' || heredoc_cnt == 0)
 #if ENABLE_HUSH_CASE
diff --git a/shell/hush_test/hush-misc/group_in_braces.right b/shell/hush_test/hush-misc/group_in_braces.right
new file mode 100644
index 0000000..a706449
--- /dev/null
+++ b/shell/hush_test/hush-misc/group_in_braces.right
@@ -0,0 +1,5 @@
+Zero:0
+Zero:0
+Zero:0
+Zero:0
+Zero:0
diff --git a/shell/hush_test/hush-misc/group_in_braces.tests b/shell/hush_test/hush-misc/group_in_braces.tests
new file mode 100755
index 0000000..f6571c3
--- /dev/null
+++ b/shell/hush_test/hush-misc/group_in_braces.tests
@@ -0,0 +1,11 @@
+# Test cases where { cmd } does not require semicolon after "cmd"
+(exit 2); { { true; } }
+echo Zero:$?
+(exit 2); {(true)}
+echo Zero:$?
+(exit 2); { true | { true; } }
+echo Zero:$?
+(exit 2); { while false; do :; done }
+echo Zero:$?
+(exit 2); { case a in b) ;; esac }
+echo Zero:$?


More information about the busybox-cvs mailing list