hush: fix "while...do f1() {a;}; f1; f1 {b;}; f1; done" bug
This commit is contained in:
parent
75bccfa375
commit
ed055214bb
70
shell/hush.c
70
shell/hush.c
@ -330,7 +330,7 @@ struct redir_struct {
|
|||||||
/* note: for heredocs, rd_filename contains heredoc delimiter,
|
/* note: for heredocs, rd_filename contains heredoc delimiter,
|
||||||
* and subsequently heredoc itself; and rd_dup is a bitmask:
|
* and subsequently heredoc itself; and rd_dup is a bitmask:
|
||||||
* 1: do we need to trim leading tabs?
|
* 1: do we need to trim leading tabs?
|
||||||
* 2: is heredoc quoted (<<'dleim' syntax) ?
|
* 2: is heredoc quoted (<<'delim' syntax) ?
|
||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
typedef enum redir_type {
|
typedef enum redir_type {
|
||||||
@ -357,25 +357,42 @@ struct command {
|
|||||||
int assignment_cnt; /* how many argv[i] are assignments? */
|
int assignment_cnt; /* how many argv[i] are assignments? */
|
||||||
smallint is_stopped; /* is the command currently running? */
|
smallint is_stopped; /* is the command currently running? */
|
||||||
smallint grp_type; /* GRP_xxx */
|
smallint grp_type; /* GRP_xxx */
|
||||||
|
#define GRP_NORMAL 0
|
||||||
|
#define GRP_SUBSHELL 1
|
||||||
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
|
# define GRP_FUNCTION 2
|
||||||
|
#endif
|
||||||
struct pipe *group; /* if non-NULL, this "command" is { list },
|
struct pipe *group; /* if non-NULL, this "command" is { list },
|
||||||
* ( list ), or a compound statement */
|
* ( list ), or a compound statement */
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
char *group_as_string;
|
char *group_as_string;
|
||||||
|
#endif
|
||||||
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
|
struct function *child_func;
|
||||||
|
/* This field is used to prevent a bug here:
|
||||||
|
* while...do f1() {a;}; f1; f1 {b;}; f1; done
|
||||||
|
* When we execute "f1() {a;}" cmd, we create new function and clear
|
||||||
|
* cmd->group, cmd->group_as_string, cmd->argv[0].
|
||||||
|
* when we execute "f1 {b;}", we notice that f1 exists,
|
||||||
|
* and that it's "parent cmd" struct is still "alive",
|
||||||
|
* we put those fields back into cmd->xxx
|
||||||
|
* (struct function has ->parent_cmd ptr to facilitate that).
|
||||||
|
* When we loop back, we can execute "f1() {a;}" again and set f1 correctly.
|
||||||
|
* Without this trick, loop would execute a;b;b;b;...
|
||||||
|
* instead of correct sequence a;b;a;b;...
|
||||||
|
* When command is freed, it severs the link
|
||||||
|
* (sets ->child_func->parent_cmd to NULL).
|
||||||
|
*/
|
||||||
#endif
|
#endif
|
||||||
char **argv; /* command name and arguments */
|
char **argv; /* command name and arguments */
|
||||||
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)
|
||||||
* and on execution these are substituted with their values.
|
* and on execution these are substituted with their values.
|
||||||
* Substitution can make _several_ words out of one argv[n]!
|
* Substitution can make _several_ words out of one argv[n]!
|
||||||
* Example: argv[0]=='.^C*^C.' here: echo .$*.
|
* Example: argv[0]=='.^C*^C.' here: echo .$*.
|
||||||
* References of the form ^C`cmd arg^C are `cmd arg` substitutions.
|
* References of the form ^C`cmd arg^C are `cmd arg` substitutions.
|
||||||
*/
|
*/
|
||||||
#define GRP_NORMAL 0
|
struct redir_struct *redirects; /* I/O redirections */
|
||||||
#define GRP_SUBSHELL 1
|
};
|
||||||
#if ENABLE_HUSH_FUNCTIONS
|
|
||||||
# define GRP_FUNCTION 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct pipe {
|
struct pipe {
|
||||||
struct pipe *next;
|
struct pipe *next;
|
||||||
@ -456,6 +473,7 @@ enum {
|
|||||||
struct function {
|
struct function {
|
||||||
struct function *next;
|
struct function *next;
|
||||||
char *name;
|
char *name;
|
||||||
|
struct command *parent_cmd;
|
||||||
struct pipe *body;
|
struct pipe *body;
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
char *body_as_string;
|
char *body_as_string;
|
||||||
@ -743,7 +761,6 @@ static void syntax_error_unexpected_ch(unsigned lineno, char ch)
|
|||||||
msg[0] = ch;
|
msg[0] = ch;
|
||||||
msg[1] = '\0';
|
msg[1] = '\0';
|
||||||
die_if_script(lineno, "syntax error: unexpected %s", msg);
|
die_if_script(lineno, "syntax error: unexpected %s", msg);
|
||||||
xfunc_die();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HUSH_DEBUG < 2
|
#if HUSH_DEBUG < 2
|
||||||
@ -2573,6 +2590,14 @@ static void free_pipe(struct pipe *pi, int indent)
|
|||||||
debug_printf_clean("%s end group\n", indenter(indent));
|
debug_printf_clean("%s end group\n", indenter(indent));
|
||||||
command->group = NULL;
|
command->group = NULL;
|
||||||
}
|
}
|
||||||
|
/* else is crucial here.
|
||||||
|
* If group != NULL, child_func is meaningless */
|
||||||
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
|
else if (command->child_func) {
|
||||||
|
debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
|
||||||
|
command->child_func->parent_cmd = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
free(command->group_as_string);
|
free(command->group_as_string);
|
||||||
command->group_as_string = NULL;
|
command->group_as_string = NULL;
|
||||||
@ -3173,9 +3198,24 @@ static int run_pipe(struct pipe *pi)
|
|||||||
|
|
||||||
while ((funcp = *funcpp) != NULL) {
|
while ((funcp = *funcpp) != NULL) {
|
||||||
if (strcmp(funcp->name, command->argv[0]) == 0) {
|
if (strcmp(funcp->name, command->argv[0]) == 0) {
|
||||||
debug_printf_exec("replacing function '%s'\n", funcp->name);
|
struct command *cmd = funcp->parent_cmd;
|
||||||
|
|
||||||
|
debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
|
||||||
|
if (!cmd) {
|
||||||
|
debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
|
||||||
free(funcp->name);
|
free(funcp->name);
|
||||||
free_pipe_list(funcp->body, /* indent: */ 0);
|
free_pipe_list(funcp->body, /* indent: */ 0);
|
||||||
|
#if !BB_MMU
|
||||||
|
free(funcp->body_as_string);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
|
||||||
|
cmd->argv[0] = funcp->name;
|
||||||
|
cmd->group = funcp->body;
|
||||||
|
#if !BB_MMU
|
||||||
|
cmd->group_as_string = funcp->body_as_string;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
goto skip;
|
goto skip;
|
||||||
}
|
}
|
||||||
funcpp = &funcp->next;
|
funcpp = &funcp->next;
|
||||||
@ -3192,13 +3232,9 @@ static int run_pipe(struct pipe *pi)
|
|||||||
#endif
|
#endif
|
||||||
command->group = NULL;
|
command->group = NULL;
|
||||||
command->argv[0] = NULL;
|
command->argv[0] = NULL;
|
||||||
free_strings(command->argv);
|
debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
|
||||||
command->argv = NULL;
|
funcp->parent_cmd = command;
|
||||||
/* note: if we are in a loop, future "executions"
|
command->child_func = funcp;
|
||||||
* of func def will see it as null command since
|
|
||||||
* command->group == NULL and command->argv == NULL */
|
|
||||||
//this isn't exactly right: while...do f1() {a;}; f1; f1 {b;}; done
|
|
||||||
//second loop will execute b!
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,8 @@ HERE
|
|||||||
echo >/dev/null ${var%%*}
|
echo >/dev/null ${var%%*}
|
||||||
set -- par1_$i par2_$i par3_$i par4_$i
|
set -- par1_$i par2_$i par3_$i par4_$i
|
||||||
trap "echo trap$i" WINCH
|
trap "echo trap$i" WINCH
|
||||||
f() { echo $1; }
|
f() { true; true; true; true; true; true; true; true; }
|
||||||
|
f() { true; true; true; true; true; true; true; true; echo $1; }
|
||||||
f >/dev/null
|
f >/dev/null
|
||||||
: $((i++))
|
: $((i++))
|
||||||
done
|
done
|
||||||
@ -127,7 +128,8 @@ HERE
|
|||||||
echo >/dev/null ${var%%*}
|
echo >/dev/null ${var%%*}
|
||||||
set -- par1_$i par2_$i par3_$i par4_$i
|
set -- par1_$i par2_$i par3_$i par4_$i
|
||||||
trap "echo trap$i" WINCH
|
trap "echo trap$i" WINCH
|
||||||
f() { echo $1; }
|
f() { true; true; true; true; true; true; true; true; }
|
||||||
|
f() { true; true; true; true; true; true; true; true; echo $1; }
|
||||||
f >/dev/null
|
f >/dev/null
|
||||||
: $((i++))
|
: $((i++))
|
||||||
done
|
done
|
||||||
|
Loading…
Reference in New Issue
Block a user