summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2025-08-18 13:14:26 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2025-08-18 13:14:26 +0200
commit2bdec03def3054bbd80d5fd23e9bb0c026f85fc4 (patch)
treea7cd2ff8f50b7d93171d4e79d9c3ec72a97c5922
parent2ccb8918190f95fe6119bee46ae4b595a2e8306d (diff)
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>
-rw-r--r--shell/hush.c52
1 files changed, 27 insertions, 25 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 00887d45f..f776a6468 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -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