hush: disentangle keyword detection, no logic changes

function                                             old     new   delta
done_word                                            790     766     -24

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2025-08-18 13:14:26 +02:00
parent 2ccb891819
commit 2bdec03def

View file

@ -3947,7 +3947,7 @@ static void debug_print_tree(struct pipe *pi, int lvl)
# endif
# if ENABLE_HUSH_CASE
[RES_CASE ] = "CASE" ,
[RES_CASE_IN ] = "CASE_IN" ,
[RES_CASE_IN] = "CASE_IN" ,
[RES_MATCH] = "MATCH",
[RES_CASE_BODY] = "CASE_BODY",
[RES_ESAC ] = "ESAC" ,
@ -4289,11 +4289,22 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx)
# endif
const struct reserved_combo *r;
if (ctx->word.has_quoted_part)
return 0;
r = match_reserved_word(ctx->word.data);
if (!r)
return r; /* NULL */
# if ENABLE_HUSH_CASE /* "case" syntax has a curveball */
if (ctx->ctx_res_w == RES_MATCH
&& r->res != RES_ESAC
) {
/* We are at WORD in ";; WORD" or "case .. in WORD".
* Here, only "esac" is a keyword.
* Else WORD is a case pattern, can be keyword-like:
* if) echo got_if;;
* is allowed.
*/
return NULL;
}
# endif
debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
# if ENABLE_HUSH_CASE
@ -4309,7 +4320,7 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx)
return r;
}
# endif
if (ctx->pipe->cmds != ctx->command /* bash disallows: nice | ! cat */
if (ctx->pipe->num_cmds != 0 /* bash disallows: nice | ! cat */
/* || ctx->pipe->pi_inverted - bash used to disallow "! ! true" bash 5.2.15 allows it */
) {
syntax_error_unexpected_ch('!');
@ -4443,36 +4454,26 @@ static int done_word(struct parse_context *ctx)
}
#if HAS_KEYWORDS
# if ENABLE_HUSH_CASE
if (ctx->ctx_res_w == RES_MATCH
&& (ctx->word.has_quoted_part
|| strcmp(ctx->word.data, "esac") != 0
)
) { /* ";; WORD" but not ";; esac" */
/* Do not match WORD as keyword:
* the WORD is a case match, can be keyword-like:
* if) echo got_if;;
* is allowed.
*/
} else
# endif
# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB
&& !ctx->word.has_quoted_part
&& strcmp(ctx->word.data, "]]") == 0
) {
/* allow "[[ ]] >file" etc */
/* End test2-specific parsing rules */
/* Allow "[[ ]] >file" etc (> is a redirect symbol again) */
command->cmd_type = CMD_SINGLEWORD_NOGLOB;
} else
# endif
if (!command->argv /* if it's the first word of command... */
&& !command->redirects /* no redirects yet... disallows: </dev/null if true; then... */
/* Is it a place where keyword can appear? */
if (!command->argv /* if it's the first word of command... */
&& !ctx->word.has_quoted_part /* ""WORD never matches any keywords */
&& !command->redirects /* no redirects yet... disallows: </dev/null if true; then... */
# if ENABLE_HUSH_LOOPS
&& ctx->ctx_res_w != RES_FOR /* not after FOR or IN */
&& ctx->ctx_res_w != RES_FOR /* not after "for" or "in" */
&& ctx->ctx_res_w != RES_IN
# endif
# if ENABLE_HUSH_CASE
&& ctx->ctx_res_w != RES_CASE /* not after CASE */
&& ctx->ctx_res_w != RES_CASE /* not after "case" */
# endif
) {
const struct reserved_combo *reserved;
@ -4481,10 +4482,10 @@ static int done_word(struct parse_context *ctx)
if (reserved) {
# if ENABLE_HUSH_LINENO_VAR
/* Case:
* "while ...; do
* cmd ..."
* while ...; do
* CMD
* If we don't close the pipe _now_, immediately after "do", lineno logic
* sees "cmd" as starting at "do" - i.e., at the previous line.
* sees CMD as starting at "do" - i.e., at the previous line.
*/
if (0
IF_HUSH_IF(|| reserved->res == RES_THEN)
@ -4502,6 +4503,7 @@ static int done_word(struct parse_context *ctx)
}
# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
if (strcmp(ctx->word.data, "[[") == 0) {
/* Inside [[ ]], parsing rules are different */
command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB;
} else
# endif