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; command->cmd_type = CMD_SUBSHELL;
} else { } else {
/* bash does not allow "{echo...", requires whitespace */ /* bash does not allow "{echo...", requires whitespace */
ch = i_getch(input); ch = i_peek(input);
if (ch != ' ' && ch != '\t' && ch != '\n') { if (ch != ' ' && ch != '\t' && ch != '\n'
&& ch != '(' /* but "{(..." is allowed (without whitespace) */
) {
syntax_error_unexpected_ch(ch); syntax_error_unexpected_ch(ch);
return 1; 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 */ || dest.has_quoted_part /* ""{... - non-special */
|| (next != ';' /* }; - special */ || (next != ';' /* }; - special */
&& next != ')' /* }) - special */ && next != ')' /* }) - special */
&& next != '(' /* {( - special */
&& next != '&' /* }& and }&& ... - special */ && next != '&' /* }& and }&& ... - special */
&& next != '|' /* }|| ... - special */ && next != '|' /* }|| ... - special */
&& !strchr(defifs, next) /* {word - non-special */ && !strchr(defifs, next) /* {word - non-special */
@ -4657,17 +4663,31 @@ static struct pipe *parse_stream(char **pstring,
* Pathological example: { ""}; } should exec "}" cmd * Pathological example: { ""}; } should exec "}" cmd
*/ */
if (ch == '}') { if (ch == '}') {
if (!IS_NULL_CMD(ctx.command) /* cmd } */ if (dest.length != 0 /* word} */
|| dest.length != 0 /* word} */
|| dest.has_quoted_part /* ""} */ || dest.has_quoted_part /* ""} */
) { ) {
goto ordinary_char; 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 | } */ if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
/* Can't be an end of {cmd}, skip the check */
goto skip_end_trigger; goto skip_end_trigger;
/* else: } does terminate a group */ /* else: } does terminate a group */
} }
term_group:
if (end_trigger && end_trigger == ch if (end_trigger && end_trigger == ch
&& (ch != ';' || heredoc_cnt == 0) && (ch != ';' || heredoc_cnt == 0)
#if ENABLE_HUSH_CASE #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:$?