hush: allow { cmd } to not be terminated by semicolon in some cases

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2016-11-04 18:46:14 +01:00
parent 06b114900f
commit 672a55e606
5 changed files with 58 additions and 6 deletions

View File

@ -0,0 +1,5 @@
Zero:0
Zero:0
Zero:0
Zero:0
Zero:0

View File

@ -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:$?

View File

@ -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

View File

@ -0,0 +1,5 @@
Zero:0
Zero:0
Zero:0
Zero:0
Zero:0

View File

@ -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:$?