hush: fix mishandling of a'b'c=fff as assignments. They are not.
function old new delta parse_stream 1920 2004 +84 done_word 715 752 +37 parse_and_run_stream 328 333 +5 builtin_exec 25 29 +4 pseudo_exec_argv 138 139 +1 run_list 2006 1999 -7 is_assignment 215 134 -81 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 5/2 up/down: 131/-88) Total: 43 bytes
This commit is contained in:
parent
c8bec9a085
commit
2b576b8e76
132
shell/hush.c
132
shell/hush.c
@ -318,10 +318,12 @@ struct redir_struct {
|
|||||||
|
|
||||||
struct child_prog {
|
struct child_prog {
|
||||||
pid_t pid; /* 0 if exited */
|
pid_t pid; /* 0 if exited */
|
||||||
|
int assignment_cnt; /* how many argv[i] are assignments? */
|
||||||
smallint is_stopped; /* is the program currently running? */
|
smallint is_stopped; /* is the program currently running? */
|
||||||
smallint subshell; /* flag, non-zero if group must be forked */
|
smallint subshell; /* flag, non-zero if group must be forked */
|
||||||
|
struct pipe *group; /* if non-NULL, this "prog" is {} group,
|
||||||
|
* subshell, or a compound statement */
|
||||||
char **argv; /* program name and arguments */
|
char **argv; /* program name and arguments */
|
||||||
struct pipe *group; /* if non-NULL, first in group or subshell */
|
|
||||||
struct redir_struct *redirects; /* I/O redirections */
|
struct redir_struct *redirects; /* I/O redirections */
|
||||||
};
|
};
|
||||||
/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
|
/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
|
||||||
@ -377,6 +379,7 @@ enum {
|
|||||||
MAYBE_ASSIGNMENT = 0,
|
MAYBE_ASSIGNMENT = 0,
|
||||||
DEFINITELY_ASSIGNMENT = 1,
|
DEFINITELY_ASSIGNMENT = 1,
|
||||||
NOT_ASSIGNMENT = 2,
|
NOT_ASSIGNMENT = 2,
|
||||||
|
WORD_IS_KEYWORD = 3, /* not assigment, but next word may be: "if v=xyz cmd;" */
|
||||||
};
|
};
|
||||||
/* 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 }
|
||||||
@ -430,7 +433,6 @@ struct globals {
|
|||||||
pid_t last_bg_pid;
|
pid_t last_bg_pid;
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
int run_list_level;
|
int run_list_level;
|
||||||
// pid_t saved_task_pgrp;
|
|
||||||
pid_t saved_tty_pgrp;
|
pid_t saved_tty_pgrp;
|
||||||
int last_jobid;
|
int last_jobid;
|
||||||
struct pipe *job_list;
|
struct pipe *job_list;
|
||||||
@ -524,10 +526,12 @@ static int free_pipe(struct pipe *pi, int indent);
|
|||||||
static int setup_redirects(struct child_prog *prog, int squirrel[]);
|
static int setup_redirects(struct child_prog *prog, int squirrel[]);
|
||||||
static int run_list(struct pipe *pi);
|
static int run_list(struct pipe *pi);
|
||||||
#if BB_MMU
|
#if BB_MMU
|
||||||
#define pseudo_exec_argv(ptrs2free, argv, argv_expanded) pseudo_exec_argv(argv, argv_expanded)
|
#define pseudo_exec_argv(ptrs2free, argv, assignment_cnt, argv_expanded) \
|
||||||
#define pseudo_exec(ptrs2free, child, argv_expanded) pseudo_exec(child, argv_expanded)
|
pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
|
||||||
|
#define pseudo_exec(ptrs2free, child, argv_expanded) \
|
||||||
|
pseudo_exec(child, argv_expanded)
|
||||||
#endif
|
#endif
|
||||||
static void pseudo_exec_argv(char **ptrs2free, char **argv, char **argv_expanded) NORETURN;
|
static void pseudo_exec_argv(char **ptrs2free, char **argv, int assignment_cnt, char **argv_expanded) NORETURN;
|
||||||
static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded) NORETURN;
|
static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded) NORETURN;
|
||||||
static int run_pipe(struct pipe *pi);
|
static int run_pipe(struct pipe *pi);
|
||||||
/* data structure manipulation: */
|
/* data structure manipulation: */
|
||||||
@ -1390,13 +1394,13 @@ static void restore_redirects(int squirrel[])
|
|||||||
* XXX no exit() here. If you don't exec, use _exit instead.
|
* XXX no exit() here. If you don't exec, use _exit instead.
|
||||||
* The at_exit handlers apparently confuse the calling process,
|
* The at_exit handlers apparently confuse the calling process,
|
||||||
* in particular stdin handling. Not sure why? -- because of vfork! (vda) */
|
* in particular stdin handling. Not sure why? -- because of vfork! (vda) */
|
||||||
static void pseudo_exec_argv(char **ptrs2free, char **argv, char **argv_expanded)
|
static void pseudo_exec_argv(char **ptrs2free, char **argv, int assignment_cnt, char **argv_expanded)
|
||||||
{
|
{
|
||||||
int i, rcode;
|
int i, rcode;
|
||||||
char *p;
|
char *p;
|
||||||
const struct built_in_command *x;
|
const struct built_in_command *x;
|
||||||
|
|
||||||
for (i = 0; is_assignment(argv[i]); i++) {
|
for (i = 0; i < assignment_cnt; i++) {
|
||||||
debug_printf_exec("pid %d environment modification: %s\n",
|
debug_printf_exec("pid %d environment modification: %s\n",
|
||||||
getpid(), argv[i]);
|
getpid(), argv[i]);
|
||||||
p = expand_string_to_string(argv[i]);
|
p = expand_string_to_string(argv[i]);
|
||||||
@ -1466,7 +1470,7 @@ static void pseudo_exec_argv(char **ptrs2free, char **argv, char **argv_expanded
|
|||||||
static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded)
|
static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded)
|
||||||
{
|
{
|
||||||
if (child->argv)
|
if (child->argv)
|
||||||
pseudo_exec_argv(ptrs2free, child->argv, argv_expanded);
|
pseudo_exec_argv(ptrs2free, child->argv, child->assignment_cnt, argv_expanded);
|
||||||
|
|
||||||
if (child->group) {
|
if (child->group) {
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
@ -1713,8 +1717,6 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
|
|||||||
p = getpgid(0); /* pgid of our process */
|
p = getpgid(0); /* pgid of our process */
|
||||||
debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p);
|
debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p);
|
||||||
tcsetpgrp(G.interactive_fd, p);
|
tcsetpgrp(G.interactive_fd, p);
|
||||||
// if (tcsetpgrp(G.interactive_fd, p) && errno != ENOTTY)
|
|
||||||
// bb_perror_msg("tcsetpgrp-4a");
|
|
||||||
return rcode;
|
return rcode;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1780,8 +1782,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
argv = child->argv;
|
argv = child->argv;
|
||||||
|
|
||||||
if (single_and_fg && argv != NULL) {
|
if (single_and_fg && argv != NULL) {
|
||||||
for (i = 0; is_assignment(argv[i]); i++)
|
i = child->assignment_cnt;
|
||||||
continue;
|
|
||||||
if (i != 0 && argv[i] == NULL) {
|
if (i != 0 && argv[i] == NULL) {
|
||||||
/* assignments, but no command: set local environment */
|
/* assignments, but no command: set local environment */
|
||||||
for (i = 0; argv[i] != NULL; i++) {
|
for (i = 0; argv[i] != NULL; i++) {
|
||||||
@ -1793,7 +1794,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Expand assignments into one string each */
|
/* Expand assignments into one string each */
|
||||||
for (i = 0; is_assignment(argv[i]); i++) {
|
for (i = 0; i < child->assignment_cnt; i++) {
|
||||||
p = expand_string_to_string(argv[i]);
|
p = expand_string_to_string(argv[i]);
|
||||||
putenv(p);
|
putenv(p);
|
||||||
//FIXME: do we leak p?!
|
//FIXME: do we leak p?!
|
||||||
@ -1991,7 +1992,7 @@ static void debug_print_tree(struct pipe *pi, int lvl)
|
|||||||
struct child_prog *child = &pi->progs[prn];
|
struct child_prog *child = &pi->progs[prn];
|
||||||
char **argv = child->argv;
|
char **argv = child->argv;
|
||||||
|
|
||||||
fprintf(stderr, "%*s prog %d", lvl*2, "", prn);
|
fprintf(stderr, "%*s prog %d assignment_cnt:%d", lvl*2, "", prn, child->assignment_cnt);
|
||||||
if (child->group) {
|
if (child->group) {
|
||||||
fprintf(stderr, " group %s: (argv=%p)\n",
|
fprintf(stderr, " group %s: (argv=%p)\n",
|
||||||
(child->subshell ? "()" : "{}"),
|
(child->subshell ? "()" : "{}"),
|
||||||
@ -2165,30 +2166,31 @@ static int run_list(struct pipe *pi)
|
|||||||
vals = (char**)encoded_dollar_at_argv;
|
vals = (char**)encoded_dollar_at_argv;
|
||||||
if (pi->next->res_word == RES_IN) {
|
if (pi->next->res_word == RES_IN) {
|
||||||
/* if no variable values after "in" we skip "for" */
|
/* if no variable values after "in" we skip "for" */
|
||||||
if (!pi->next->progs->argv)
|
if (!pi->next->progs[0].argv)
|
||||||
break;
|
break;
|
||||||
vals = pi->next->progs->argv;
|
vals = pi->next->progs[0].argv;
|
||||||
} /* else: "for var; do..." -> assume "$@" list */
|
} /* else: "for var; do..." -> assume "$@" list */
|
||||||
/* create list of variable values */
|
/* create list of variable values */
|
||||||
debug_print_strings("for_list made from", vals);
|
debug_print_strings("for_list made from", vals);
|
||||||
for_list = expand_strvec_to_strvec(vals);
|
for_list = expand_strvec_to_strvec(vals);
|
||||||
for_lcur = for_list;
|
for_lcur = for_list;
|
||||||
debug_print_strings("for_list", for_list);
|
debug_print_strings("for_list", for_list);
|
||||||
for_varname = pi->progs->argv[0];
|
for_varname = pi->progs[0].argv[0];
|
||||||
pi->progs->argv[0] = NULL;
|
pi->progs[0].argv[0] = NULL;
|
||||||
}
|
}
|
||||||
free(pi->progs->argv[0]);
|
free(pi->progs[0].argv[0]);
|
||||||
if (!*for_lcur) {
|
if (!*for_lcur) {
|
||||||
/* "for" loop is over, clean up */
|
/* "for" loop is over, clean up */
|
||||||
free(for_list);
|
free(for_list);
|
||||||
for_list = NULL;
|
for_list = NULL;
|
||||||
for_lcur = NULL;
|
for_lcur = NULL;
|
||||||
pi->progs->argv[0] = for_varname;
|
pi->progs[0].argv[0] = for_varname;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* insert next value from for_lcur */
|
/* insert next value from for_lcur */
|
||||||
//TODO: does it need escaping?
|
//TODO: does it need escaping?
|
||||||
pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
|
pi->progs[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
|
||||||
|
pi->progs[0].assignment_cnt = 1;
|
||||||
}
|
}
|
||||||
if (rword == RES_IN) /* "for v IN list;..." - "in" has no cmds anyway */
|
if (rword == RES_IN) /* "for v IN list;..." - "in" has no cmds anyway */
|
||||||
continue;
|
continue;
|
||||||
@ -2902,11 +2904,12 @@ static void initialize_context(struct p_context *ctx)
|
|||||||
* case, function, and select are obnoxious, save those for later.
|
* case, function, and select are obnoxious, save those for later.
|
||||||
*/
|
*/
|
||||||
#if HAS_KEYWORDS
|
#if HAS_KEYWORDS
|
||||||
static int reserved_word(const o_string *word, struct p_context *ctx)
|
static int reserved_word(o_string *word, struct p_context *ctx)
|
||||||
{
|
{
|
||||||
struct reserved_combo {
|
struct reserved_combo {
|
||||||
char literal[7];
|
char literal[6];
|
||||||
unsigned char res;
|
unsigned char res;
|
||||||
|
unsigned char assignment_flag;
|
||||||
int flag;
|
int flag;
|
||||||
};
|
};
|
||||||
enum {
|
enum {
|
||||||
@ -2939,29 +2942,29 @@ static int reserved_word(const o_string *word, struct p_context *ctx)
|
|||||||
*/
|
*/
|
||||||
static const struct reserved_combo reserved_list[] = {
|
static const struct reserved_combo reserved_list[] = {
|
||||||
#if ENABLE_HUSH_IF
|
#if ENABLE_HUSH_IF
|
||||||
{ "!", RES_NONE, 0 },
|
{ "!", RES_NONE, NOT_ASSIGNMENT , 0 },
|
||||||
{ "if", RES_IF, FLAG_THEN | FLAG_START },
|
{ "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START },
|
||||||
{ "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
|
{ "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
|
||||||
{ "elif", RES_ELIF, FLAG_THEN },
|
{ "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN },
|
||||||
{ "else", RES_ELSE, FLAG_FI },
|
{ "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI },
|
||||||
{ "fi", RES_FI, FLAG_END },
|
{ "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
{ "for", RES_FOR, FLAG_IN | FLAG_DO | FLAG_START },
|
{ "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
|
||||||
{ "while", RES_WHILE, FLAG_DO | FLAG_START },
|
{ "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
|
||||||
{ "until", RES_UNTIL, FLAG_DO | FLAG_START },
|
{ "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
|
||||||
{ "in", RES_IN, FLAG_DO },
|
{ "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
|
||||||
{ "do", RES_DO, FLAG_DONE },
|
{ "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE },
|
||||||
{ "done", RES_DONE, FLAG_END },
|
{ "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
{ "case", RES_CASE, FLAG_MATCH | FLAG_START },
|
{ "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
|
||||||
{ "esac", RES_ESAC, FLAG_END },
|
{ "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
static const struct reserved_combo reserved_match = {
|
static const struct reserved_combo reserved_match = {
|
||||||
"" /* "match" */, RES_MATCH, FLAG_MATCH | FLAG_ESAC
|
"", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
const struct reserved_combo *r;
|
const struct reserved_combo *r;
|
||||||
@ -3008,6 +3011,7 @@ static int reserved_word(const o_string *word, struct p_context *ctx)
|
|||||||
*ctx = *old; /* physical copy */
|
*ctx = *old; /* physical copy */
|
||||||
free(old);
|
free(old);
|
||||||
}
|
}
|
||||||
|
word->o_assignment = r->assignment_flag;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -3021,17 +3025,22 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
struct child_prog *child = ctx->child;
|
struct child_prog *child = ctx->child;
|
||||||
|
|
||||||
debug_printf_parse("done_word entered: '%s' %p\n", word->data, child);
|
debug_printf_parse("done_word entered: '%s' %p\n", word->data, child);
|
||||||
/* 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 = NOT_ASSIGNMENT;
|
|
||||||
} else {
|
|
||||||
word->o_assignment = MAYBE_ASSIGNMENT;
|
|
||||||
}
|
|
||||||
if (word->length == 0 && word->nonnull == 0) {
|
if (word->length == 0 && word->nonnull == 0) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
/* 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)
|
||||||
|
child->assignment_cnt++;
|
||||||
|
word->o_assignment = MAYBE_ASSIGNMENT;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->pending_redirect) {
|
if (ctx->pending_redirect) {
|
||||||
/* We do not glob in e.g. >*.tmp case. bash seems to glob here
|
/* We do not glob in e.g. >*.tmp case. bash seems to glob here
|
||||||
* only if run as "bash", not "sh" */
|
* only if run as "bash", not "sh" */
|
||||||
@ -3066,7 +3075,6 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
debug_printf_parse(": checking '%s' for reserved-ness\n", word->data);
|
debug_printf_parse(": checking '%s' for reserved-ness\n", word->data);
|
||||||
if (reserved_word(word, ctx)) {
|
if (reserved_word(word, ctx)) {
|
||||||
o_reset(word);
|
o_reset(word);
|
||||||
word->o_assignment = NOT_ASSIGNMENT;
|
|
||||||
debug_printf_parse("done_word return %d\n", (ctx->ctx_res_w == RES_SNTX));
|
debug_printf_parse("done_word return %d\n", (ctx->ctx_res_w == RES_SNTX));
|
||||||
return (ctx->ctx_res_w == RES_SNTX);
|
return (ctx->ctx_res_w == RES_SNTX);
|
||||||
}
|
}
|
||||||
@ -3343,11 +3351,6 @@ static int process_command_subs(o_string *dest,
|
|||||||
o_addchr(dest, '\n');
|
o_addchr(dest, '\n');
|
||||||
eol_cnt--;
|
eol_cnt--;
|
||||||
}
|
}
|
||||||
// /* Even unquoted `echo '\'` results in two backslashes
|
|
||||||
// * (which are converted into one by globbing later) */
|
|
||||||
// if (!dest->o_quote && ch == '\\') {
|
|
||||||
// o_addchr(dest, ch);
|
|
||||||
// }
|
|
||||||
o_addQchr(dest, ch);
|
o_addQchr(dest, ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3367,6 +3370,9 @@ static int process_command_subs(o_string *dest,
|
|||||||
static int parse_group(o_string *dest, struct p_context *ctx,
|
static int parse_group(o_string *dest, struct p_context *ctx,
|
||||||
struct in_str *input, int ch)
|
struct in_str *input, int ch)
|
||||||
{
|
{
|
||||||
|
/* NB: parse_group may create and use its own o_string,
|
||||||
|
* without any code changes. It just so happens that code is smaller
|
||||||
|
* if we (ab)use caller's one. */
|
||||||
int rcode;
|
int rcode;
|
||||||
const char *endch = NULL;
|
const char *endch = NULL;
|
||||||
struct p_context sub;
|
struct p_context sub;
|
||||||
@ -3624,7 +3630,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
* A single-quote triggers a bypass of the main loop until its mate is
|
* A single-quote triggers a bypass of the main loop until its mate is
|
||||||
* found. When recursing, quote state is passed in via dest->o_quote. */
|
* found. When recursing, quote state is passed in via dest->o_quote. */
|
||||||
|
|
||||||
debug_printf_parse("parse_stream entered, end_trigger='%s'\n", end_trigger);
|
debug_printf_parse("parse_stream entered, end_trigger='%s' dest->o_assignment:%d\n", end_trigger, dest->o_assignment);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
m = CHAR_IFS;
|
m = CHAR_IFS;
|
||||||
@ -3647,7 +3653,8 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
o_addQchr(dest, ch);
|
o_addQchr(dest, ch);
|
||||||
if (dest->o_assignment == MAYBE_ASSIGNMENT
|
if ((dest->o_assignment == MAYBE_ASSIGNMENT
|
||||||
|
|| dest->o_assignment == WORD_IS_KEYWORD)
|
||||||
&& ch == '='
|
&& ch == '='
|
||||||
&& is_assignment(dest->data)
|
&& is_assignment(dest->data)
|
||||||
) {
|
) {
|
||||||
@ -3676,6 +3683,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
|
dest->o_assignment = MAYBE_ASSIGNMENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (end_trigger) {
|
if (end_trigger) {
|
||||||
@ -3686,6 +3694,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
done_word(dest, ctx);
|
done_word(dest, ctx);
|
||||||
//err chk?
|
//err chk?
|
||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
|
dest->o_assignment = MAYBE_ASSIGNMENT;
|
||||||
}
|
}
|
||||||
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))
|
||||||
@ -3841,6 +3850,10 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
new_cmd:
|
||||||
|
/* We just finished a cmd. New one may start
|
||||||
|
* with an assignment */
|
||||||
|
dest->o_assignment = MAYBE_ASSIGNMENT;
|
||||||
break;
|
break;
|
||||||
case '&':
|
case '&':
|
||||||
done_word(dest, ctx);
|
done_word(dest, ctx);
|
||||||
@ -3850,14 +3863,14 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
} else {
|
} else {
|
||||||
done_pipe(ctx, PIPE_BG);
|
done_pipe(ctx, PIPE_BG);
|
||||||
}
|
}
|
||||||
break;
|
goto new_cmd;
|
||||||
case '|':
|
case '|':
|
||||||
done_word(dest, ctx);
|
done_word(dest, ctx);
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
if (ctx->ctx_res_w == RES_MATCH)
|
if (ctx->ctx_res_w == RES_MATCH)
|
||||||
break; /* we are in case's "word | word)" */
|
break; /* we are in case's "word | word)" */
|
||||||
#endif
|
#endif
|
||||||
if (next == '|') {
|
if (next == '|') { /* || */
|
||||||
i_getch(input);
|
i_getch(input);
|
||||||
done_pipe(ctx, PIPE_OR);
|
done_pipe(ctx, PIPE_OR);
|
||||||
} else {
|
} else {
|
||||||
@ -3866,7 +3879,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
* "echo foo 2| cat" yields "foo 2". */
|
* "echo foo 2| cat" yields "foo 2". */
|
||||||
done_command(ctx);
|
done_command(ctx);
|
||||||
}
|
}
|
||||||
break;
|
goto new_cmd;
|
||||||
case '(':
|
case '(':
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
/* "case... in [(]word)..." - skip '(' */
|
/* "case... in [(]word)..." - skip '(' */
|
||||||
@ -3881,7 +3894,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
debug_printf_parse("parse_stream return 1: parse_group returned non-0\n");
|
debug_printf_parse("parse_stream return 1: parse_group returned non-0\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
goto new_cmd;
|
||||||
case ')':
|
case ')':
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
if (ctx->ctx_res_w == RES_MATCH)
|
if (ctx->ctx_res_w == RES_MATCH)
|
||||||
@ -3947,6 +3960,7 @@ static int parse_and_run_stream(struct in_str *inp, int parse_flag)
|
|||||||
/* We will stop & execute after each ';' or '\n'.
|
/* We will stop & execute after each ';' or '\n'.
|
||||||
* Example: "sleep 9999; echo TEST" + ctrl-C:
|
* Example: "sleep 9999; echo TEST" + ctrl-C:
|
||||||
* TEST should be printed */
|
* TEST should be printed */
|
||||||
|
temp.o_assignment = MAYBE_ASSIGNMENT;
|
||||||
rcode = parse_stream(&temp, &ctx, inp, ";\n");
|
rcode = parse_stream(&temp, &ctx, inp, ";\n");
|
||||||
#if HAS_KEYWORDS
|
#if HAS_KEYWORDS
|
||||||
if (rcode != 1 && ctx.old_flag != 0) {
|
if (rcode != 1 && ctx.old_flag != 0) {
|
||||||
@ -4006,9 +4020,7 @@ static void setup_job_control(void)
|
|||||||
{
|
{
|
||||||
pid_t shell_pgrp;
|
pid_t shell_pgrp;
|
||||||
|
|
||||||
// G.saved_task_pgrp =
|
|
||||||
shell_pgrp = getpgrp();
|
shell_pgrp = getpgrp();
|
||||||
// debug_printf_jobs("saved_task_pgrp=%d\n", G.saved_task_pgrp);
|
|
||||||
close_on_exec_on(G.interactive_fd);
|
close_on_exec_on(G.interactive_fd);
|
||||||
|
|
||||||
/* If we were ran as 'hush &',
|
/* If we were ran as 'hush &',
|
||||||
@ -4302,7 +4314,7 @@ static int builtin_exec(char **argv)
|
|||||||
char **ptrs2free = alloc_ptrs(argv);
|
char **ptrs2free = alloc_ptrs(argv);
|
||||||
#endif
|
#endif
|
||||||
// FIXME: if exec fails, bash does NOT exit! We do...
|
// FIXME: if exec fails, bash does NOT exit! We do...
|
||||||
pseudo_exec_argv(ptrs2free, argv + 1, NULL);
|
pseudo_exec_argv(ptrs2free, argv + 1, 0, NULL);
|
||||||
/* never returns */
|
/* never returns */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
shell/hush_test/hush-misc/assignment1.right
Normal file
9
shell/hush_test/hush-misc/assignment1.right
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
if1:0
|
||||||
|
while1:0
|
||||||
|
until1:0
|
||||||
|
if2:0
|
||||||
|
while2:0
|
||||||
|
until2:0
|
||||||
|
if3:0
|
||||||
|
while3:0
|
||||||
|
until3:0
|
42
shell/hush_test/hush-misc/assignment1.tests
Executable file
42
shell/hush_test/hush-misc/assignment1.tests
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
# Assignments after some keywords should still work
|
||||||
|
|
||||||
|
if a=1 true; then a=1 true; elif a=1 true; then a=1 true; else a=1 true; fi
|
||||||
|
echo if1:$?
|
||||||
|
while a=1 true; do a=1 true; break; done
|
||||||
|
echo while1:$?
|
||||||
|
until a=1 false; do a=1 true; break; done
|
||||||
|
echo until1:$?
|
||||||
|
|
||||||
|
if a=1 true
|
||||||
|
then a=1 true
|
||||||
|
elif a=1 true
|
||||||
|
then a=1 true
|
||||||
|
else a=1 true
|
||||||
|
fi
|
||||||
|
echo if2:$?
|
||||||
|
while a=1 true
|
||||||
|
do a=1 true
|
||||||
|
break
|
||||||
|
done
|
||||||
|
echo while2:$?
|
||||||
|
until a=1 false
|
||||||
|
do a=1 true
|
||||||
|
break
|
||||||
|
done
|
||||||
|
echo until2:$?
|
||||||
|
|
||||||
|
if
|
||||||
|
a=1 true; then
|
||||||
|
a=1 true; elif
|
||||||
|
a=1 true; then
|
||||||
|
a=1 true; else
|
||||||
|
a=1 true; fi
|
||||||
|
echo if3:$?
|
||||||
|
while
|
||||||
|
a=1 true; do
|
||||||
|
a=1 true; break; done
|
||||||
|
echo while3:$?
|
||||||
|
until
|
||||||
|
a=1 false; do
|
||||||
|
a=1 true; break; done
|
||||||
|
echo until3:$?
|
2
shell/hush_test/hush-misc/assignment2.rigth
Normal file
2
shell/hush_test/hush-misc/assignment2.rigth
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
hush: can't exec 'a=b': No such file or directory
|
||||||
|
1
|
4
shell/hush_test/hush-misc/assignment2.tests
Executable file
4
shell/hush_test/hush-misc/assignment2.tests
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
# This must not be interpreted as an assignment
|
||||||
|
a''=b true
|
||||||
|
echo $?
|
||||||
|
# (buglet: $? should be 127. it is currently 1)
|
Loading…
Reference in New Issue
Block a user