mirror of
https://git.busybox.net/busybox
synced 2026-02-07 20:50:26 +00:00
hush: allow nested negation "! ! ! CMD" - bash 5.2.15 allows it
Also, deindent "ch == EOF" code branch in parse_stream() function old new delta done_word 799 797 -2 parse_stream 2453 2446 -7 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-9) Total: -9 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
2b0b74e8b4
commit
f161bc628f
3 changed files with 71 additions and 73 deletions
135
shell/hush.c
135
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 &&<whitespace><EOF>' and 'date ||<whitespace><EOF>' 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 && <nl> 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 &&<whitespace><EOF>' and 'date ||<whitespace><EOF>' 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
bash 3.2 fails this
|
||||
hush: syntax error: unexpected !
|
||||
0:0
|
||||
1:1
|
||||
|
|
|
|||
|
|
@ -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:$?
|
||||
|
|
|
|||
Loading…
Reference in a new issue