hush: fix misparsing of "... do eval a= ...". Closes 3721
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
eafc558f94
commit
29f9b7268a
77
shell/hush.c
77
shell/hush.c
@ -449,6 +449,15 @@ enum {
|
|||||||
/* Used for initialization: o_string foo = NULL_O_STRING; */
|
/* Used for initialization: o_string foo = NULL_O_STRING; */
|
||||||
#define NULL_O_STRING { NULL }
|
#define NULL_O_STRING { NULL }
|
||||||
|
|
||||||
|
#ifndef debug_printf_parse
|
||||||
|
static const char *const assignment_flag[] = {
|
||||||
|
"MAYBE_ASSIGNMENT",
|
||||||
|
"DEFINITELY_ASSIGNMENT",
|
||||||
|
"NOT_ASSIGNMENT",
|
||||||
|
"WORD_IS_KEYWORD",
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct in_str {
|
typedef struct in_str {
|
||||||
const char *p;
|
const char *p;
|
||||||
/* eof_flag=1: last char in ->p is really an EOF */
|
/* eof_flag=1: last char in ->p is really an EOF */
|
||||||
@ -3033,24 +3042,24 @@ static const struct reserved_combo* match_reserved_word(o_string *word)
|
|||||||
*/
|
*/
|
||||||
static const struct reserved_combo reserved_list[] = {
|
static const struct reserved_combo reserved_list[] = {
|
||||||
# if ENABLE_HUSH_IF
|
# if ENABLE_HUSH_IF
|
||||||
{ "!", RES_NONE, NOT_ASSIGNMENT , 0 },
|
{ "!", RES_NONE, NOT_ASSIGNMENT , 0 },
|
||||||
{ "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START },
|
{ "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START },
|
||||||
{ "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
|
{ "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
|
||||||
{ "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN },
|
{ "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN },
|
||||||
{ "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI },
|
{ "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI },
|
||||||
{ "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
|
{ "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
|
||||||
# endif
|
# endif
|
||||||
# if ENABLE_HUSH_LOOPS
|
# if ENABLE_HUSH_LOOPS
|
||||||
{ "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
|
{ "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
|
||||||
{ "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
|
{ "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
|
||||||
{ "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
|
{ "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
|
||||||
{ "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
|
{ "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
|
||||||
{ "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE },
|
{ "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE },
|
||||||
{ "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
|
{ "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
|
||||||
# endif
|
# endif
|
||||||
# if ENABLE_HUSH_CASE
|
# if ENABLE_HUSH_CASE
|
||||||
{ "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
|
{ "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
|
||||||
{ "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
|
{ "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
|
||||||
# endif
|
# endif
|
||||||
};
|
};
|
||||||
const struct reserved_combo *r;
|
const struct reserved_combo *r;
|
||||||
@ -3116,6 +3125,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
|
|||||||
ctx->ctx_res_w = r->res;
|
ctx->ctx_res_w = r->res;
|
||||||
ctx->old_flag = r->flag;
|
ctx->old_flag = r->flag;
|
||||||
word->o_assignment = r->assignment_flag;
|
word->o_assignment = r->assignment_flag;
|
||||||
|
debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]);
|
||||||
|
|
||||||
if (ctx->old_flag & FLAG_END) {
|
if (ctx->old_flag & FLAG_END) {
|
||||||
struct parse_context *old;
|
struct parse_context *old;
|
||||||
@ -3182,18 +3192,6 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
|
debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
|
||||||
ctx->pending_redirect = NULL;
|
ctx->pending_redirect = NULL;
|
||||||
} else {
|
} else {
|
||||||
/* If this word wasn't an assignment, next ones definitely
|
|
||||||
* can't be assignments. Even if they look like ones. */
|
|
||||||
if (word->o_assignment != DEFINITELY_ASSIGNMENT
|
|
||||||
&& word->o_assignment != WORD_IS_KEYWORD
|
|
||||||
) {
|
|
||||||
word->o_assignment = NOT_ASSIGNMENT;
|
|
||||||
} else {
|
|
||||||
if (word->o_assignment == DEFINITELY_ASSIGNMENT)
|
|
||||||
command->assignment_cnt++;
|
|
||||||
word->o_assignment = MAYBE_ASSIGNMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HAS_KEYWORDS
|
#if HAS_KEYWORDS
|
||||||
# if ENABLE_HUSH_CASE
|
# if ENABLE_HUSH_CASE
|
||||||
if (ctx->ctx_dsemicolon
|
if (ctx->ctx_dsemicolon
|
||||||
@ -3213,8 +3211,9 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
&& ctx->ctx_res_w != RES_CASE
|
&& ctx->ctx_res_w != RES_CASE
|
||||||
# endif
|
# endif
|
||||||
) {
|
) {
|
||||||
debug_printf_parse("checking '%s' for reserved-ness\n", word->data);
|
int reserved = reserved_word(word, ctx);
|
||||||
if (reserved_word(word, ctx)) {
|
debug_printf_parse("checking for reserved-ness: %d\n", reserved);
|
||||||
|
if (reserved) {
|
||||||
o_reset_to_empty_unquoted(word);
|
o_reset_to_empty_unquoted(word);
|
||||||
debug_printf_parse("done_word return %d\n",
|
debug_printf_parse("done_word return %d\n",
|
||||||
(ctx->ctx_res_w == RES_SNTX));
|
(ctx->ctx_res_w == RES_SNTX));
|
||||||
@ -3235,6 +3234,23 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
"groups and arglists don't mix\n");
|
"groups and arglists don't mix\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If this word wasn't an assignment, next ones definitely
|
||||||
|
* can't be assignments. Even if they look like ones. */
|
||||||
|
if (word->o_assignment != DEFINITELY_ASSIGNMENT
|
||||||
|
&& word->o_assignment != WORD_IS_KEYWORD
|
||||||
|
) {
|
||||||
|
word->o_assignment = NOT_ASSIGNMENT;
|
||||||
|
} else {
|
||||||
|
if (word->o_assignment == DEFINITELY_ASSIGNMENT) {
|
||||||
|
command->assignment_cnt++;
|
||||||
|
debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt);
|
||||||
|
}
|
||||||
|
debug_printf_parse("word->o_assignment was:'%s'\n", assignment_flag[word->o_assignment]);
|
||||||
|
word->o_assignment = MAYBE_ASSIGNMENT;
|
||||||
|
}
|
||||||
|
debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]);
|
||||||
|
|
||||||
if (word->has_quoted_part
|
if (word->has_quoted_part
|
||||||
/* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
|
/* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
|
||||||
&& (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
|
&& (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
|
||||||
@ -4259,6 +4275,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
&& is_well_formed_var_name(dest.data, '=')
|
&& is_well_formed_var_name(dest.data, '=')
|
||||||
) {
|
) {
|
||||||
dest.o_assignment = DEFINITELY_ASSIGNMENT;
|
dest.o_assignment = DEFINITELY_ASSIGNMENT;
|
||||||
|
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -4308,6 +4325,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
heredoc_cnt = 0;
|
heredoc_cnt = 0;
|
||||||
}
|
}
|
||||||
dest.o_assignment = MAYBE_ASSIGNMENT;
|
dest.o_assignment = MAYBE_ASSIGNMENT;
|
||||||
|
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
|
||||||
ch = ';';
|
ch = ';';
|
||||||
/* note: if (is_blank) continue;
|
/* note: if (is_blank) continue;
|
||||||
* will still trigger for us */
|
* will still trigger for us */
|
||||||
@ -4357,6 +4375,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
}
|
}
|
||||||
done_pipe(&ctx, PIPE_SEQ);
|
done_pipe(&ctx, PIPE_SEQ);
|
||||||
dest.o_assignment = MAYBE_ASSIGNMENT;
|
dest.o_assignment = MAYBE_ASSIGNMENT;
|
||||||
|
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
|
||||||
/* Do we sit outside of any if's, loops or case's? */
|
/* Do we sit outside of any if's, loops or case's? */
|
||||||
if (!HAS_KEYWORDS
|
if (!HAS_KEYWORDS
|
||||||
IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
|
IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
|
||||||
@ -4463,6 +4482,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
/* ch is a special char and thus this word
|
/* ch is a special char and thus this word
|
||||||
* cannot be an assignment */
|
* cannot be an assignment */
|
||||||
dest.o_assignment = NOT_ASSIGNMENT;
|
dest.o_assignment = NOT_ASSIGNMENT;
|
||||||
|
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note: nommu_addchr(&ctx.as_string, ch) is already done */
|
/* Note: nommu_addchr(&ctx.as_string, ch) is already done */
|
||||||
@ -4561,6 +4581,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
/* We just finished a cmd. New one may start
|
/* We just finished a cmd. New one may start
|
||||||
* with an assignment */
|
* with an assignment */
|
||||||
dest.o_assignment = MAYBE_ASSIGNMENT;
|
dest.o_assignment = MAYBE_ASSIGNMENT;
|
||||||
|
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
|
||||||
break;
|
break;
|
||||||
case '&':
|
case '&':
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&dest, &ctx)) {
|
||||||
|
1
shell/hush_test/hush-misc/assignment4.right
Normal file
1
shell/hush_test/hush-misc/assignment4.right
Normal file
@ -0,0 +1 @@
|
|||||||
|
Done:0
|
3
shell/hush_test/hush-misc/assignment4.tests
Executable file
3
shell/hush_test/hush-misc/assignment4.tests
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
# There was a bug where we misinterpreted assignments after 'do':
|
||||||
|
for i in 1; do eval b=; done
|
||||||
|
echo Done:$?
|
Loading…
x
Reference in New Issue
Block a user