hush: put "current word" structure into parsing context
function old new delta done_word 790 767 -23 parse_stream 3018 2919 -99 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-122) Total: -122 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
e93031e6dc
commit
09b7a7ec0e
242
shell/hush.c
242
shell/hush.c
@ -522,7 +522,6 @@ typedef struct o_string {
|
|||||||
* possibly empty one: word"", wo''rd etc. */
|
* possibly empty one: word"", wo''rd etc. */
|
||||||
smallint has_quoted_part;
|
smallint has_quoted_part;
|
||||||
smallint has_empty_slot;
|
smallint has_empty_slot;
|
||||||
smallint o_assignment; /* 0:maybe, 1:yes, 2:no */
|
|
||||||
} o_string;
|
} o_string;
|
||||||
enum {
|
enum {
|
||||||
EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */
|
EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */
|
||||||
@ -531,13 +530,6 @@ enum {
|
|||||||
* by prepending \ to *, ?, [, \ */
|
* by prepending \ to *, ?, [, \ */
|
||||||
EXP_FLAG_ESC_GLOB_CHARS = 0x1,
|
EXP_FLAG_ESC_GLOB_CHARS = 0x1,
|
||||||
};
|
};
|
||||||
enum {
|
|
||||||
MAYBE_ASSIGNMENT = 0,
|
|
||||||
DEFINITELY_ASSIGNMENT = 1,
|
|
||||||
NOT_ASSIGNMENT = 2,
|
|
||||||
/* Not an assignment, but next word may be: "if v=xyz cmd;" */
|
|
||||||
WORD_IS_KEYWORD = 3,
|
|
||||||
};
|
|
||||||
/* 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 }
|
||||||
|
|
||||||
@ -694,9 +686,11 @@ struct parse_context {
|
|||||||
struct command *command;
|
struct command *command;
|
||||||
/* last redirect in command->redirects list */
|
/* last redirect in command->redirects list */
|
||||||
struct redir_struct *pending_redirect;
|
struct redir_struct *pending_redirect;
|
||||||
|
o_string word;
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
o_string as_string;
|
o_string as_string;
|
||||||
#endif
|
#endif
|
||||||
|
smallint is_assignment; /* 0:maybe, 1:yes, 2:no, 3:keyword */
|
||||||
#if HAS_KEYWORDS
|
#if HAS_KEYWORDS
|
||||||
smallint ctx_res_w;
|
smallint ctx_res_w;
|
||||||
smallint ctx_inverted; /* "! cmd | cmd" */
|
smallint ctx_inverted; /* "! cmd | cmd" */
|
||||||
@ -717,6 +711,13 @@ struct parse_context {
|
|||||||
struct parse_context *stack;
|
struct parse_context *stack;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
enum {
|
||||||
|
MAYBE_ASSIGNMENT = 0,
|
||||||
|
DEFINITELY_ASSIGNMENT = 1,
|
||||||
|
NOT_ASSIGNMENT = 2,
|
||||||
|
/* Not an assignment, but next word may be: "if v=xyz cmd;" */
|
||||||
|
WORD_IS_KEYWORD = 3,
|
||||||
|
};
|
||||||
|
|
||||||
/* On program start, environ points to initial environment.
|
/* On program start, environ points to initial environment.
|
||||||
* putenv adds new pointers into it, unsetenv removes them.
|
* putenv adds new pointers into it, unsetenv removes them.
|
||||||
@ -3671,6 +3672,8 @@ static void done_pipe(struct parse_context *ctx, pipe_style type)
|
|||||||
static void initialize_context(struct parse_context *ctx)
|
static void initialize_context(struct parse_context *ctx)
|
||||||
{
|
{
|
||||||
memset(ctx, 0, sizeof(*ctx));
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
if (MAYBE_ASSIGNMENT != 0)
|
||||||
|
ctx->is_assignment = MAYBE_ASSIGNMENT;
|
||||||
ctx->pipe = ctx->list_head = new_pipe();
|
ctx->pipe = ctx->list_head = new_pipe();
|
||||||
/* Create the memory for command, roughly:
|
/* Create the memory for command, roughly:
|
||||||
* ctx->pipe->cmds = new struct command;
|
* ctx->pipe->cmds = new struct command;
|
||||||
@ -3752,7 +3755,7 @@ static const struct reserved_combo* match_reserved_word(o_string *word)
|
|||||||
}
|
}
|
||||||
/* Return NULL: not a keyword, else: keyword
|
/* Return NULL: not a keyword, else: keyword
|
||||||
*/
|
*/
|
||||||
static const struct reserved_combo* reserved_word(o_string *word, struct parse_context *ctx)
|
static const struct reserved_combo* reserved_word(struct parse_context *ctx)
|
||||||
{
|
{
|
||||||
# if ENABLE_HUSH_CASE
|
# if ENABLE_HUSH_CASE
|
||||||
static const struct reserved_combo reserved_match = {
|
static const struct reserved_combo reserved_match = {
|
||||||
@ -3761,9 +3764,9 @@ static const struct reserved_combo* reserved_word(o_string *word, struct parse_c
|
|||||||
# endif
|
# endif
|
||||||
const struct reserved_combo *r;
|
const struct reserved_combo *r;
|
||||||
|
|
||||||
if (word->has_quoted_part)
|
if (ctx->word.has_quoted_part)
|
||||||
return 0;
|
return 0;
|
||||||
r = match_reserved_word(word);
|
r = match_reserved_word(&ctx->word);
|
||||||
if (!r)
|
if (!r)
|
||||||
return r; /* NULL */
|
return r; /* NULL */
|
||||||
|
|
||||||
@ -3790,7 +3793,7 @@ static const struct reserved_combo* reserved_word(o_string *word, struct parse_c
|
|||||||
initialize_context(ctx);
|
initialize_context(ctx);
|
||||||
ctx->stack = old;
|
ctx->stack = old;
|
||||||
} else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
|
} else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
|
||||||
syntax_error_at(word->data);
|
syntax_error_at(ctx->word.data);
|
||||||
ctx->ctx_res_w = RES_SNTX;
|
ctx->ctx_res_w = RES_SNTX;
|
||||||
return r;
|
return r;
|
||||||
} else {
|
} else {
|
||||||
@ -3803,8 +3806,8 @@ static const struct reserved_combo* reserved_word(o_string *word, struct parse_c
|
|||||||
|
|
||||||
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;
|
ctx->is_assignment = r->assignment_flag;
|
||||||
debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]);
|
debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
|
||||||
|
|
||||||
if (ctx->old_flag & FLAG_END) {
|
if (ctx->old_flag & FLAG_END) {
|
||||||
struct parse_context *old;
|
struct parse_context *old;
|
||||||
@ -3850,12 +3853,12 @@ static const struct reserved_combo* reserved_word(o_string *word, struct parse_c
|
|||||||
* Normal return is 0. Syntax errors return 1.
|
* Normal return is 0. Syntax errors return 1.
|
||||||
* Note: on return, word is reset, but not o_free'd!
|
* Note: on return, word is reset, but not o_free'd!
|
||||||
*/
|
*/
|
||||||
static int done_word(o_string *word, struct parse_context *ctx)
|
static int done_word(struct parse_context *ctx)
|
||||||
{
|
{
|
||||||
struct command *command = ctx->command;
|
struct command *command = ctx->command;
|
||||||
|
|
||||||
debug_printf_parse("done_word entered: '%s' %p\n", word->data, command);
|
debug_printf_parse("done_word entered: '%s' %p\n", ctx->word.data, command);
|
||||||
if (word->length == 0 && !word->has_quoted_part) {
|
if (ctx->word.length == 0 && !ctx->word.has_quoted_part) {
|
||||||
debug_printf_parse("done_word return 0: true null, ignored\n");
|
debug_printf_parse("done_word return 0: true null, ignored\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -3885,7 +3888,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
// <<EOF$((1))
|
// <<EOF$((1))
|
||||||
// <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug]
|
// <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug]
|
||||||
|
|
||||||
ctx->pending_redirect->rd_filename = xstrdup(word->data);
|
ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data);
|
||||||
/* Cater for >\file case:
|
/* Cater for >\file case:
|
||||||
* >\a creates file a; >\\a, >"\a", >"\\a" create file \a
|
* >\a creates file a; >\\a, >"\a", >"\\a" create file \a
|
||||||
* Same with heredocs:
|
* Same with heredocs:
|
||||||
@ -3894,17 +3897,17 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
|
if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
|
||||||
unbackslash(ctx->pending_redirect->rd_filename);
|
unbackslash(ctx->pending_redirect->rd_filename);
|
||||||
/* Is it <<"HEREDOC"? */
|
/* Is it <<"HEREDOC"? */
|
||||||
if (word->has_quoted_part) {
|
if (ctx->word.has_quoted_part) {
|
||||||
ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
|
ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
|
debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data);
|
||||||
ctx->pending_redirect = NULL;
|
ctx->pending_redirect = NULL;
|
||||||
} else {
|
} else {
|
||||||
#if HAS_KEYWORDS
|
#if HAS_KEYWORDS
|
||||||
# if ENABLE_HUSH_CASE
|
# if ENABLE_HUSH_CASE
|
||||||
if (ctx->ctx_dsemicolon
|
if (ctx->ctx_dsemicolon
|
||||||
&& strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */
|
&& strcmp(ctx->word.data, "esac") != 0 /* not "... pattern) cmd;; esac" */
|
||||||
) {
|
) {
|
||||||
/* already done when ctx_dsemicolon was set to 1: */
|
/* already done when ctx_dsemicolon was set to 1: */
|
||||||
/* ctx->ctx_res_w = RES_MATCH; */
|
/* ctx->ctx_res_w = RES_MATCH; */
|
||||||
@ -3921,7 +3924,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
# endif
|
# endif
|
||||||
) {
|
) {
|
||||||
const struct reserved_combo *reserved;
|
const struct reserved_combo *reserved;
|
||||||
reserved = reserved_word(word, ctx);
|
reserved = reserved_word(ctx);
|
||||||
debug_printf_parse("checking for reserved-ness: %d\n", !!reserved);
|
debug_printf_parse("checking for reserved-ness: %d\n", !!reserved);
|
||||||
if (reserved) {
|
if (reserved) {
|
||||||
# if ENABLE_HUSH_LINENO_VAR
|
# if ENABLE_HUSH_LINENO_VAR
|
||||||
@ -3940,7 +3943,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
o_reset_to_empty_unquoted(word);
|
o_reset_to_empty_unquoted(&ctx->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));
|
||||||
return (ctx->ctx_res_w == RES_SNTX);
|
return (ctx->ctx_res_w == RES_SNTX);
|
||||||
@ -3948,7 +3951,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
# if defined(CMD_SINGLEWORD_NOGLOB)
|
# if defined(CMD_SINGLEWORD_NOGLOB)
|
||||||
if (0
|
if (0
|
||||||
# if BASH_TEST2
|
# if BASH_TEST2
|
||||||
|| strcmp(word->data, "[[") == 0
|
|| strcmp(ctx->word.data, "[[") == 0
|
||||||
# endif
|
# endif
|
||||||
/* In bash, local/export/readonly are special, args
|
/* In bash, local/export/readonly are special, args
|
||||||
* are assignments and therefore expansion of them
|
* are assignments and therefore expansion of them
|
||||||
@ -3965,9 +3968,9 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
* $ "export" i=`echo 'aaa bbb'`; echo "$i"
|
* $ "export" i=`echo 'aaa bbb'`; echo "$i"
|
||||||
* aaa
|
* aaa
|
||||||
*/
|
*/
|
||||||
IF_HUSH_LOCAL( || strcmp(word->data, "local") == 0)
|
IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0)
|
||||||
IF_HUSH_EXPORT( || strcmp(word->data, "export") == 0)
|
IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0)
|
||||||
IF_HUSH_READONLY( || strcmp(word->data, "readonly") == 0)
|
IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0)
|
||||||
) {
|
) {
|
||||||
command->cmd_type = CMD_SINGLEWORD_NOGLOB;
|
command->cmd_type = CMD_SINGLEWORD_NOGLOB;
|
||||||
}
|
}
|
||||||
@ -3978,7 +3981,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
|
|
||||||
if (command->group) {
|
if (command->group) {
|
||||||
/* "{ echo foo; } echo bar" - bad */
|
/* "{ echo foo; } echo bar" - bad */
|
||||||
syntax_error_at(word->data);
|
syntax_error_at(ctx->word.data);
|
||||||
debug_printf_parse("done_word return 1: syntax error, "
|
debug_printf_parse("done_word return 1: syntax error, "
|
||||||
"groups and arglists don't mix\n");
|
"groups and arglists don't mix\n");
|
||||||
return 1;
|
return 1;
|
||||||
@ -3986,26 +3989,26 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
|
|
||||||
/* If this word wasn't an assignment, next ones definitely
|
/* If this word wasn't an assignment, next ones definitely
|
||||||
* can't be assignments. Even if they look like ones. */
|
* can't be assignments. Even if they look like ones. */
|
||||||
if (word->o_assignment != DEFINITELY_ASSIGNMENT
|
if (ctx->is_assignment != DEFINITELY_ASSIGNMENT
|
||||||
&& word->o_assignment != WORD_IS_KEYWORD
|
&& ctx->is_assignment != WORD_IS_KEYWORD
|
||||||
) {
|
) {
|
||||||
word->o_assignment = NOT_ASSIGNMENT;
|
ctx->is_assignment = NOT_ASSIGNMENT;
|
||||||
} else {
|
} else {
|
||||||
if (word->o_assignment == DEFINITELY_ASSIGNMENT) {
|
if (ctx->is_assignment == DEFINITELY_ASSIGNMENT) {
|
||||||
command->assignment_cnt++;
|
command->assignment_cnt++;
|
||||||
debug_printf_parse("++assignment_cnt=%d\n", 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]);
|
debug_printf_parse("ctx->is_assignment was:'%s'\n", assignment_flag[ctx->is_assignment]);
|
||||||
word->o_assignment = MAYBE_ASSIGNMENT;
|
ctx->is_assignment = MAYBE_ASSIGNMENT;
|
||||||
}
|
}
|
||||||
debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]);
|
debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
|
||||||
command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
|
command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data));
|
||||||
debug_print_strings("word appended to argv", command->argv);
|
debug_print_strings("word appended to argv", command->argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
if (ctx->ctx_res_w == RES_FOR) {
|
if (ctx->ctx_res_w == RES_FOR) {
|
||||||
if (word->has_quoted_part
|
if (ctx->word.has_quoted_part
|
||||||
|| !is_well_formed_var_name(command->argv[0], '\0')
|
|| !is_well_formed_var_name(command->argv[0], '\0')
|
||||||
) {
|
) {
|
||||||
/* bash says just "not a valid identifier" */
|
/* bash says just "not a valid identifier" */
|
||||||
@ -4026,7 +4029,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
o_reset_to_empty_unquoted(word);
|
o_reset_to_empty_unquoted(&ctx->word);
|
||||||
|
|
||||||
debug_printf_parse("done_word return 0\n");
|
debug_printf_parse("done_word return 0\n");
|
||||||
return 0;
|
return 0;
|
||||||
@ -4310,14 +4313,10 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
int end_trigger);
|
int end_trigger);
|
||||||
|
|
||||||
|
|
||||||
#if !ENABLE_HUSH_FUNCTIONS
|
static int parse_group(struct parse_context *ctx,
|
||||||
#define parse_group(dest, ctx, input, ch) \
|
|
||||||
parse_group(ctx, input, ch)
|
|
||||||
#endif
|
|
||||||
static int parse_group(o_string *dest, struct parse_context *ctx,
|
|
||||||
struct in_str *input, int ch)
|
struct in_str *input, int ch)
|
||||||
{
|
{
|
||||||
/* dest contains characters seen prior to ( or {.
|
/* ctx->word contains characters seen prior to ( or {.
|
||||||
* Typically it's empty, but for function defs,
|
* Typically it's empty, but for function defs,
|
||||||
* it contains function name (without '()'). */
|
* it contains function name (without '()'). */
|
||||||
#if BB_MMU
|
#if BB_MMU
|
||||||
@ -4331,9 +4330,9 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
|
|||||||
|
|
||||||
debug_printf_parse("parse_group entered\n");
|
debug_printf_parse("parse_group entered\n");
|
||||||
#if ENABLE_HUSH_FUNCTIONS
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
if (ch == '(' && !dest->has_quoted_part) {
|
if (ch == '(' && !ctx->word.has_quoted_part) {
|
||||||
if (dest->length)
|
if (ctx->word.length)
|
||||||
if (done_word(dest, ctx))
|
if (done_word(ctx))
|
||||||
return 1;
|
return 1;
|
||||||
if (!command->argv)
|
if (!command->argv)
|
||||||
goto skip; /* (... */
|
goto skip; /* (... */
|
||||||
@ -4365,8 +4364,8 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
|
|||||||
|
|
||||||
#if 0 /* Prevented by caller */
|
#if 0 /* Prevented by caller */
|
||||||
if (command->argv /* word [word]{... */
|
if (command->argv /* word [word]{... */
|
||||||
|| dest->length /* word{... */
|
|| ctx->word.length /* word{... */
|
||||||
|| dest->has_quoted_part /* ""{... */
|
|| ctx->word.has_quoted_part /* ""{... */
|
||||||
) {
|
) {
|
||||||
syntax_error(NULL);
|
syntax_error(NULL);
|
||||||
debug_printf_parse("parse_group return 1: "
|
debug_printf_parse("parse_group return 1: "
|
||||||
@ -4972,29 +4971,28 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
int end_trigger)
|
int end_trigger)
|
||||||
{
|
{
|
||||||
struct parse_context ctx;
|
struct parse_context ctx;
|
||||||
o_string dest = NULL_O_STRING;
|
|
||||||
int heredoc_cnt;
|
int heredoc_cnt;
|
||||||
|
|
||||||
/* Single-quote triggers a bypass of the main loop until its mate is
|
/* Single-quote triggers a bypass of the main loop until its mate is
|
||||||
* found. When recursing, quote state is passed in via dest->o_expflags.
|
* found. When recursing, quote state is passed in via ctx.word.o_expflags.
|
||||||
*/
|
*/
|
||||||
debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
|
debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
|
||||||
end_trigger ? end_trigger : 'X');
|
end_trigger ? end_trigger : 'X');
|
||||||
debug_enter();
|
debug_enter();
|
||||||
|
|
||||||
/* If very first arg is "" or '', dest.data may end up NULL.
|
initialize_context(&ctx);
|
||||||
* Preventing this: */
|
|
||||||
o_addchr(&dest, '\0');
|
/* If very first arg is "" or '', ctx.word.data may end up NULL.
|
||||||
dest.length = 0;
|
* Preventing this:
|
||||||
|
*/
|
||||||
|
o_addchr(&ctx.word, '\0');
|
||||||
|
ctx.word.length = 0;
|
||||||
|
|
||||||
/* We used to separate words on $IFS here. This was wrong.
|
/* We used to separate words on $IFS here. This was wrong.
|
||||||
* $IFS is used only for word splitting when $var is expanded,
|
* $IFS is used only for word splitting when $var is expanded,
|
||||||
* here we should use blank chars as separators, not $IFS
|
* here we should use blank chars as separators, not $IFS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (MAYBE_ASSIGNMENT != 0)
|
|
||||||
dest.o_assignment = MAYBE_ASSIGNMENT;
|
|
||||||
initialize_context(&ctx);
|
|
||||||
heredoc_cnt = 0;
|
heredoc_cnt = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
const char *is_blank;
|
const char *is_blank;
|
||||||
@ -5006,7 +5004,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
|
|
||||||
ch = i_getch(input);
|
ch = i_getch(input);
|
||||||
debug_printf_parse(": ch=%c (%d) escape=%d\n",
|
debug_printf_parse(": ch=%c (%d) escape=%d\n",
|
||||||
ch, ch, !!(dest.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
|
ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
struct pipe *pi;
|
struct pipe *pi;
|
||||||
|
|
||||||
@ -5023,10 +5021,10 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
o_free(&dest);
|
o_free(&ctx.word);
|
||||||
done_pipe(&ctx, PIPE_SEQ);
|
done_pipe(&ctx, PIPE_SEQ);
|
||||||
pi = ctx.list_head;
|
pi = ctx.list_head;
|
||||||
/* If we got nothing... */
|
/* If we got nothing... */
|
||||||
@ -5066,8 +5064,8 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
SPECIAL_VAR_SYMBOL_STR;
|
SPECIAL_VAR_SYMBOL_STR;
|
||||||
/* Are { and } special here? */
|
/* Are { and } special here? */
|
||||||
if (ctx.command->argv /* word [word]{... - non-special */
|
if (ctx.command->argv /* word [word]{... - non-special */
|
||||||
|| dest.length /* word{... - non-special */
|
|| ctx.word.length /* word{... - non-special */
|
||||||
|| dest.has_quoted_part /* ""{... - non-special */
|
|| ctx.word.has_quoted_part /* ""{... - non-special */
|
||||||
|| (next != ';' /* }; - special */
|
|| (next != ';' /* }; - special */
|
||||||
&& next != ')' /* }) - special */
|
&& next != ')' /* }) - special */
|
||||||
&& next != '(' /* {( - special */
|
&& next != '(' /* {( - special */
|
||||||
@ -5084,14 +5082,14 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
|
|
||||||
if (!is_special && !is_blank) { /* ordinary char */
|
if (!is_special && !is_blank) { /* ordinary char */
|
||||||
ordinary_char:
|
ordinary_char:
|
||||||
o_addQchr(&dest, ch);
|
o_addQchr(&ctx.word, ch);
|
||||||
if ((dest.o_assignment == MAYBE_ASSIGNMENT
|
if ((ctx.is_assignment == MAYBE_ASSIGNMENT
|
||||||
|| dest.o_assignment == WORD_IS_KEYWORD)
|
|| ctx.is_assignment == WORD_IS_KEYWORD)
|
||||||
&& ch == '='
|
&& ch == '='
|
||||||
&& is_well_formed_var_name(dest.data, '=')
|
&& is_well_formed_var_name(ctx.word.data, '=')
|
||||||
) {
|
) {
|
||||||
dest.o_assignment = DEFINITELY_ASSIGNMENT;
|
ctx.is_assignment = DEFINITELY_ASSIGNMENT;
|
||||||
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
|
debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -5113,7 +5111,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
}
|
}
|
||||||
/* ch == last eaten whitespace char */
|
/* ch == last eaten whitespace char */
|
||||||
#endif
|
#endif
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
if (ch == '\n') {
|
if (ch == '\n') {
|
||||||
@ -5123,7 +5121,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
* "case ... in <newline> word) ..."
|
* "case ... in <newline> word) ..."
|
||||||
*/
|
*/
|
||||||
if (IS_NULL_CMD(ctx.command)
|
if (IS_NULL_CMD(ctx.command)
|
||||||
&& dest.length == 0 && !dest.has_quoted_part
|
&& ctx.word.length == 0 && !ctx.word.has_quoted_part
|
||||||
) {
|
) {
|
||||||
/* This newline can be ignored. But...
|
/* This newline can be ignored. But...
|
||||||
* Without check #1, interactive shell
|
* Without check #1, interactive shell
|
||||||
@ -5158,8 +5156,8 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
}
|
}
|
||||||
heredoc_cnt = 0;
|
heredoc_cnt = 0;
|
||||||
}
|
}
|
||||||
dest.o_assignment = MAYBE_ASSIGNMENT;
|
ctx.is_assignment = MAYBE_ASSIGNMENT;
|
||||||
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
|
debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
|
||||||
ch = ';';
|
ch = ';';
|
||||||
/* note: if (is_blank) continue;
|
/* note: if (is_blank) continue;
|
||||||
* will still trigger for us */
|
* will still trigger for us */
|
||||||
@ -5171,8 +5169,8 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
* Pathological example: { ""}; } should exec "}" cmd
|
* Pathological example: { ""}; } should exec "}" cmd
|
||||||
*/
|
*/
|
||||||
if (ch == '}') {
|
if (ch == '}') {
|
||||||
if (dest.length != 0 /* word} */
|
if (ctx.word.length != 0 /* word} */
|
||||||
|| dest.has_quoted_part /* ""} */
|
|| ctx.word.has_quoted_part /* ""} */
|
||||||
) {
|
) {
|
||||||
goto ordinary_char;
|
goto ordinary_char;
|
||||||
}
|
}
|
||||||
@ -5201,7 +5199,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
&& (ch != ')'
|
&& (ch != ')'
|
||||||
|| ctx.ctx_res_w != RES_MATCH
|
|| ctx.ctx_res_w != RES_MATCH
|
||||||
|| (!dest.has_quoted_part && strcmp(dest.data, "esac") == 0)
|
|| (!ctx.word.has_quoted_part && strcmp(ctx.word.data, "esac") == 0)
|
||||||
)
|
)
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
@ -5218,17 +5216,17 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
syntax_error_unterm_str("here document");
|
syntax_error_unterm_str("here document");
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
done_pipe(&ctx, PIPE_SEQ);
|
done_pipe(&ctx, PIPE_SEQ);
|
||||||
dest.o_assignment = MAYBE_ASSIGNMENT;
|
ctx.is_assignment = MAYBE_ASSIGNMENT;
|
||||||
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
|
debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_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))
|
||||||
) {
|
) {
|
||||||
o_free(&dest);
|
o_free(&ctx.word);
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data);
|
debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data);
|
||||||
if (pstring)
|
if (pstring)
|
||||||
@ -5257,8 +5255,8 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
* an assignment. a=1 2>z b=2: b=2 is still assignment */
|
* an assignment. a=1 2>z b=2: b=2 is still assignment */
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case '>':
|
case '>':
|
||||||
redir_fd = redirect_opt_num(&dest);
|
redir_fd = redirect_opt_num(&ctx.word);
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
redir_style = REDIRECT_OVERWRITE;
|
redir_style = REDIRECT_OVERWRITE;
|
||||||
@ -5279,8 +5277,8 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
goto parse_error;
|
goto parse_error;
|
||||||
continue; /* back to top of while (1) */
|
continue; /* back to top of while (1) */
|
||||||
case '<':
|
case '<':
|
||||||
redir_fd = redirect_opt_num(&dest);
|
redir_fd = redirect_opt_num(&ctx.word);
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
redir_style = REDIRECT_INPUT;
|
redir_style = REDIRECT_INPUT;
|
||||||
@ -5307,7 +5305,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
goto parse_error;
|
goto parse_error;
|
||||||
continue; /* back to top of while (1) */
|
continue; /* back to top of while (1) */
|
||||||
case '#':
|
case '#':
|
||||||
if (dest.length == 0 && !dest.has_quoted_part) {
|
if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
|
||||||
/* skip "#comment" */
|
/* skip "#comment" */
|
||||||
/* note: we do not add it to &ctx.as_string */
|
/* note: we do not add it to &ctx.as_string */
|
||||||
/* TODO: in bash:
|
/* TODO: in bash:
|
||||||
@ -5342,14 +5340,14 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dest.o_assignment == MAYBE_ASSIGNMENT
|
if (ctx.is_assignment == MAYBE_ASSIGNMENT
|
||||||
/* check that we are not in word in "a=1 2>word b=1": */
|
/* check that we are not in word in "a=1 2>word b=1": */
|
||||||
&& !ctx.pending_redirect
|
&& !ctx.pending_redirect
|
||||||
) {
|
) {
|
||||||
/* 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;
|
ctx.is_assignment = NOT_ASSIGNMENT;
|
||||||
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
|
debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note: nommu_addchr(&ctx.as_string, ch) is already done */
|
/* Note: nommu_addchr(&ctx.as_string, ch) is already done */
|
||||||
@ -5357,12 +5355,12 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
switch (ch) {
|
switch (ch) {
|
||||||
case SPECIAL_VAR_SYMBOL:
|
case SPECIAL_VAR_SYMBOL:
|
||||||
/* Convert raw ^C to corresponding special variable reference */
|
/* Convert raw ^C to corresponding special variable reference */
|
||||||
o_addchr(&dest, SPECIAL_VAR_SYMBOL);
|
o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
|
||||||
o_addchr(&dest, SPECIAL_VAR_QUOTED_SVS);
|
o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case '#':
|
case '#':
|
||||||
/* non-comment #: "echo a#b" etc */
|
/* non-comment #: "echo a#b" etc */
|
||||||
o_addchr(&dest, ch);
|
o_addchr(&ctx.word, ch);
|
||||||
break;
|
break;
|
||||||
case '\\':
|
case '\\':
|
||||||
if (next == EOF) {
|
if (next == EOF) {
|
||||||
@ -5371,29 +5369,29 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
}
|
}
|
||||||
ch = i_getch(input);
|
ch = i_getch(input);
|
||||||
/* note: ch != '\n' (that case does not reach this place) */
|
/* note: ch != '\n' (that case does not reach this place) */
|
||||||
o_addchr(&dest, '\\');
|
o_addchr(&ctx.word, '\\');
|
||||||
/*nommu_addchr(&ctx.as_string, '\\'); - already done */
|
/*nommu_addchr(&ctx.as_string, '\\'); - already done */
|
||||||
o_addchr(&dest, ch);
|
o_addchr(&ctx.word, ch);
|
||||||
nommu_addchr(&ctx.as_string, ch);
|
nommu_addchr(&ctx.as_string, ch);
|
||||||
/* Example: echo Hello \2>file
|
/* Example: echo Hello \2>file
|
||||||
* we need to know that word 2 is quoted */
|
* we need to know that word 2 is quoted */
|
||||||
dest.has_quoted_part = 1;
|
ctx.word.has_quoted_part = 1;
|
||||||
break;
|
break;
|
||||||
case '$':
|
case '$':
|
||||||
if (!parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0)) {
|
if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) {
|
||||||
debug_printf_parse("parse_stream parse error: "
|
debug_printf_parse("parse_stream parse error: "
|
||||||
"parse_dollar returned 0 (error)\n");
|
"parse_dollar returned 0 (error)\n");
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '\'':
|
case '\'':
|
||||||
dest.has_quoted_part = 1;
|
ctx.word.has_quoted_part = 1;
|
||||||
if (next == '\'' && !ctx.pending_redirect) {
|
if (next == '\'' && !ctx.pending_redirect) {
|
||||||
insert_empty_quoted_str_marker:
|
insert_empty_quoted_str_marker:
|
||||||
nommu_addchr(&ctx.as_string, next);
|
nommu_addchr(&ctx.as_string, next);
|
||||||
i_getch(input); /* eat second ' */
|
i_getch(input); /* eat second ' */
|
||||||
o_addchr(&dest, SPECIAL_VAR_SYMBOL);
|
o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
|
||||||
o_addchr(&dest, SPECIAL_VAR_SYMBOL);
|
o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
|
||||||
} else {
|
} else {
|
||||||
while (1) {
|
while (1) {
|
||||||
ch = i_getch(input);
|
ch = i_getch(input);
|
||||||
@ -5406,38 +5404,38 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
break;
|
break;
|
||||||
if (ch == SPECIAL_VAR_SYMBOL) {
|
if (ch == SPECIAL_VAR_SYMBOL) {
|
||||||
/* Convert raw ^C to corresponding special variable reference */
|
/* Convert raw ^C to corresponding special variable reference */
|
||||||
o_addchr(&dest, SPECIAL_VAR_SYMBOL);
|
o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
|
||||||
o_addchr(&dest, SPECIAL_VAR_QUOTED_SVS);
|
o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
|
||||||
}
|
}
|
||||||
o_addqchr(&dest, ch);
|
o_addqchr(&ctx.word, ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '"':
|
case '"':
|
||||||
dest.has_quoted_part = 1;
|
ctx.word.has_quoted_part = 1;
|
||||||
if (next == '"' && !ctx.pending_redirect)
|
if (next == '"' && !ctx.pending_redirect)
|
||||||
goto insert_empty_quoted_str_marker;
|
goto insert_empty_quoted_str_marker;
|
||||||
if (dest.o_assignment == NOT_ASSIGNMENT)
|
if (ctx.is_assignment == NOT_ASSIGNMENT)
|
||||||
dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
|
ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
|
||||||
if (!encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1))
|
if (!encode_string(&ctx.as_string, &ctx.word, input, '"', /*process_bkslash:*/ 1))
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
dest.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
|
ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
|
||||||
break;
|
break;
|
||||||
#if ENABLE_HUSH_TICK
|
#if ENABLE_HUSH_TICK
|
||||||
case '`': {
|
case '`': {
|
||||||
USE_FOR_NOMMU(unsigned pos;)
|
USE_FOR_NOMMU(unsigned pos;)
|
||||||
|
|
||||||
o_addchr(&dest, SPECIAL_VAR_SYMBOL);
|
o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
|
||||||
o_addchr(&dest, '`');
|
o_addchr(&ctx.word, '`');
|
||||||
USE_FOR_NOMMU(pos = dest.length;)
|
USE_FOR_NOMMU(pos = ctx.word.length;)
|
||||||
if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0))
|
if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0))
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
# if !BB_MMU
|
# if !BB_MMU
|
||||||
o_addstr(&ctx.as_string, dest.data + pos);
|
o_addstr(&ctx.as_string, ctx.word.data + pos);
|
||||||
o_addchr(&ctx.as_string, '`');
|
o_addchr(&ctx.as_string, '`');
|
||||||
# endif
|
# endif
|
||||||
o_addchr(&dest, SPECIAL_VAR_SYMBOL);
|
o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
|
||||||
//debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos);
|
//debug_printf_subst("SUBST RES3 '%s'\n", ctx.word.data + pos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -5445,7 +5443,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
case_semi:
|
case_semi:
|
||||||
#endif
|
#endif
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
done_pipe(&ctx, PIPE_SEQ);
|
done_pipe(&ctx, PIPE_SEQ);
|
||||||
@ -5468,11 +5466,11 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
new_cmd:
|
new_cmd:
|
||||||
/* 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;
|
ctx.is_assignment = MAYBE_ASSIGNMENT;
|
||||||
debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
|
debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
|
||||||
break;
|
break;
|
||||||
case '&':
|
case '&':
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
if (next == '\\')
|
if (next == '\\')
|
||||||
@ -5486,7 +5484,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
}
|
}
|
||||||
goto new_cmd;
|
goto new_cmd;
|
||||||
case '|':
|
case '|':
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
@ -5511,14 +5509,14 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
/* "case... in [(]word)..." - skip '(' */
|
/* "case... in [(]word)..." - skip '(' */
|
||||||
if (ctx.ctx_res_w == RES_MATCH
|
if (ctx.ctx_res_w == RES_MATCH
|
||||||
&& ctx.command->argv == NULL /* not (word|(... */
|
&& ctx.command->argv == NULL /* not (word|(... */
|
||||||
&& dest.length == 0 /* not word(... */
|
&& ctx.word.length == 0 /* not word(... */
|
||||||
&& dest.has_quoted_part == 0 /* not ""(... */
|
&& ctx.word.has_quoted_part == 0 /* not ""(... */
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
case '{':
|
case '{':
|
||||||
if (parse_group(&dest, &ctx, input, ch) != 0) {
|
if (parse_group(&ctx, input, ch) != 0) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
goto new_cmd;
|
goto new_cmd;
|
||||||
@ -5575,7 +5573,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
IF_HAS_KEYWORDS(pctx = p2;)
|
IF_HAS_KEYWORDS(pctx = p2;)
|
||||||
} while (HAS_KEYWORDS && pctx);
|
} while (HAS_KEYWORDS && pctx);
|
||||||
|
|
||||||
o_free(&dest);
|
o_free(&ctx.word);
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
if (pstring)
|
if (pstring)
|
||||||
*pstring = NULL;
|
*pstring = NULL;
|
||||||
|
Loading…
Reference in New Issue
Block a user