diff --git a/shell/hush.c b/shell/hush.c index bca27e038..b32d903ae 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -4190,12 +4190,12 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx) # endif if (r->flag == 0) { /* '!' */ if (ctx->pipe->cmds != ctx->command /* bash disallows: nice | ! cat */ - || ctx->pipe->pi_inverted /* bash disallows: ! ! true */ + /* || ctx->pipe->pi_inverted - bash used to disallow "! ! true" bash 5.2.15 allows it */ ) { syntax_error_unexpected_ch('!'); ctx->ctx_res_w = RES_SNTX; } - ctx->pipe->pi_inverted = 1; + ctx->pipe->pi_inverted = 1 - ctx->pipe->pi_inverted; return r; } if (r->flag & FLAG_START) { @@ -5588,6 +5588,7 @@ static struct pipe *parse_stream(char **pstring, struct in_str *input, int end_trigger) { + struct pipe *pi; struct parse_context ctx; int heredoc_cnt; @@ -5622,64 +5623,8 @@ static struct pipe *parse_stream(char **pstring, ch = i_getch(input); debug_printf_parse(": ch:%c (%d) globprotect:%d\n", ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_GLOBPROTECT_CHARS)); - if (ch == EOF) { - struct pipe *pi; - - if (heredoc_cnt) { - syntax_error_unterm_str("here document"); - goto parse_error_exitcode1; - } - if (end_trigger == ')') { - syntax_error_unterm_ch('('); - goto parse_error_exitcode1; - } - if (end_trigger == '}') { - syntax_error_unterm_ch('{'); - goto parse_error_exitcode1; - } - - if (done_word(&ctx)) { - goto parse_error_exitcode1; - } - o_free_and_set_NULL(&ctx.word); - if (done_pipe(&ctx, PIPE_SEQ)) { - /* Testcase: sh -c 'date |' */ - syntax_error_unterm_ch('|'); - goto parse_error_exitcode1; - } -// TODO: catch 'date &&' and 'date ||' too - - /* Do we sit inside of any if's, loops or case's? */ - if (HAS_KEYWORDS - IF_HAS_KEYWORDS(&& (ctx.ctx_res_w != RES_NONE || ctx.old_flag != 0)) - ) { - syntax_error_unterm_str("compound statement"); - goto parse_error_exitcode1; - } - - pi = ctx.list_head; - /* If we got nothing... */ - if (pi->num_cmds == 0 - IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE) - ) { - free_pipe_list(pi); - pi = NULL; - } -#if !BB_MMU - debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data); - if (pstring) - *pstring = ctx.as_string.data; - else - o_free(&ctx.as_string); -#endif - // heredoc_cnt must be 0 here anyway - //if (heredoc_cnt_ptr) - // *heredoc_cnt_ptr = heredoc_cnt; - debug_leave(); - debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt); - debug_printf_parse("parse_stream return %p: EOF\n", pi); - return pi; - } + if (ch == EOF) + break; /* Handle "'" and "\" first, as they won't play nice with * i_peek_and_eat_bkslash_nl() anyway: @@ -5705,7 +5650,7 @@ static struct pipe *parse_stream(char **pstring, /* bash-4.3.43 was removing backslash, * but 4.4.19 retains it, most other shells too */ - continue; /* get next char */ + break; } /* Example: echo Hello \2>file * we need to know that word 2 is quoted @@ -5719,7 +5664,7 @@ static struct pipe *parse_stream(char **pstring, if (ch == '\'') { ctx.word.has_quoted_part = 1; next = i_getch(input); - if (next == '\'' && !ctx.pending_redirect) + if (next == '\'' && !ctx.pending_redirect/*why?*/) goto insert_empty_quoted_str_marker; ch = next; @@ -5835,7 +5780,7 @@ static struct pipe *parse_stream(char **pstring, * a "cmd1 && cmd2 &" construct, * cmd1 may need to run in BG). */ - struct pipe *pi = ctx.list_head; + pi = ctx.list_head; if (pi->num_cmds != 0 /* check #1 */ && pi->followup != PIPE_BG /* check #2 */ ) { @@ -6206,12 +6151,66 @@ static struct pipe *parse_stream(char **pstring, } } /* while (1) */ + /* Reached EOF */ + if (heredoc_cnt) { + syntax_error_unterm_str("here document"); + goto parse_error_exitcode1; + } + if (end_trigger == ')') { + syntax_error_unterm_ch('('); + goto parse_error_exitcode1; + } + if (end_trigger == '}') { + syntax_error_unterm_ch('{'); + goto parse_error_exitcode1; + } + + if (done_word(&ctx)) { + goto parse_error_exitcode1; + } + o_free_and_set_NULL(&ctx.word); + if (done_pipe(&ctx, PIPE_SEQ)) { + /* Testcase: sh -c 'date |' */ + syntax_error_unterm_ch('|'); + goto parse_error_exitcode1; + } +// TODO: catch 'date &&' and 'date ||' too + +#if HAS_KEYWORDS + /* Do we sit inside of any if's, loops or case's? */ + if (ctx.ctx_res_w != RES_NONE || ctx.old_flag != 0) { + syntax_error_unterm_str("compound statement"); + goto parse_error_exitcode1; + } +#endif + pi = ctx.list_head; + /* If we got nothing... */ + if (pi->num_cmds == 0 + IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE) + ) { + free_pipe_list(pi); + pi = NULL; + } +#if !BB_MMU + debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data); + if (pstring) + *pstring = ctx.as_string.data; + else + o_free(&ctx.as_string); +#endif + // heredoc_cnt must be 0 here anyway + //if (heredoc_cnt_ptr) + // *heredoc_cnt_ptr = heredoc_cnt; + debug_leave(); + debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt); + debug_printf_parse("parse_stream return %p: EOF\n", pi); + return pi; + parse_error_exitcode1: G.last_exitcode = 1; parse_error: { - struct parse_context *pctx; - IF_HAS_KEYWORDS(struct parse_context *p2;) + struct parse_context *pctx IF_HAS_KEYWORDS(, *p2;); /* Clean up allocated tree. * Sample for finding leaks on syntax error recovery path. @@ -6226,8 +6225,7 @@ static struct pipe *parse_stream(char **pstring, /* Update pipe/command counts, * otherwise freeing may miss some */ done_pipe(pctx, PIPE_SEQ); - debug_printf_clean("freeing list %p from ctx %p\n", - pctx->list_head, pctx); + debug_printf_clean("freeing list %p from ctx %p\n", pctx->list_head, pctx); debug_print_tree(pctx->list_head, 0); free_pipe_list(pctx->list_head); debug_printf_clean("freed list %p\n", pctx->list_head); @@ -6235,9 +6233,8 @@ static struct pipe *parse_stream(char **pstring, o_free(&pctx->as_string); #endif IF_HAS_KEYWORDS(p2 = pctx->stack;) - if (pctx != &ctx) { + if (pctx != &ctx) free(pctx); - } IF_HAS_KEYWORDS(pctx = p2;) } while (HAS_KEYWORDS && pctx); diff --git a/shell/hush_test/hush-misc/syntax_err_negate.right b/shell/hush_test/hush-misc/syntax_err_negate.right index 0b8d211c7..39db799ea 100644 --- a/shell/hush_test/hush-misc/syntax_err_negate.right +++ b/shell/hush_test/hush-misc/syntax_err_negate.right @@ -1,2 +1,2 @@ -bash 3.2 fails this -hush: syntax error: unexpected ! +0:0 +1:1 diff --git a/shell/hush_test/hush-misc/syntax_err_negate.tests b/shell/hush_test/hush-misc/syntax_err_negate.tests index d61b1b09f..64f0f8a75 100755 --- a/shell/hush_test/hush-misc/syntax_err_negate.tests +++ b/shell/hush_test/hush-misc/syntax_err_negate.tests @@ -1,2 +1,3 @@ -echo bash 3.2 fails this -! ! true +# bash 3.2 fails this, 5.2.15 allows +! ! true; echo 0:$? +! ! ! true; echo 1:$?