diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2025-08-12 17:55:48 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2025-08-12 17:56:58 +0200 |
| commit | 5ecbed0e2660f74f3690de52387a7c2f3ea3f2d2 (patch) | |
| tree | adc06cd42a26b8f76260ed1ea38c538218d1457e /shell | |
| parent | ab1de7df999c43f12be26d90e6a143ecfa966630 (diff) | |
| download | busybox-w32-5ecbed0e2660f74f3690de52387a7c2f3ea3f2d2.tar.gz busybox-w32-5ecbed0e2660f74f3690de52387a7c2f3ea3f2d2.tar.bz2 busybox-w32-5ecbed0e2660f74f3690de52387a7c2f3ea3f2d2.zip | |
hush: do not segfault on "for </dev/null v in..."
This is not accepted by bash, we may also disallow this,
but for now, at least do not crash
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 233 | ||||
| -rw-r--r-- | shell/hush_test/hush-redir/redir_and_constructs1.right | 2 | ||||
| -rwxr-xr-x | shell/hush_test/hush-redir/redir_and_constructs1.tests | 2 |
3 files changed, 124 insertions, 113 deletions
diff --git a/shell/hush.c b/shell/hush.c index 254f39943..fe3d77c65 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -3914,9 +3914,11 @@ static struct pipe *new_pipe(void) | |||
| 3914 | return pi; | 3914 | return pi; |
| 3915 | } | 3915 | } |
| 3916 | 3916 | ||
| 3917 | /* Command (member of a pipe) is complete, or we start a new pipe | 3917 | /* Parsing of command (member of a pipe) is completed. |
| 3918 | * if ctx->command is NULL. | 3918 | * If it's not null, a new empty command structure is added |
| 3919 | * No errors possible here. | 3919 | * to the current pipe, and ctx->command is set to it. |
| 3920 | * Return the current number of already parsed commands in the pipe. | ||
| 3921 | * No errors are possible here. | ||
| 3920 | */ | 3922 | */ |
| 3921 | static int done_command(struct parse_context *ctx) | 3923 | static int done_command(struct parse_context *ctx) |
| 3922 | { | 3924 | { |
| @@ -3932,17 +3934,16 @@ static int done_command(struct parse_context *ctx) | |||
| 3932 | ctx->pending_redirect = NULL; | 3934 | ctx->pending_redirect = NULL; |
| 3933 | } | 3935 | } |
| 3934 | #endif | 3936 | #endif |
| 3935 | |||
| 3936 | if (command) { | 3937 | if (command) { |
| 3937 | if (IS_NULL_CMD(command)) { | 3938 | if (IS_NULL_CMD(command)) { |
| 3938 | debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); | 3939 | debug_printf_parse("done_command: skipping null cmd, num_cmds:%d\n", pi->num_cmds); |
| 3939 | goto clear_and_ret; | 3940 | goto clear_and_ret; |
| 3940 | } | 3941 | } |
| 3941 | pi->num_cmds++; | 3942 | pi->num_cmds++; |
| 3942 | debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); | 3943 | debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); |
| 3943 | //debug_print_tree(ctx->list_head, 20); | 3944 | //debug_print_tree(ctx->list_head, 20); |
| 3944 | } else { | 3945 | } else { |
| 3945 | debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds); | 3946 | debug_printf_parse("done_command: initializing, num_cmds:%d\n", pi->num_cmds); |
| 3946 | } | 3947 | } |
| 3947 | 3948 | ||
| 3948 | /* Only real trickiness here is that the uncommitted | 3949 | /* Only real trickiness here is that the uncommitted |
| @@ -3953,28 +3954,33 @@ static int done_command(struct parse_context *ctx) | |||
| 3953 | memset(command, 0, sizeof(*command)); | 3954 | memset(command, 0, sizeof(*command)); |
| 3954 | #if ENABLE_HUSH_LINENO_VAR | 3955 | #if ENABLE_HUSH_LINENO_VAR |
| 3955 | command->lineno = G.parse_lineno; | 3956 | command->lineno = G.parse_lineno; |
| 3956 | debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno); | 3957 | debug_printf_parse("command->lineno=G.parse_lineno (%u)\n", G.parse_lineno); |
| 3957 | #endif | 3958 | #endif |
| 3958 | return pi->num_cmds; /* used only for 0/nonzero check */ | 3959 | return pi->num_cmds; |
| 3959 | } | 3960 | } |
| 3960 | 3961 | ||
| 3962 | /* Parsing of a pipe is completed. | ||
| 3963 | * Finish prsing current command via done_command(). | ||
| 3964 | * (If the pipe is not empty, but done_command() did not change the number | ||
| 3965 | * of commands in pipe, return value is 1. Used for catching syntax errors) | ||
| 3966 | */ | ||
| 3961 | static int done_pipe(struct parse_context *ctx, pipe_style type) | 3967 | static int done_pipe(struct parse_context *ctx, pipe_style type) |
| 3962 | { | 3968 | { |
| 3963 | int not_null_pipe; | 3969 | int num_cmds; |
| 3964 | int oldnum; | 3970 | int oldnum; |
| 3965 | int nullcommand; | 3971 | int last_cmd_is_null; |
| 3966 | 3972 | ||
| 3967 | debug_printf_parse("done_pipe entered, followup %d\n", type); | 3973 | debug_printf_parse("done_pipe entered, followup %d\n", type); |
| 3968 | /* Close previous command */ | 3974 | /* Close previous command */ |
| 3969 | oldnum = ctx->pipe->num_cmds; | 3975 | oldnum = ctx->pipe->num_cmds; |
| 3970 | not_null_pipe = done_command(ctx); | 3976 | num_cmds = done_command(ctx); |
| 3971 | 3977 | ||
| 3972 | /* This is true if this was a non-empty pipe, | 3978 | /* This is true if this was a non-empty pipe, |
| 3973 | * but done_command didn't add a new member to it. | 3979 | * but done_command didn't add a new member to it. |
| 3974 | * Usually it is a syntax error. | 3980 | * Usually it is a syntax error. |
| 3975 | * Examples: "date | | ...", "date | ; ..." | 3981 | * Examples: "date | | ...", "date | ; ..." |
| 3976 | */ | 3982 | */ |
| 3977 | nullcommand = (oldnum && not_null_pipe == oldnum); | 3983 | last_cmd_is_null = (oldnum != 0 && num_cmds == oldnum); |
| 3978 | 3984 | ||
| 3979 | #if HAS_KEYWORDS | 3985 | #if HAS_KEYWORDS |
| 3980 | ctx->pipe->pi_inverted = ctx->ctx_inverted; | 3986 | ctx->pipe->pi_inverted = ctx->ctx_inverted; |
| @@ -4017,7 +4023,7 @@ static int done_pipe(struct parse_context *ctx, pipe_style type) | |||
| 4017 | ctx->list_head = ctx->pipe = pi; | 4023 | ctx->list_head = ctx->pipe = pi; |
| 4018 | /* for cases like "cmd && &", do not be tricked by last command | 4024 | /* for cases like "cmd && &", do not be tricked by last command |
| 4019 | * being null - the entire {...} & is NOT null! */ | 4025 | * being null - the entire {...} & is NOT null! */ |
| 4020 | not_null_pipe = 1; | 4026 | num_cmds = 1; |
| 4021 | } else { | 4027 | } else { |
| 4022 | no_conv: | 4028 | no_conv: |
| 4023 | ctx->pipe->followup = type; | 4029 | ctx->pipe->followup = type; |
| @@ -4026,7 +4032,7 @@ static int done_pipe(struct parse_context *ctx, pipe_style type) | |||
| 4026 | /* Without this check, even just <enter> on command line generates | 4032 | /* Without this check, even just <enter> on command line generates |
| 4027 | * tree of three NOPs (!). Which is harmless but annoying. | 4033 | * tree of three NOPs (!). Which is harmless but annoying. |
| 4028 | * IOW: it is safe to do it unconditionally. */ | 4034 | * IOW: it is safe to do it unconditionally. */ |
| 4029 | if (not_null_pipe | 4035 | if (num_cmds != 0 |
| 4030 | #if ENABLE_HUSH_IF | 4036 | #if ENABLE_HUSH_IF |
| 4031 | || ctx->ctx_res_w == RES_FI | 4037 | || ctx->ctx_res_w == RES_FI |
| 4032 | #endif | 4038 | #endif |
| @@ -4041,8 +4047,8 @@ static int done_pipe(struct parse_context *ctx, pipe_style type) | |||
| 4041 | ) { | 4047 | ) { |
| 4042 | struct pipe *new_p; | 4048 | struct pipe *new_p; |
| 4043 | debug_printf_parse("done_pipe: adding new pipe: " | 4049 | debug_printf_parse("done_pipe: adding new pipe: " |
| 4044 | "not_null_pipe:%d ctx->ctx_res_w:%d\n", | 4050 | "num_cmds:%d ctx->ctx_res_w:%d\n", |
| 4045 | not_null_pipe, ctx->ctx_res_w); | 4051 | num_cmds, ctx->ctx_res_w); |
| 4046 | new_p = new_pipe(); | 4052 | new_p = new_pipe(); |
| 4047 | ctx->pipe->next = new_p; | 4053 | ctx->pipe->next = new_p; |
| 4048 | ctx->pipe = new_p; | 4054 | ctx->pipe = new_p; |
| @@ -4071,8 +4077,8 @@ static int done_pipe(struct parse_context *ctx, pipe_style type) | |||
| 4071 | done_command(ctx); | 4077 | done_command(ctx); |
| 4072 | //debug_print_tree(ctx->list_head, 10); | 4078 | //debug_print_tree(ctx->list_head, 10); |
| 4073 | } | 4079 | } |
| 4074 | debug_printf_parse("done_pipe return:%d\n", nullcommand); | 4080 | debug_printf_parse("done_pipe return: last_cmd_is_null:%d\n", last_cmd_is_null); |
| 4075 | return nullcommand; | 4081 | return last_cmd_is_null; |
| 4076 | } | 4082 | } |
| 4077 | 4083 | ||
| 4078 | static void initialize_context(struct parse_context *ctx) | 4084 | static void initialize_context(struct parse_context *ctx) |
| @@ -4255,7 +4261,10 @@ static const struct reserved_combo* reserved_word(struct parse_context *ctx) | |||
| 4255 | } | 4261 | } |
| 4256 | #endif /* HAS_KEYWORDS */ | 4262 | #endif /* HAS_KEYWORDS */ |
| 4257 | 4263 | ||
| 4258 | /* Word is complete, look at it and update parsing context. | 4264 | /* Parsing of a word is complete. |
| 4265 | * Look at it and update current command: | ||
| 4266 | * update current command's argv/cmd_type/etc, fill in redirect name and type, | ||
| 4267 | * check reserved-ness and assignment-ness, etc... | ||
| 4259 | * Normal return is 0. Syntax errors return 1. | 4268 | * Normal return is 0. Syntax errors return 1. |
| 4260 | * Note: on return, word is reset, but not o_free'd! | 4269 | * Note: on return, word is reset, but not o_free'd! |
| 4261 | */ | 4270 | */ |
| @@ -4292,7 +4301,7 @@ static int done_word(struct parse_context *ctx) | |||
| 4292 | // as written: | 4301 | // as written: |
| 4293 | // <<EOF$t | 4302 | // <<EOF$t |
| 4294 | // <<EOF$((1)) | 4303 | // <<EOF$((1)) |
| 4295 | // <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug] | 4304 | // <<EOF`true` [bash 4.3.43 bug: this case also makes heredoc "quoted", a-la <<"EOF". Fixed by 5.2.15] |
| 4296 | 4305 | ||
| 4297 | ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data); | 4306 | ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data); |
| 4298 | /* Cater for >\file case: | 4307 | /* Cater for >\file case: |
| @@ -4309,38 +4318,41 @@ static int done_word(struct parse_context *ctx) | |||
| 4309 | } | 4318 | } |
| 4310 | debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data); | 4319 | debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data); |
| 4311 | ctx->pending_redirect = NULL; | 4320 | ctx->pending_redirect = NULL; |
| 4312 | } else { | 4321 | goto ret; |
| 4322 | } | ||
| 4323 | |||
| 4313 | #if HAS_KEYWORDS | 4324 | #if HAS_KEYWORDS |
| 4314 | # if ENABLE_HUSH_CASE | 4325 | # if ENABLE_HUSH_CASE |
| 4315 | if (ctx->ctx_dsemicolon | 4326 | if (ctx->ctx_dsemicolon |
| 4316 | && strcmp(ctx->word.data, "esac") != 0 /* not "... pattern) cmd;; esac" */ | 4327 | && strcmp(ctx->word.data, "esac") != 0 /* not "... pattern) cmd;; esac" */ |
| 4317 | ) { | 4328 | ) { |
| 4318 | /* already done when ctx_dsemicolon was set to 1: */ | 4329 | /* already done when ctx_dsemicolon was set to 1: */ |
| 4319 | /* ctx->ctx_res_w = RES_MATCH; */ | 4330 | /* ctx->ctx_res_w = RES_MATCH; */ |
| 4320 | ctx->ctx_dsemicolon = 0; | 4331 | ctx->ctx_dsemicolon = 0; |
| 4321 | } else | 4332 | } else |
| 4322 | # endif | 4333 | # endif |
| 4323 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | 4334 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) |
| 4324 | if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB | 4335 | if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB |
| 4325 | && strcmp(ctx->word.data, "]]") == 0 | 4336 | && strcmp(ctx->word.data, "]]") == 0 |
| 4326 | ) { | 4337 | ) { |
| 4327 | /* allow "[[ ]] >file" etc */ | 4338 | /* allow "[[ ]] >file" etc */ |
| 4328 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | 4339 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; |
| 4329 | } else | 4340 | } else |
| 4330 | # endif | 4341 | # endif |
| 4331 | if (!command->argv /* if it's the first word... */ | 4342 | if (!command->argv /* if it's the first word... */ |
| 4343 | && !command->redirects /* and no redirects yet... try: </dev/null ! true; echo $? */ | ||
| 4332 | # if ENABLE_HUSH_LOOPS | 4344 | # if ENABLE_HUSH_LOOPS |
| 4333 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ | 4345 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ |
| 4334 | && ctx->ctx_res_w != RES_IN | 4346 | && ctx->ctx_res_w != RES_IN |
| 4335 | # endif | 4347 | # endif |
| 4336 | # if ENABLE_HUSH_CASE | 4348 | # if ENABLE_HUSH_CASE |
| 4337 | && ctx->ctx_res_w != RES_CASE | 4349 | && ctx->ctx_res_w != RES_CASE |
| 4338 | # endif | 4350 | # endif |
| 4339 | ) { | 4351 | ) { |
| 4340 | const struct reserved_combo *reserved; | 4352 | const struct reserved_combo *reserved; |
| 4341 | reserved = reserved_word(ctx); | 4353 | reserved = reserved_word(ctx); |
| 4342 | debug_printf_parse("checking for reserved-ness: %d\n", !!reserved); | 4354 | debug_printf_parse("checking for reserved-ness: %d\n", !!reserved); |
| 4343 | if (reserved) { | 4355 | if (reserved) { |
| 4344 | # if ENABLE_HUSH_LINENO_VAR | 4356 | # if ENABLE_HUSH_LINENO_VAR |
| 4345 | /* Case: | 4357 | /* Case: |
| 4346 | * "while ...; do | 4358 | * "while ...; do |
| @@ -4348,80 +4360,79 @@ static int done_word(struct parse_context *ctx) | |||
| 4348 | * If we don't close the pipe _now_, immediately after "do", lineno logic | 4360 | * If we don't close the pipe _now_, immediately after "do", lineno logic |
| 4349 | * sees "cmd" as starting at "do" - i.e., at the previous line. | 4361 | * sees "cmd" as starting at "do" - i.e., at the previous line. |
| 4350 | */ | 4362 | */ |
| 4351 | if (0 | 4363 | if (0 |
| 4352 | IF_HUSH_IF(|| reserved->res == RES_THEN) | 4364 | IF_HUSH_IF(|| reserved->res == RES_THEN) |
| 4353 | IF_HUSH_IF(|| reserved->res == RES_ELIF) | 4365 | IF_HUSH_IF(|| reserved->res == RES_ELIF) |
| 4354 | IF_HUSH_IF(|| reserved->res == RES_ELSE) | 4366 | IF_HUSH_IF(|| reserved->res == RES_ELSE) |
| 4355 | IF_HUSH_LOOPS(|| reserved->res == RES_DO) | 4367 | IF_HUSH_LOOPS(|| reserved->res == RES_DO) |
| 4356 | ) { | 4368 | ) { |
| 4357 | done_pipe(ctx, PIPE_SEQ); | 4369 | done_pipe(ctx, PIPE_SEQ); |
| 4358 | } | ||
| 4359 | # endif | ||
| 4360 | o_reset_to_empty_unquoted(&ctx->word); | ||
| 4361 | debug_printf_parse("done_word return %d\n", | ||
| 4362 | (ctx->ctx_res_w == RES_SNTX)); | ||
| 4363 | return (ctx->ctx_res_w == RES_SNTX); | ||
| 4364 | } | 4370 | } |
| 4371 | # endif | ||
| 4372 | o_reset_to_empty_unquoted(&ctx->word); | ||
| 4373 | debug_printf_parse("done_word return %d\n", | ||
| 4374 | (ctx->ctx_res_w == RES_SNTX)); | ||
| 4375 | return (ctx->ctx_res_w == RES_SNTX); | ||
| 4376 | } | ||
| 4365 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | 4377 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) |
| 4366 | if (strcmp(ctx->word.data, "[[") == 0) { | 4378 | if (strcmp(ctx->word.data, "[[") == 0) { |
| 4367 | command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB; | 4379 | command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB; |
| 4368 | } else | 4380 | } else |
| 4369 | # endif | 4381 | # endif |
| 4370 | # if defined(CMD_SINGLEWORD_NOGLOB) | 4382 | # if defined(CMD_SINGLEWORD_NOGLOB) |
| 4371 | if (0 | 4383 | if (0 |
| 4372 | /* In bash, local/export/readonly are special, args | 4384 | /* In bash, local/export/readonly are special, args |
| 4373 | * are assignments and therefore expansion of them | 4385 | * are assignments and therefore expansion of them |
| 4374 | * should be "one-word" expansion: | 4386 | * should be "one-word" expansion: |
| 4375 | * $ export i=`echo 'a b'` # one arg: "i=a b" | 4387 | * $ export i=`echo 'a b'` # one arg: "i=a b" |
| 4376 | * compare with: | 4388 | * compare with: |
| 4377 | * $ ls i=`echo 'a b'` # two args: "i=a" and "b" | 4389 | * $ ls i=`echo 'a b'` # two args: "i=a" and "b" |
| 4378 | * ls: cannot access i=a: No such file or directory | 4390 | * ls: cannot access i=a: No such file or directory |
| 4379 | * ls: cannot access b: No such file or directory | 4391 | * ls: cannot access b: No such file or directory |
| 4380 | * Note: bash 3.2.33(1) does this only if export word | 4392 | * Note: bash 3.2.33(1) does this only if export word |
| 4381 | * itself is not quoted: | 4393 | * itself is not quoted: |
| 4382 | * $ export i=`echo 'aaa bbb'`; echo "$i" | 4394 | * $ export i=`echo 'aaa bbb'`; echo "$i" |
| 4383 | * aaa bbb | 4395 | * aaa bbb |
| 4384 | * $ "export" i=`echo 'aaa bbb'`; echo "$i" | 4396 | * $ "export" i=`echo 'aaa bbb'`; echo "$i" |
| 4385 | * aaa | 4397 | * aaa |
| 4386 | */ | 4398 | */ |
| 4387 | IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0) | 4399 | IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0) |
| 4388 | IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0) | 4400 | IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0) |
| 4389 | IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0) | 4401 | IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0) |
| 4390 | ) { | 4402 | ) { |
| 4391 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | 4403 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; |
| 4392 | } | 4404 | } |
| 4393 | # else | 4405 | # else |
| 4394 | { /* empty block to pair "if ... else" */ } | 4406 | { /* empty block to pair "if ... else" */ } |
| 4395 | # endif | 4407 | # endif |
| 4396 | } | 4408 | } |
| 4397 | #endif /* HAS_KEYWORDS */ | 4409 | #endif /* HAS_KEYWORDS */ |
| 4398 | 4410 | ||
| 4399 | if (command->group) { | 4411 | if (command->group) { |
| 4400 | /* "{ echo foo; } echo bar" - bad */ | 4412 | /* "{ echo foo; } echo bar" - bad */ |
| 4401 | syntax_error_at(ctx->word.data); | 4413 | syntax_error_at(ctx->word.data); |
| 4402 | debug_printf_parse("done_word return 1: syntax error, " | 4414 | debug_printf_parse("done_word return 1: syntax error, " |
| 4403 | "groups and arglists don't mix\n"); | 4415 | "groups and arglists don't mix\n"); |
| 4404 | return 1; | 4416 | return 1; |
| 4405 | } | 4417 | } |
| 4406 | 4418 | ||
| 4407 | /* If this word wasn't an assignment, next ones definitely | 4419 | /* If this word wasn't an assignment, next ones definitely |
| 4408 | * can't be assignments. Even if they look like ones. */ | 4420 | * can't be assignments. Even if they look like ones. */ |
| 4409 | if (ctx->is_assignment != DEFINITELY_ASSIGNMENT | 4421 | if (ctx->is_assignment != DEFINITELY_ASSIGNMENT |
| 4410 | && ctx->is_assignment != WORD_IS_KEYWORD | 4422 | && ctx->is_assignment != WORD_IS_KEYWORD |
| 4411 | ) { | 4423 | ) { |
| 4412 | ctx->is_assignment = NOT_ASSIGNMENT; | 4424 | ctx->is_assignment = NOT_ASSIGNMENT; |
| 4413 | } else { | 4425 | } else { |
| 4414 | if (ctx->is_assignment == DEFINITELY_ASSIGNMENT) { | 4426 | if (ctx->is_assignment == DEFINITELY_ASSIGNMENT) { |
| 4415 | command->assignment_cnt++; | 4427 | command->assignment_cnt++; |
| 4416 | debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt); | 4428 | debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt); |
| 4417 | } | ||
| 4418 | debug_printf_parse("ctx->is_assignment was:'%s'\n", assignment_flag[ctx->is_assignment]); | ||
| 4419 | ctx->is_assignment = MAYBE_ASSIGNMENT; | ||
| 4420 | } | 4429 | } |
| 4421 | debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]); | 4430 | debug_printf_parse("ctx->is_assignment was:'%s'\n", assignment_flag[ctx->is_assignment]); |
| 4422 | command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data)); | 4431 | ctx->is_assignment = MAYBE_ASSIGNMENT; |
| 4423 | debug_print_strings("word appended to argv", command->argv); | ||
| 4424 | } | 4432 | } |
| 4433 | debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]); | ||
| 4434 | command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data)); | ||
| 4435 | debug_print_strings("word appended to argv", command->argv); | ||
| 4425 | 4436 | ||
| 4426 | #if ENABLE_HUSH_LOOPS | 4437 | #if ENABLE_HUSH_LOOPS |
| 4427 | if (ctx->ctx_res_w == RES_FOR) { | 4438 | if (ctx->ctx_res_w == RES_FOR) { |
| @@ -4446,8 +4457,8 @@ static int done_word(struct parse_context *ctx) | |||
| 4446 | } | 4457 | } |
| 4447 | #endif | 4458 | #endif |
| 4448 | 4459 | ||
| 4460 | ret: | ||
| 4449 | o_reset_to_empty_unquoted(&ctx->word); | 4461 | o_reset_to_empty_unquoted(&ctx->word); |
| 4450 | |||
| 4451 | debug_printf_parse("done_word return 0\n"); | 4462 | debug_printf_parse("done_word return 0\n"); |
| 4452 | return 0; | 4463 | return 0; |
| 4453 | } | 4464 | } |
| @@ -4770,8 +4781,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 4770 | struct in_str *input, | 4781 | struct in_str *input, |
| 4771 | int end_trigger); | 4782 | int end_trigger); |
| 4772 | 4783 | ||
| 4773 | /* Returns number of heredocs not yet consumed, | 4784 | /* Returns number of heredocs not yet consumed, or -1 on error. |
| 4774 | * or -1 on error. | ||
| 4775 | */ | 4785 | */ |
| 4776 | static int parse_group(struct parse_context *ctx, | 4786 | static int parse_group(struct parse_context *ctx, |
| 4777 | struct in_str *input, int ch) | 4787 | struct in_str *input, int ch) |
| @@ -4832,12 +4842,9 @@ static int parse_group(struct parse_context *ctx, | |||
| 4832 | if (command->argv /* word [word]{... */ | 4842 | if (command->argv /* word [word]{... */ |
| 4833 | || ctx->word.length /* word{... */ | 4843 | || ctx->word.length /* word{... */ |
| 4834 | || ctx->word.has_quoted_part /* ""{... */ | 4844 | || ctx->word.has_quoted_part /* ""{... */ |
| 4835 | ) { | 4845 | ) |
| 4836 | syntax_error(NULL); | ||
| 4837 | debug_printf_parse("parse_group return -1: " | 4846 | debug_printf_parse("parse_group return -1: " |
| 4838 | "syntax error, groups and arglists don't mix\n"); | 4847 | "syntax error, groups and arglists don't mix\n"); |
| 4839 | return -1; | ||
| 4840 | } | ||
| 4841 | #endif | 4848 | #endif |
| 4842 | 4849 | ||
| 4843 | IF_HUSH_FUNCTIONS(skip:) | 4850 | IF_HUSH_FUNCTIONS(skip:) |
diff --git a/shell/hush_test/hush-redir/redir_and_constructs1.right b/shell/hush_test/hush-redir/redir_and_constructs1.right new file mode 100644 index 000000000..232cd8734 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_and_constructs1.right | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | hush: can't execute '!': No such file or directory | ||
| 2 | 127:127 | ||
diff --git a/shell/hush_test/hush-redir/redir_and_constructs1.tests b/shell/hush_test/hush-redir/redir_and_constructs1.tests new file mode 100755 index 000000000..a92731e04 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_and_constructs1.tests | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | # Reserved words are not recognized after redirects | ||
| 2 | </dev/null ! true; echo 127:$? | ||
