aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2016-11-04 18:46:14 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2016-11-04 18:46:14 +0100
commit672a55e606fc50e4ffe7810bd0d9cd1cf9b980a3 (patch)
treedd1805a175268a0e16ba578e095627e90e73f5fb /shell
parent06b114900fc57cac0e422d26228f4d0aaf5d2288 (diff)
downloadbusybox-w32-672a55e606fc50e4ffe7810bd0d9cd1cf9b980a3.tar.gz
busybox-w32-672a55e606fc50e4ffe7810bd0d9cd1cf9b980a3.tar.bz2
busybox-w32-672a55e606fc50e4ffe7810bd0d9cd1cf9b980a3.zip
hush: allow { cmd } to not be terminated by semicolon in some cases
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r--shell/ash_test/ash-misc/group_in_braces.right5
-rwxr-xr-xshell/ash_test/ash-misc/group_in_braces.tests11
-rw-r--r--shell/hush.c32
-rw-r--r--shell/hush_test/hush-misc/group_in_braces.right5
-rwxr-xr-xshell/hush_test/hush-misc/group_in_braces.tests11
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 000000000..a7064499b
--- /dev/null
+++ b/shell/ash_test/ash-misc/group_in_braces.right
@@ -0,0 +1,5 @@
1Zero:0
2Zero:0
3Zero:0
4Zero:0
5Zero: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 000000000..f6571c35d
--- /dev/null
+++ b/shell/ash_test/ash-misc/group_in_braces.tests
@@ -0,0 +1,11 @@
1# Test cases where { cmd } does not require semicolon after "cmd"
2(exit 2); { { true; } }
3echo Zero:$?
4(exit 2); {(true)}
5echo Zero:$?
6(exit 2); { true | { true; } }
7echo Zero:$?
8(exit 2); { while false; do :; done }
9echo Zero:$?
10(exit 2); { case a in b) ;; esac }
11echo Zero:$?
diff --git a/shell/hush.c b/shell/hush.c
index 7c2f157b8..336de75ad 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -3915,12 +3915,17 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
3915 command->cmd_type = CMD_SUBSHELL; 3915 command->cmd_type = CMD_SUBSHELL;
3916 } else { 3916 } else {
3917 /* bash does not allow "{echo...", requires whitespace */ 3917 /* bash does not allow "{echo...", requires whitespace */
3918 ch = i_getch(input); 3918 ch = i_peek(input);
3919 if (ch != ' ' && ch != '\t' && ch != '\n') { 3919 if (ch != ' ' && ch != '\t' && ch != '\n'
3920 && ch != '(' /* but "{(..." is allowed (without whitespace) */
3921 ) {
3920 syntax_error_unexpected_ch(ch); 3922 syntax_error_unexpected_ch(ch);
3921 return 1; 3923 return 1;
3922 } 3924 }
3923 nommu_addchr(&ctx->as_string, ch); 3925 if (ch != '(') {
3926 ch = i_getch(input);
3927 nommu_addchr(&ctx->as_string, ch);
3928 }
3924 } 3929 }
3925 3930
3926 { 3931 {
@@ -4575,6 +4580,7 @@ static struct pipe *parse_stream(char **pstring,
4575 || dest.has_quoted_part /* ""{... - non-special */ 4580 || dest.has_quoted_part /* ""{... - non-special */
4576 || (next != ';' /* }; - special */ 4581 || (next != ';' /* }; - special */
4577 && next != ')' /* }) - special */ 4582 && next != ')' /* }) - special */
4583 && next != '(' /* {( - special */
4578 && next != '&' /* }& and }&& ... - special */ 4584 && next != '&' /* }& and }&& ... - special */
4579 && next != '|' /* }|| ... - special */ 4585 && next != '|' /* }|| ... - special */
4580 && !strchr(defifs, next) /* {word - non-special */ 4586 && !strchr(defifs, next) /* {word - non-special */
@@ -4657,17 +4663,31 @@ static struct pipe *parse_stream(char **pstring,
4657 * Pathological example: { ""}; } should exec "}" cmd 4663 * Pathological example: { ""}; } should exec "}" cmd
4658 */ 4664 */
4659 if (ch == '}') { 4665 if (ch == '}') {
4660 if (!IS_NULL_CMD(ctx.command) /* cmd } */ 4666 if (dest.length != 0 /* word} */
4661 || dest.length != 0 /* word} */
4662 || dest.has_quoted_part /* ""} */ 4667 || dest.has_quoted_part /* ""} */
4663 ) { 4668 ) {
4664 goto ordinary_char; 4669 goto ordinary_char;
4665 } 4670 }
4671 if (!IS_NULL_CMD(ctx.command)) { /* cmd } */
4672 /* Generally, there should be semicolon: "cmd; }"
4673 * However, bash allows to omit it if "cmd" is
4674 * a group. Examples:
4675 * { { echo 1; } }
4676 * {(echo 1)}
4677 * { echo 0 >&2 | { echo 1; } }
4678 * { while false; do :; done }
4679 * { case a in b) ;; esac }
4680 */
4681 if (ctx.command->group)
4682 goto term_group;
4683 goto ordinary_char;
4684 }
4666 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */ 4685 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
4686 /* Can't be an end of {cmd}, skip the check */
4667 goto skip_end_trigger; 4687 goto skip_end_trigger;
4668 /* else: } does terminate a group */ 4688 /* else: } does terminate a group */
4669 } 4689 }
4670 4690 term_group:
4671 if (end_trigger && end_trigger == ch 4691 if (end_trigger && end_trigger == ch
4672 && (ch != ';' || heredoc_cnt == 0) 4692 && (ch != ';' || heredoc_cnt == 0)
4673#if ENABLE_HUSH_CASE 4693#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 000000000..a7064499b
--- /dev/null
+++ b/shell/hush_test/hush-misc/group_in_braces.right
@@ -0,0 +1,5 @@
1Zero:0
2Zero:0
3Zero:0
4Zero:0
5Zero: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 000000000..f6571c35d
--- /dev/null
+++ b/shell/hush_test/hush-misc/group_in_braces.tests
@@ -0,0 +1,11 @@
1# Test cases where { cmd } does not require semicolon after "cmd"
2(exit 2); { { true; } }
3echo Zero:$?
4(exit 2); {(true)}
5echo Zero:$?
6(exit 2); { true | { true; } }
7echo Zero:$?
8(exit 2); { while false; do :; done }
9echo Zero:$?
10(exit 2); { case a in b) ;; esac }
11echo Zero:$?