mirror of
https://github.com/sheumann/hush.git
synced 2024-08-25 21:28:57 +00:00
hush: massive renaming of ill-named structures and fields
hush: error out on constructs like: $ abc(def) - was working as if it was (abcdef) $ case b in abc(a|(b) echo YES; esac - was ignoring 'abc' and extra '('
This commit is contained in:
parent
578de8644c
commit
9af22c7626
408
shell/hush.c
408
shell/hush.c
@ -45,12 +45,11 @@
|
|||||||
* aliases
|
* aliases
|
||||||
* Arithmetic Expansion
|
* Arithmetic Expansion
|
||||||
* <(list) and >(list) Process Substitution
|
* <(list) and >(list) Process Substitution
|
||||||
* reserved words: case, esac, select, function
|
* reserved words: select, function
|
||||||
* Here Documents ( << word )
|
* Here Documents ( << word )
|
||||||
* Functions
|
* Functions
|
||||||
* Major bugs:
|
* Major bugs:
|
||||||
* job handling woefully incomplete and buggy (improved --vda)
|
* job handling woefully incomplete and buggy (improved --vda)
|
||||||
* reserved word execution woefully incomplete and buggy
|
|
||||||
* to-do:
|
* to-do:
|
||||||
* port selected bugfixes from post-0.49 busybox lash - done?
|
* port selected bugfixes from post-0.49 busybox lash - done?
|
||||||
* change { and } from special chars to reserved words
|
* change { and } from special chars to reserved words
|
||||||
@ -291,23 +290,6 @@ typedef enum reserved_style {
|
|||||||
RES_SNTX
|
RES_SNTX
|
||||||
} reserved_style;
|
} reserved_style;
|
||||||
|
|
||||||
/* This holds pointers to the various results of parsing */
|
|
||||||
struct p_context {
|
|
||||||
struct child_prog *child;
|
|
||||||
struct pipe *list_head;
|
|
||||||
struct pipe *pipe;
|
|
||||||
struct redir_struct *pending_redirect;
|
|
||||||
#if HAS_KEYWORDS
|
|
||||||
smallint ctx_res_w;
|
|
||||||
smallint ctx_inverted; /* "! cmd | cmd" */
|
|
||||||
#if ENABLE_HUSH_CASE
|
|
||||||
smallint ctx_dsemicolon; /* ";;" seen */
|
|
||||||
#endif
|
|
||||||
int old_flag; /* bitmask of FLAG_xxx, for figuring out valid reserved words */
|
|
||||||
struct p_context *stack;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct redir_struct {
|
struct redir_struct {
|
||||||
struct redir_struct *next;
|
struct redir_struct *next;
|
||||||
char *rd_filename; /* filename */
|
char *rd_filename; /* filename */
|
||||||
@ -316,14 +298,14 @@ struct redir_struct {
|
|||||||
smallint /*enum redir_type*/ rd_type;
|
smallint /*enum redir_type*/ rd_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct child_prog {
|
struct command {
|
||||||
pid_t pid; /* 0 if exited */
|
pid_t pid; /* 0 if exited */
|
||||||
int assignment_cnt; /* how many argv[i] are assignments? */
|
int assignment_cnt; /* how many argv[i] are assignments? */
|
||||||
smallint is_stopped; /* is the program currently running? */
|
smallint is_stopped; /* is the command 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,
|
struct pipe *group; /* if non-NULL, this "prog" is {} group,
|
||||||
* subshell, or a compound statement */
|
* subshell, or a compound statement */
|
||||||
char **argv; /* program name and arguments */
|
char **argv; /* command name and arguments */
|
||||||
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)
|
||||||
@ -335,20 +317,37 @@ struct child_prog {
|
|||||||
|
|
||||||
struct pipe {
|
struct pipe {
|
||||||
struct pipe *next;
|
struct pipe *next;
|
||||||
int num_progs; /* total number of programs in job */
|
int num_cmds; /* total number of commands in job */
|
||||||
int alive_progs; /* number of programs running (not exited) */
|
int alive_cmds; /* number of commands running (not exited) */
|
||||||
int stopped_progs; /* number of programs alive, but stopped */
|
int stopped_cmds; /* number of commands alive, but stopped */
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
int jobid; /* job number */
|
int jobid; /* job number */
|
||||||
pid_t pgrp; /* process group ID for the job */
|
pid_t pgrp; /* process group ID for the job */
|
||||||
char *cmdtext; /* name of job */
|
char *cmdtext; /* name of job */
|
||||||
#endif
|
#endif
|
||||||
struct child_prog *progs; /* array of commands in pipe */
|
struct command *cmds; /* array of commands in pipe */
|
||||||
smallint followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
|
smallint followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
|
||||||
IF_HAS_KEYWORDS(smallint pi_inverted;) /* "! cmd | cmd" */
|
IF_HAS_KEYWORDS(smallint pi_inverted;) /* "! cmd | cmd" */
|
||||||
IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */
|
IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* This holds pointers to the various results of parsing */
|
||||||
|
struct parse_context {
|
||||||
|
struct command *command;
|
||||||
|
struct pipe *list_head;
|
||||||
|
struct pipe *pipe;
|
||||||
|
struct redir_struct *pending_redirect;
|
||||||
|
#if HAS_KEYWORDS
|
||||||
|
smallint ctx_res_w;
|
||||||
|
smallint ctx_inverted; /* "! cmd | cmd" */
|
||||||
|
#if ENABLE_HUSH_CASE
|
||||||
|
smallint ctx_dsemicolon; /* ";;" seen */
|
||||||
|
#endif
|
||||||
|
int old_flag; /* bitmask of FLAG_xxx, for figuring out valid reserved words */
|
||||||
|
struct parse_context *stack;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
/* 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.
|
||||||
* Neither of these (de)allocates the strings.
|
* Neither of these (de)allocates the strings.
|
||||||
@ -523,23 +522,23 @@ static void setup_string_in_str(struct in_str *i, const char *s);
|
|||||||
static int free_pipe_list(struct pipe *head, int indent);
|
static int free_pipe_list(struct pipe *head, int indent);
|
||||||
static int free_pipe(struct pipe *pi, int indent);
|
static int free_pipe(struct pipe *pi, int indent);
|
||||||
/* really run the final data structures: */
|
/* really run the final data structures: */
|
||||||
static int setup_redirects(struct child_prog *prog, int squirrel[]);
|
static int setup_redirects(struct command *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, assignment_cnt, argv_expanded) \
|
#define pseudo_exec_argv(ptrs2free, argv, assignment_cnt, argv_expanded) \
|
||||||
pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
|
pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
|
||||||
#define pseudo_exec(ptrs2free, child, argv_expanded) \
|
#define pseudo_exec(ptrs2free, command, argv_expanded) \
|
||||||
pseudo_exec(child, argv_expanded)
|
pseudo_exec(command, argv_expanded)
|
||||||
#endif
|
#endif
|
||||||
static void pseudo_exec_argv(char **ptrs2free, char **argv, int assignment_cnt, 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 command *command, char **argv_expanded) NORETURN;
|
||||||
static int run_pipe(struct pipe *pi);
|
static int run_pipe(struct pipe *pi);
|
||||||
/* data structure manipulation: */
|
/* data structure manipulation: */
|
||||||
static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
|
static int setup_redirect(struct parse_context *ctx, int fd, redir_type style, struct in_str *input);
|
||||||
static void initialize_context(struct p_context *ctx);
|
static void initialize_context(struct parse_context *ctx);
|
||||||
static int done_word(o_string *dest, struct p_context *ctx);
|
static int done_word(o_string *dest, struct parse_context *ctx);
|
||||||
static int done_command(struct p_context *ctx);
|
static int done_command(struct parse_context *ctx);
|
||||||
static void done_pipe(struct p_context *ctx, pipe_style type);
|
static void done_pipe(struct parse_context *ctx, pipe_style type);
|
||||||
/* primary string parsing: */
|
/* primary string parsing: */
|
||||||
static int redirect_dup_num(struct in_str *input);
|
static int redirect_dup_num(struct in_str *input);
|
||||||
static int redirect_opt_num(o_string *o);
|
static int redirect_opt_num(o_string *o);
|
||||||
@ -547,11 +546,11 @@ static int redirect_opt_num(o_string *o);
|
|||||||
static int process_command_subs(o_string *dest,
|
static int process_command_subs(o_string *dest,
|
||||||
struct in_str *input, const char *subst_end);
|
struct in_str *input, const char *subst_end);
|
||||||
#endif
|
#endif
|
||||||
static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);
|
static int parse_group(o_string *dest, struct parse_context *ctx, struct in_str *input, int ch);
|
||||||
static const char *lookup_param(const char *src);
|
static const char *lookup_param(const char *src);
|
||||||
static int handle_dollar(o_string *dest,
|
static int handle_dollar(o_string *dest,
|
||||||
struct in_str *input);
|
struct in_str *input);
|
||||||
static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, const char *end_trigger);
|
static int parse_stream(o_string *dest, struct parse_context *ctx, struct in_str *input0, const char *end_trigger);
|
||||||
/* setup: */
|
/* setup: */
|
||||||
static int parse_and_run_stream(struct in_str *inp, int parse_flag);
|
static int parse_and_run_stream(struct in_str *inp, int parse_flag);
|
||||||
static int parse_and_run_string(const char *s, int parse_flag);
|
static int parse_and_run_string(const char *s, int parse_flag);
|
||||||
@ -837,7 +836,7 @@ static void handler_ctrl_z(int sig UNUSED_PARAM)
|
|||||||
/* parent */
|
/* parent */
|
||||||
/* finish filling up pipe info */
|
/* finish filling up pipe info */
|
||||||
G.toplevel_list->pgrp = pid; /* child is in its own pgrp */
|
G.toplevel_list->pgrp = pid; /* child is in its own pgrp */
|
||||||
G.toplevel_list->progs[0].pid = pid;
|
G.toplevel_list->cmds[0].pid = pid;
|
||||||
/* parent needs to longjmp out of running nofork.
|
/* parent needs to longjmp out of running nofork.
|
||||||
* we will "return" exitcode 0, with child put in background */
|
* we will "return" exitcode 0, with child put in background */
|
||||||
// as usual we can have all kinds of nasty problems with leaked malloc data here
|
// as usual we can have all kinds of nasty problems with leaked malloc data here
|
||||||
@ -1334,7 +1333,7 @@ static void setup_string_in_str(struct in_str *i, const char *s)
|
|||||||
|
|
||||||
/* squirrel != NULL means we squirrel away copies of stdin, stdout,
|
/* squirrel != NULL means we squirrel away copies of stdin, stdout,
|
||||||
* and stderr if they are redirected. */
|
* and stderr if they are redirected. */
|
||||||
static int setup_redirects(struct child_prog *prog, int squirrel[])
|
static int setup_redirects(struct command *prog, int squirrel[])
|
||||||
{
|
{
|
||||||
int openfd, mode;
|
int openfd, mode;
|
||||||
struct redir_struct *redir;
|
struct redir_struct *redir;
|
||||||
@ -1467,18 +1466,18 @@ static void pseudo_exec_argv(char **ptrs2free, char **argv, int assignment_cnt,
|
|||||||
|
|
||||||
/* Called after [v]fork() in run_pipe()
|
/* Called after [v]fork() in run_pipe()
|
||||||
*/
|
*/
|
||||||
static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded)
|
static void pseudo_exec(char **ptrs2free, struct command *command, char **argv_expanded)
|
||||||
{
|
{
|
||||||
if (child->argv)
|
if (command->argv)
|
||||||
pseudo_exec_argv(ptrs2free, child->argv, child->assignment_cnt, argv_expanded);
|
pseudo_exec_argv(ptrs2free, command->argv, command->assignment_cnt, argv_expanded);
|
||||||
|
|
||||||
if (child->group) {
|
if (command->group) {
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
bb_error_msg_and_die("nested lists are not supported on NOMMU");
|
bb_error_msg_and_die("nested lists are not supported on NOMMU");
|
||||||
#else
|
#else
|
||||||
int rcode;
|
int rcode;
|
||||||
debug_printf_exec("pseudo_exec: run_list\n");
|
debug_printf_exec("pseudo_exec: run_list\n");
|
||||||
rcode = run_list(child->group);
|
rcode = run_list(command->group);
|
||||||
/* OK to leak memory by not calling free_pipe_list,
|
/* OK to leak memory by not calling free_pipe_list,
|
||||||
* since this process is about to exit */
|
* since this process is about to exit */
|
||||||
_exit(rcode);
|
_exit(rcode);
|
||||||
@ -1502,7 +1501,7 @@ static const char *get_cmdtext(struct pipe *pi)
|
|||||||
* On subsequent bg argv is trashed, but we won't use it */
|
* On subsequent bg argv is trashed, but we won't use it */
|
||||||
if (pi->cmdtext)
|
if (pi->cmdtext)
|
||||||
return pi->cmdtext;
|
return pi->cmdtext;
|
||||||
argv = pi->progs[0].argv;
|
argv = pi->cmds[0].argv;
|
||||||
if (!argv || !argv[0]) {
|
if (!argv || !argv[0]) {
|
||||||
pi->cmdtext = xzalloc(1);
|
pi->cmdtext = xzalloc(1);
|
||||||
return pi->cmdtext;
|
return pi->cmdtext;
|
||||||
@ -1511,7 +1510,7 @@ static const char *get_cmdtext(struct pipe *pi)
|
|||||||
len = 0;
|
len = 0;
|
||||||
do len += strlen(*argv) + 1; while (*++argv);
|
do len += strlen(*argv) + 1; while (*++argv);
|
||||||
pi->cmdtext = p = xmalloc(len);
|
pi->cmdtext = p = xmalloc(len);
|
||||||
argv = pi->progs[0].argv;
|
argv = pi->cmds[0].argv;
|
||||||
do {
|
do {
|
||||||
len = strlen(*argv);
|
len = strlen(*argv);
|
||||||
memcpy(p, *argv, len);
|
memcpy(p, *argv, len);
|
||||||
@ -1545,12 +1544,12 @@ static void insert_bg_job(struct pipe *pi)
|
|||||||
|
|
||||||
/* Physically copy the struct job */
|
/* Physically copy the struct job */
|
||||||
memcpy(thejob, pi, sizeof(struct pipe));
|
memcpy(thejob, pi, sizeof(struct pipe));
|
||||||
thejob->progs = xzalloc(sizeof(pi->progs[0]) * pi->num_progs);
|
thejob->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
|
||||||
/* We cannot copy entire pi->progs[] vector! Double free()s will happen */
|
/* We cannot copy entire pi->cmds[] vector! Double free()s will happen */
|
||||||
for (i = 0; i < pi->num_progs; i++) {
|
for (i = 0; i < pi->num_cmds; i++) {
|
||||||
// TODO: do we really need to have so many fields which are just dead weight
|
// TODO: do we really need to have so many fields which are just dead weight
|
||||||
// at execution stage?
|
// at execution stage?
|
||||||
thejob->progs[i].pid = pi->progs[i].pid;
|
thejob->cmds[i].pid = pi->cmds[i].pid;
|
||||||
/* all other fields are not used and stay zero */
|
/* all other fields are not used and stay zero */
|
||||||
}
|
}
|
||||||
thejob->next = NULL;
|
thejob->next = NULL;
|
||||||
@ -1558,8 +1557,8 @@ static void insert_bg_job(struct pipe *pi)
|
|||||||
|
|
||||||
/* We don't wait for background thejobs to return -- append it
|
/* We don't wait for background thejobs to return -- append it
|
||||||
to the list of backgrounded thejobs and leave it alone */
|
to the list of backgrounded thejobs and leave it alone */
|
||||||
printf("[%d] %d %s\n", thejob->jobid, thejob->progs[0].pid, thejob->cmdtext);
|
printf("[%d] %d %s\n", thejob->jobid, thejob->cmds[0].pid, thejob->cmdtext);
|
||||||
G.last_bg_pid = thejob->progs[0].pid;
|
G.last_bg_pid = thejob->cmds[0].pid;
|
||||||
G.last_jobid = thejob->jobid;
|
G.last_jobid = thejob->jobid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1585,7 +1584,7 @@ static void remove_bg_job(struct pipe *pi)
|
|||||||
static void delete_finished_bg_job(struct pipe *pi)
|
static void delete_finished_bg_job(struct pipe *pi)
|
||||||
{
|
{
|
||||||
remove_bg_job(pi);
|
remove_bg_job(pi);
|
||||||
pi->stopped_progs = 0;
|
pi->stopped_cmds = 0;
|
||||||
free_pipe(pi, 0);
|
free_pipe(pi, 0);
|
||||||
free(pi);
|
free(pi);
|
||||||
}
|
}
|
||||||
@ -1637,29 +1636,29 @@ static int checkjobs(struct pipe* fg_pipe)
|
|||||||
#endif
|
#endif
|
||||||
/* Were we asked to wait for fg pipe? */
|
/* Were we asked to wait for fg pipe? */
|
||||||
if (fg_pipe) {
|
if (fg_pipe) {
|
||||||
for (i = 0; i < fg_pipe->num_progs; i++) {
|
for (i = 0; i < fg_pipe->num_cmds; i++) {
|
||||||
debug_printf_jobs("check pid %d\n", fg_pipe->progs[i].pid);
|
debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
|
||||||
if (fg_pipe->progs[i].pid != childpid)
|
if (fg_pipe->cmds[i].pid != childpid)
|
||||||
continue;
|
continue;
|
||||||
/* printf("process %d exit %d\n", i, WEXITSTATUS(status)); */
|
/* printf("process %d exit %d\n", i, WEXITSTATUS(status)); */
|
||||||
if (dead) {
|
if (dead) {
|
||||||
fg_pipe->progs[i].pid = 0;
|
fg_pipe->cmds[i].pid = 0;
|
||||||
fg_pipe->alive_progs--;
|
fg_pipe->alive_cmds--;
|
||||||
if (i == fg_pipe->num_progs - 1) {
|
if (i == fg_pipe->num_cmds - 1) {
|
||||||
/* last process gives overall exitstatus */
|
/* last process gives overall exitstatus */
|
||||||
rcode = WEXITSTATUS(status);
|
rcode = WEXITSTATUS(status);
|
||||||
IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
|
IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fg_pipe->progs[i].is_stopped = 1;
|
fg_pipe->cmds[i].is_stopped = 1;
|
||||||
fg_pipe->stopped_progs++;
|
fg_pipe->stopped_cmds++;
|
||||||
}
|
}
|
||||||
debug_printf_jobs("fg_pipe: alive_progs %d stopped_progs %d\n",
|
debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
|
||||||
fg_pipe->alive_progs, fg_pipe->stopped_progs);
|
fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
|
||||||
if (fg_pipe->alive_progs - fg_pipe->stopped_progs <= 0) {
|
if (fg_pipe->alive_cmds - fg_pipe->stopped_cmds <= 0) {
|
||||||
/* All processes in fg pipe have exited/stopped */
|
/* All processes in fg pipe have exited/stopped */
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
if (fg_pipe->alive_progs)
|
if (fg_pipe->alive_cmds)
|
||||||
insert_bg_job(fg_pipe);
|
insert_bg_job(fg_pipe);
|
||||||
#endif
|
#endif
|
||||||
return rcode;
|
return rcode;
|
||||||
@ -1674,8 +1673,8 @@ static int checkjobs(struct pipe* fg_pipe)
|
|||||||
/* We asked to wait for bg or orphaned children */
|
/* We asked to wait for bg or orphaned children */
|
||||||
/* No need to remember exitcode in this case */
|
/* No need to remember exitcode in this case */
|
||||||
for (pi = G.job_list; pi; pi = pi->next) {
|
for (pi = G.job_list; pi; pi = pi->next) {
|
||||||
for (i = 0; i < pi->num_progs; i++) {
|
for (i = 0; i < pi->num_cmds; i++) {
|
||||||
if (pi->progs[i].pid == childpid)
|
if (pi->cmds[i].pid == childpid)
|
||||||
goto found_pi_and_prognum;
|
goto found_pi_and_prognum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1686,17 +1685,17 @@ static int checkjobs(struct pipe* fg_pipe)
|
|||||||
found_pi_and_prognum:
|
found_pi_and_prognum:
|
||||||
if (dead) {
|
if (dead) {
|
||||||
/* child exited */
|
/* child exited */
|
||||||
pi->progs[i].pid = 0;
|
pi->cmds[i].pid = 0;
|
||||||
pi->alive_progs--;
|
pi->alive_cmds--;
|
||||||
if (!pi->alive_progs) {
|
if (!pi->alive_cmds) {
|
||||||
printf(JOB_STATUS_FORMAT, pi->jobid,
|
printf(JOB_STATUS_FORMAT, pi->jobid,
|
||||||
"Done", pi->cmdtext);
|
"Done", pi->cmdtext);
|
||||||
delete_finished_bg_job(pi);
|
delete_finished_bg_job(pi);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* child stopped */
|
/* child stopped */
|
||||||
pi->progs[i].is_stopped = 1;
|
pi->cmds[i].is_stopped = 1;
|
||||||
pi->stopped_progs++;
|
pi->stopped_cmds++;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} /* while (waitpid succeeds)... */
|
} /* while (waitpid succeeds)... */
|
||||||
@ -1745,7 +1744,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
int i;
|
int i;
|
||||||
int nextin;
|
int nextin;
|
||||||
int pipefds[2]; /* pipefds[0] is for reading */
|
int pipefds[2]; /* pipefds[0] is for reading */
|
||||||
struct child_prog *child;
|
struct command *command;
|
||||||
char **argv_expanded = NULL;
|
char **argv_expanded = NULL;
|
||||||
char **argv;
|
char **argv;
|
||||||
const struct built_in_command *x;
|
const struct built_in_command *x;
|
||||||
@ -1753,36 +1752,36 @@ static int run_pipe(struct pipe *pi)
|
|||||||
/* it is not always needed, but we aim to smaller code */
|
/* it is not always needed, but we aim to smaller code */
|
||||||
int squirrel[] = { -1, -1, -1 };
|
int squirrel[] = { -1, -1, -1 };
|
||||||
int rcode;
|
int rcode;
|
||||||
const int single_and_fg = (pi->num_progs == 1 && pi->followup != PIPE_BG);
|
const int single_and_fg = (pi->num_cmds == 1 && pi->followup != PIPE_BG);
|
||||||
|
|
||||||
debug_printf_exec("run_pipe start: single_and_fg=%d\n", single_and_fg);
|
debug_printf_exec("run_pipe start: single_and_fg=%d\n", single_and_fg);
|
||||||
|
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
pi->pgrp = -1;
|
pi->pgrp = -1;
|
||||||
#endif
|
#endif
|
||||||
pi->alive_progs = 1;
|
pi->alive_cmds = 1;
|
||||||
pi->stopped_progs = 0;
|
pi->stopped_cmds = 0;
|
||||||
|
|
||||||
/* Check if this is a simple builtin (not part of a pipe).
|
/* Check if this is a simple builtin (not part of a pipe).
|
||||||
* Builtins within pipes have to fork anyway, and are handled in
|
* Builtins within pipes have to fork anyway, and are handled in
|
||||||
* pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
|
* pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
|
||||||
*/
|
*/
|
||||||
child = &(pi->progs[0]);
|
command = &(pi->cmds[0]);
|
||||||
if (single_and_fg && child->group && child->subshell == 0) {
|
if (single_and_fg && command->group && command->subshell == 0) {
|
||||||
debug_printf("non-subshell grouping\n");
|
debug_printf("non-subshell grouping\n");
|
||||||
setup_redirects(child, squirrel);
|
setup_redirects(command, squirrel);
|
||||||
debug_printf_exec(": run_list\n");
|
debug_printf_exec(": run_list\n");
|
||||||
rcode = run_list(child->group) & 0xff;
|
rcode = run_list(command->group) & 0xff;
|
||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
debug_printf_exec("run_pipe return %d\n", rcode);
|
debug_printf_exec("run_pipe return %d\n", rcode);
|
||||||
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
|
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
|
||||||
return rcode;
|
return rcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
argv = child->argv;
|
argv = command->argv;
|
||||||
|
|
||||||
if (single_and_fg && argv != NULL) {
|
if (single_and_fg && argv != NULL) {
|
||||||
i = child->assignment_cnt;
|
i = command->assignment_cnt;
|
||||||
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++) {
|
||||||
@ -1794,7 +1793,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Expand assignments into one string each */
|
/* Expand assignments into one string each */
|
||||||
for (i = 0; i < child->assignment_cnt; i++) {
|
for (i = 0; i < command->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?!
|
||||||
@ -1807,7 +1806,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
if (strcmp(argv_expanded[0], x->cmd) == 0) {
|
if (strcmp(argv_expanded[0], x->cmd) == 0) {
|
||||||
if (x->function == builtin_exec && argv_expanded[1] == NULL) {
|
if (x->function == builtin_exec && argv_expanded[1] == NULL) {
|
||||||
debug_printf("magic exec\n");
|
debug_printf("magic exec\n");
|
||||||
setup_redirects(child, NULL);
|
setup_redirects(command, NULL);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
debug_printf("builtin inline %s\n", argv_expanded[0]);
|
debug_printf("builtin inline %s\n", argv_expanded[0]);
|
||||||
@ -1815,7 +1814,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
* This is perfect for work that comes after exec().
|
* This is perfect for work that comes after exec().
|
||||||
* Is it really safe for inline use? Experimentally,
|
* Is it really safe for inline use? Experimentally,
|
||||||
* things seem to work with glibc. */
|
* things seem to work with glibc. */
|
||||||
setup_redirects(child, squirrel);
|
setup_redirects(command, squirrel);
|
||||||
debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv_expanded[1]);
|
debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv_expanded[1]);
|
||||||
rcode = x->function(argv_expanded) & 0xff;
|
rcode = x->function(argv_expanded) & 0xff;
|
||||||
free(argv_expanded);
|
free(argv_expanded);
|
||||||
@ -1829,7 +1828,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
{
|
{
|
||||||
int a = find_applet_by_name(argv_expanded[0]);
|
int a = find_applet_by_name(argv_expanded[0]);
|
||||||
if (a >= 0 && APPLET_IS_NOFORK(a)) {
|
if (a >= 0 && APPLET_IS_NOFORK(a)) {
|
||||||
setup_redirects(child, squirrel);
|
setup_redirects(command, squirrel);
|
||||||
save_nofork_data(&G.nofork_save);
|
save_nofork_data(&G.nofork_save);
|
||||||
debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]);
|
debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]);
|
||||||
rcode = run_nofork_applet_prime(&G.nofork_save, a, argv_expanded);
|
rcode = run_nofork_applet_prime(&G.nofork_save, a, argv_expanded);
|
||||||
@ -1852,18 +1851,18 @@ static int run_pipe(struct pipe *pi)
|
|||||||
set_jobctrl_sighandler(SIG_IGN);
|
set_jobctrl_sighandler(SIG_IGN);
|
||||||
|
|
||||||
/* Going to fork a child per each pipe member */
|
/* Going to fork a child per each pipe member */
|
||||||
pi->alive_progs = 0;
|
pi->alive_cmds = 0;
|
||||||
nextin = 0;
|
nextin = 0;
|
||||||
|
|
||||||
for (i = 0; i < pi->num_progs; i++) {
|
for (i = 0; i < pi->num_cmds; i++) {
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
char **ptrs2free = NULL;
|
char **ptrs2free = NULL;
|
||||||
#endif
|
#endif
|
||||||
child = &(pi->progs[i]);
|
command = &(pi->cmds[i]);
|
||||||
if (child->argv) {
|
if (command->argv) {
|
||||||
debug_printf_exec(": pipe member '%s' '%s'...\n", child->argv[0], child->argv[1]);
|
debug_printf_exec(": pipe member '%s' '%s'...\n", command->argv[0], command->argv[1]);
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
ptrs2free = alloc_ptrs(child->argv);
|
ptrs2free = alloc_ptrs(command->argv);
|
||||||
#endif
|
#endif
|
||||||
} else
|
} else
|
||||||
debug_printf_exec(": pipe member with no argv\n");
|
debug_printf_exec(": pipe member with no argv\n");
|
||||||
@ -1871,11 +1870,11 @@ static int run_pipe(struct pipe *pi)
|
|||||||
/* pipes are inserted between pairs of commands */
|
/* pipes are inserted between pairs of commands */
|
||||||
pipefds[0] = 0;
|
pipefds[0] = 0;
|
||||||
pipefds[1] = 1;
|
pipefds[1] = 1;
|
||||||
if ((i + 1) < pi->num_progs)
|
if ((i + 1) < pi->num_cmds)
|
||||||
xpipe(pipefds);
|
xpipe(pipefds);
|
||||||
|
|
||||||
child->pid = BB_MMU ? fork() : vfork();
|
command->pid = BB_MMU ? fork() : vfork();
|
||||||
if (!child->pid) { /* child */
|
if (!command->pid) { /* child */
|
||||||
if (ENABLE_HUSH_JOB)
|
if (ENABLE_HUSH_JOB)
|
||||||
die_sleep = 0; /* let nofork's xfuncs die */
|
die_sleep = 0; /* let nofork's xfuncs die */
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
@ -1901,45 +1900,45 @@ static int run_pipe(struct pipe *pi)
|
|||||||
close(pipefds[0]); /* read end */
|
close(pipefds[0]); /* read end */
|
||||||
/* Like bash, explicit redirects override pipes,
|
/* Like bash, explicit redirects override pipes,
|
||||||
* and the pipe fd is available for dup'ing. */
|
* and the pipe fd is available for dup'ing. */
|
||||||
setup_redirects(child, NULL);
|
setup_redirects(command, NULL);
|
||||||
|
|
||||||
/* Restore default handlers just prior to exec */
|
/* Restore default handlers just prior to exec */
|
||||||
set_jobctrl_sighandler(SIG_DFL);
|
set_jobctrl_sighandler(SIG_DFL);
|
||||||
set_misc_sighandler(SIG_DFL);
|
set_misc_sighandler(SIG_DFL);
|
||||||
signal(SIGCHLD, SIG_DFL);
|
signal(SIGCHLD, SIG_DFL);
|
||||||
pseudo_exec(ptrs2free, child, argv_expanded); /* does not return */
|
pseudo_exec(ptrs2free, command, argv_expanded); /* does not return */
|
||||||
}
|
}
|
||||||
free(argv_expanded);
|
free(argv_expanded);
|
||||||
argv_expanded = NULL;
|
argv_expanded = NULL;
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
free_strings(ptrs2free);
|
free_strings(ptrs2free);
|
||||||
#endif
|
#endif
|
||||||
if (child->pid < 0) { /* [v]fork failed */
|
if (command->pid < 0) { /* [v]fork failed */
|
||||||
/* Clearly indicate, was it fork or vfork */
|
/* Clearly indicate, was it fork or vfork */
|
||||||
bb_perror_msg(BB_MMU ? "fork" : "vfork");
|
bb_perror_msg(BB_MMU ? "fork" : "vfork");
|
||||||
} else {
|
} else {
|
||||||
pi->alive_progs++;
|
pi->alive_cmds++;
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
/* Second and next children need to know pid of first one */
|
/* Second and next children need to know pid of first one */
|
||||||
if (pi->pgrp < 0)
|
if (pi->pgrp < 0)
|
||||||
pi->pgrp = child->pid;
|
pi->pgrp = command->pid;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i)
|
if (i)
|
||||||
close(nextin);
|
close(nextin);
|
||||||
if ((i + 1) < pi->num_progs)
|
if ((i + 1) < pi->num_cmds)
|
||||||
close(pipefds[1]); /* write end */
|
close(pipefds[1]); /* write end */
|
||||||
/* Pass read (output) pipe end to next iteration */
|
/* Pass read (output) pipe end to next iteration */
|
||||||
nextin = pipefds[0];
|
nextin = pipefds[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pi->alive_progs) {
|
if (!pi->alive_cmds) {
|
||||||
debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
|
debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_progs);
|
debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1988,16 +1987,16 @@ static void debug_print_tree(struct pipe *pi, int lvl)
|
|||||||
fprintf(stderr, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "",
|
fprintf(stderr, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "",
|
||||||
pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]);
|
pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]);
|
||||||
prn = 0;
|
prn = 0;
|
||||||
while (prn < pi->num_progs) {
|
while (prn < pi->num_cmds) {
|
||||||
struct child_prog *child = &pi->progs[prn];
|
struct command *command = &pi->cmds[prn];
|
||||||
char **argv = child->argv;
|
char **argv = command->argv;
|
||||||
|
|
||||||
fprintf(stderr, "%*s prog %d assignment_cnt:%d", lvl*2, "", prn, child->assignment_cnt);
|
fprintf(stderr, "%*s prog %d assignment_cnt:%d", lvl*2, "", prn, command->assignment_cnt);
|
||||||
if (child->group) {
|
if (command->group) {
|
||||||
fprintf(stderr, " group %s: (argv=%p)\n",
|
fprintf(stderr, " group %s: (argv=%p)\n",
|
||||||
(child->subshell ? "()" : "{}"),
|
(command->subshell ? "()" : "{}"),
|
||||||
argv);
|
argv);
|
||||||
debug_print_tree(child->group, lvl+1);
|
debug_print_tree(command->group, lvl+1);
|
||||||
prn++;
|
prn++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2151,7 +2150,7 @@ static int run_list(struct pipe *pi)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
if (rword == RES_FOR) { /* && pi->num_progs - always == 1 */
|
if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
|
||||||
if (!for_lcur) {
|
if (!for_lcur) {
|
||||||
/* first loop through for */
|
/* first loop through for */
|
||||||
|
|
||||||
@ -2166,31 +2165,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[0].argv)
|
if (!pi->next->cmds[0].argv)
|
||||||
break;
|
break;
|
||||||
vals = pi->next->progs[0].argv;
|
vals = pi->next->cmds[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[0].argv[0];
|
for_varname = pi->cmds[0].argv[0];
|
||||||
pi->progs[0].argv[0] = NULL;
|
pi->cmds[0].argv[0] = NULL;
|
||||||
}
|
}
|
||||||
free(pi->progs[0].argv[0]);
|
free(pi->cmds[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[0].argv[0] = for_varname;
|
pi->cmds[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[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
|
pi->cmds[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
|
||||||
pi->progs[0].assignment_cnt = 1;
|
pi->cmds[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;
|
||||||
@ -2200,7 +2199,7 @@ static int run_list(struct pipe *pi)
|
|||||||
#endif
|
#endif
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
if (rword == RES_CASE) {
|
if (rword == RES_CASE) {
|
||||||
case_word = expand_strvec_to_string(pi->progs->argv);
|
case_word = expand_strvec_to_string(pi->cmds->argv);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (rword == RES_MATCH) {
|
if (rword == RES_MATCH) {
|
||||||
@ -2209,7 +2208,7 @@ static int run_list(struct pipe *pi)
|
|||||||
if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
|
if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
|
||||||
break;
|
break;
|
||||||
/* all prev words didn't match, does this one match? */
|
/* all prev words didn't match, does this one match? */
|
||||||
argv = pi->progs->argv;
|
argv = pi->cmds->argv;
|
||||||
while (*argv) {
|
while (*argv) {
|
||||||
char *pattern = expand_string_to_string(*argv);
|
char *pattern = expand_string_to_string(*argv);
|
||||||
/* TODO: which FNM_xxx flags to use? */
|
/* TODO: which FNM_xxx flags to use? */
|
||||||
@ -2229,14 +2228,14 @@ static int run_list(struct pipe *pi)
|
|||||||
continue; /* not matched yet, skip this pipe */
|
continue; /* not matched yet, skip this pipe */
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (pi->num_progs == 0)
|
if (pi->num_cmds == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* After analyzing all keywords and conditions, we decided
|
/* After analyzing all keywords and conditions, we decided
|
||||||
* to execute this pipe. NB: has to do checkjobs(NULL)
|
* to execute this pipe. NB: has to do checkjobs(NULL)
|
||||||
* after run_pipe() to collect any background children,
|
* after run_pipe() to collect any background children,
|
||||||
* even if list execution is to be stopped. */
|
* even if list execution is to be stopped. */
|
||||||
debug_printf_exec(": run_pipe with %d members\n", pi->num_progs);
|
debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds);
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
@ -2349,30 +2348,30 @@ static int run_list(struct pipe *pi)
|
|||||||
static int free_pipe(struct pipe *pi, int indent)
|
static int free_pipe(struct pipe *pi, int indent)
|
||||||
{
|
{
|
||||||
char **p;
|
char **p;
|
||||||
struct child_prog *child;
|
struct command *command;
|
||||||
struct redir_struct *r, *rnext;
|
struct redir_struct *r, *rnext;
|
||||||
int a, i, ret_code = 0;
|
int a, i, ret_code = 0;
|
||||||
|
|
||||||
if (pi->stopped_progs > 0)
|
if (pi->stopped_cmds > 0)
|
||||||
return ret_code;
|
return ret_code;
|
||||||
debug_printf_clean("%s run pipe: (pid %d)\n", indenter(indent), getpid());
|
debug_printf_clean("%s run pipe: (pid %d)\n", indenter(indent), getpid());
|
||||||
for (i = 0; i < pi->num_progs; i++) {
|
for (i = 0; i < pi->num_cmds; i++) {
|
||||||
child = &pi->progs[i];
|
command = &pi->cmds[i];
|
||||||
debug_printf_clean("%s command %d:\n", indenter(indent), i);
|
debug_printf_clean("%s command %d:\n", indenter(indent), i);
|
||||||
if (child->argv) {
|
if (command->argv) {
|
||||||
for (a = 0, p = child->argv; *p; a++, p++) {
|
for (a = 0, p = command->argv; *p; a++, p++) {
|
||||||
debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p);
|
debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p);
|
||||||
}
|
}
|
||||||
free_strings(child->argv);
|
free_strings(command->argv);
|
||||||
child->argv = NULL;
|
command->argv = NULL;
|
||||||
} else if (child->group) {
|
} else if (command->group) {
|
||||||
debug_printf_clean("%s begin group (subshell:%d)\n", indenter(indent), child->subshell);
|
debug_printf_clean("%s begin group (subshell:%d)\n", indenter(indent), command->subshell);
|
||||||
ret_code = free_pipe_list(child->group, indent+3);
|
ret_code = free_pipe_list(command->group, indent+3);
|
||||||
debug_printf_clean("%s end group\n", indenter(indent));
|
debug_printf_clean("%s end group\n", indenter(indent));
|
||||||
} else {
|
} else {
|
||||||
debug_printf_clean("%s (nil)\n", indenter(indent));
|
debug_printf_clean("%s (nil)\n", indenter(indent));
|
||||||
}
|
}
|
||||||
for (r = child->redirects; r; r = rnext) {
|
for (r = command->redirects; r; r = rnext) {
|
||||||
debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->rd_type].descrip);
|
debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->rd_type].descrip);
|
||||||
if (r->dup == -1) {
|
if (r->dup == -1) {
|
||||||
/* guard against the case >$FOO, where foo is unset or blank */
|
/* guard against the case >$FOO, where foo is unset or blank */
|
||||||
@ -2387,10 +2386,10 @@ static int free_pipe(struct pipe *pi, int indent)
|
|||||||
rnext = r->next;
|
rnext = r->next;
|
||||||
free(r);
|
free(r);
|
||||||
}
|
}
|
||||||
child->redirects = NULL;
|
command->redirects = NULL;
|
||||||
}
|
}
|
||||||
free(pi->progs); /* children are an array, they get freed all at once */
|
free(pi->cmds); /* children are an array, they get freed all at once */
|
||||||
pi->progs = NULL;
|
pi->cmds = NULL;
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
free(pi->cmdtext);
|
free(pi->cmdtext);
|
||||||
pi->cmdtext = NULL;
|
pi->cmdtext = NULL;
|
||||||
@ -2422,7 +2421,7 @@ static int run_and_free_list(struct pipe *pi)
|
|||||||
int rcode = 0;
|
int rcode = 0;
|
||||||
debug_printf_exec("run_and_free_list entered\n");
|
debug_printf_exec("run_and_free_list entered\n");
|
||||||
if (!G.fake_mode) {
|
if (!G.fake_mode) {
|
||||||
debug_printf_exec(": run_list with %d members\n", pi->num_progs);
|
debug_printf_exec(": run_list with %d members\n", pi->num_cmds);
|
||||||
rcode = run_list(pi);
|
rcode = run_list(pi);
|
||||||
}
|
}
|
||||||
/* free_pipe_list has the side effect of clearing memory.
|
/* free_pipe_list has the side effect of clearing memory.
|
||||||
@ -2833,11 +2832,11 @@ static void unset_local_var(const char *name)
|
|||||||
* for file descriptor duplication, e.g., "2>&1".
|
* for file descriptor duplication, e.g., "2>&1".
|
||||||
* Return code is 0 normally, 1 if a syntax error is detected in src.
|
* Return code is 0 normally, 1 if a syntax error is detected in src.
|
||||||
* Resource errors (in xmalloc) cause the process to exit */
|
* Resource errors (in xmalloc) cause the process to exit */
|
||||||
static int setup_redirect(struct p_context *ctx, int fd, redir_type style,
|
static int setup_redirect(struct parse_context *ctx, int fd, redir_type style,
|
||||||
struct in_str *input)
|
struct in_str *input)
|
||||||
{
|
{
|
||||||
struct child_prog *child = ctx->child;
|
struct command *command = ctx->command;
|
||||||
struct redir_struct *redir = child->redirects;
|
struct redir_struct *redir = command->redirects;
|
||||||
struct redir_struct *last_redir = NULL;
|
struct redir_struct *last_redir = NULL;
|
||||||
|
|
||||||
/* Create a new redir_struct and drop it onto the end of the linked list */
|
/* Create a new redir_struct and drop it onto the end of the linked list */
|
||||||
@ -2851,7 +2850,7 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style,
|
|||||||
if (last_redir) {
|
if (last_redir) {
|
||||||
last_redir->next = redir;
|
last_redir->next = redir;
|
||||||
} else {
|
} else {
|
||||||
child->redirects = redir;
|
command->redirects = redir;
|
||||||
}
|
}
|
||||||
|
|
||||||
redir->rd_type = style;
|
redir->rd_type = style;
|
||||||
@ -2887,13 +2886,13 @@ static struct pipe *new_pipe(void)
|
|||||||
return pi;
|
return pi;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initialize_context(struct p_context *ctx)
|
static void initialize_context(struct parse_context *ctx)
|
||||||
{
|
{
|
||||||
memset(ctx, 0, sizeof(*ctx));
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
ctx->pipe = ctx->list_head = new_pipe();
|
ctx->pipe = ctx->list_head = new_pipe();
|
||||||
/* Create the memory for child, roughly:
|
/* Create the memory for command, roughly:
|
||||||
* ctx->pipe->progs = new struct child_prog;
|
* ctx->pipe->cmds = new struct command;
|
||||||
* ctx->child = &ctx->pipe->progs[0];
|
* ctx->command = &ctx->pipe->cmds[0];
|
||||||
*/
|
*/
|
||||||
done_command(ctx);
|
done_command(ctx);
|
||||||
}
|
}
|
||||||
@ -2904,7 +2903,7 @@ 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(o_string *word, struct p_context *ctx)
|
static int reserved_word(o_string *word, struct parse_context *ctx)
|
||||||
{
|
{
|
||||||
struct reserved_combo {
|
struct reserved_combo {
|
||||||
char literal[6];
|
char literal[6];
|
||||||
@ -2988,7 +2987,7 @@ static int reserved_word(o_string *word, struct p_context *ctx)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (r->flag & FLAG_START) {
|
if (r->flag & FLAG_START) {
|
||||||
struct p_context *new;
|
struct parse_context *new;
|
||||||
debug_printf("push stack\n");
|
debug_printf("push stack\n");
|
||||||
new = xmalloc(sizeof(*new));
|
new = xmalloc(sizeof(*new));
|
||||||
*new = *ctx; /* physical copy */
|
*new = *ctx; /* physical copy */
|
||||||
@ -3002,12 +3001,12 @@ static int reserved_word(o_string *word, struct p_context *ctx)
|
|||||||
ctx->ctx_res_w = r->res;
|
ctx->ctx_res_w = r->res;
|
||||||
ctx->old_flag = r->flag;
|
ctx->old_flag = r->flag;
|
||||||
if (ctx->old_flag & FLAG_END) {
|
if (ctx->old_flag & FLAG_END) {
|
||||||
struct p_context *old;
|
struct parse_context *old;
|
||||||
debug_printf("pop stack\n");
|
debug_printf("pop stack\n");
|
||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
old = ctx->stack;
|
old = ctx->stack;
|
||||||
old->child->group = ctx->list_head;
|
old->command->group = ctx->list_head;
|
||||||
old->child->subshell = 0;
|
old->command->subshell = 0;
|
||||||
*ctx = *old; /* physical copy */
|
*ctx = *old; /* physical copy */
|
||||||
free(old);
|
free(old);
|
||||||
}
|
}
|
||||||
@ -3020,11 +3019,11 @@ static int reserved_word(o_string *word, struct p_context *ctx)
|
|||||||
|
|
||||||
/* Word is complete, look at it and update parsing context.
|
/* Word is complete, look at it and update parsing context.
|
||||||
* Normal return is 0. Syntax errors return 1. */
|
* Normal return is 0. Syntax errors return 1. */
|
||||||
static int done_word(o_string *word, struct p_context *ctx)
|
static int done_word(o_string *word, struct parse_context *ctx)
|
||||||
{
|
{
|
||||||
struct child_prog *child = ctx->child;
|
struct command *command = ctx->command;
|
||||||
|
|
||||||
debug_printf_parse("done_word entered: '%s' %p\n", word->data, child);
|
debug_printf_parse("done_word entered: '%s' %p\n", word->data, command);
|
||||||
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;
|
||||||
@ -3037,7 +3036,7 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
word->o_assignment = NOT_ASSIGNMENT;
|
word->o_assignment = NOT_ASSIGNMENT;
|
||||||
} else {
|
} else {
|
||||||
if (word->o_assignment == DEFINITELY_ASSIGNMENT)
|
if (word->o_assignment == DEFINITELY_ASSIGNMENT)
|
||||||
child->assignment_cnt++;
|
command->assignment_cnt++;
|
||||||
word->o_assignment = MAYBE_ASSIGNMENT;
|
word->o_assignment = MAYBE_ASSIGNMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3050,7 +3049,7 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
} else {
|
} else {
|
||||||
/* "{ echo foo; } echo bar" - bad */
|
/* "{ echo foo; } echo bar" - bad */
|
||||||
/* NB: bash allows e.g. "if true; then { echo foo; } fi". TODO? */
|
/* NB: bash allows e.g. "if true; then { echo foo; } fi". TODO? */
|
||||||
if (child->group) {
|
if (command->group) {
|
||||||
syntax(NULL);
|
syntax(NULL);
|
||||||
debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n");
|
debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n");
|
||||||
return 1;
|
return 1;
|
||||||
@ -3066,7 +3065,7 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!child->argv /* if it's the first word... */
|
if (!command->argv /* if it's the first word... */
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
&& ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
|
&& ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
|
||||||
&& ctx->ctx_res_w != RES_IN
|
&& ctx->ctx_res_w != RES_IN
|
||||||
@ -3102,8 +3101,8 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
o_addchr(word, SPECIAL_VAR_SYMBOL);
|
o_addchr(word, SPECIAL_VAR_SYMBOL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
child->argv = add_malloced_string_to_strings(child->argv, xstrdup(word->data));
|
command->argv = add_malloced_string_to_strings(command->argv, xstrdup(word->data));
|
||||||
debug_print_strings("word appended to argv", child->argv);
|
debug_print_strings("word appended to argv", command->argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
o_reset(word);
|
o_reset(word);
|
||||||
@ -3115,7 +3114,7 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
* as it is "for v; in ...". FOR and IN become two pipe structs
|
* as it is "for v; in ...". FOR and IN become two pipe structs
|
||||||
* in parse tree. */
|
* in parse tree. */
|
||||||
if (ctx->ctx_res_w == RES_FOR) {
|
if (ctx->ctx_res_w == RES_FOR) {
|
||||||
//TODO: check that child->argv[0] is a valid variable name!
|
//TODO: check that command->argv[0] is a valid variable name!
|
||||||
done_pipe(ctx, PIPE_SEQ);
|
done_pipe(ctx, PIPE_SEQ);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -3131,40 +3130,40 @@ static int done_word(o_string *word, struct p_context *ctx)
|
|||||||
|
|
||||||
/* Command (member of a pipe) is complete. The only possible error here
|
/* Command (member of a pipe) is complete. The only possible error here
|
||||||
* is out of memory, in which case xmalloc exits. */
|
* is out of memory, in which case xmalloc exits. */
|
||||||
static int done_command(struct p_context *ctx)
|
static int done_command(struct parse_context *ctx)
|
||||||
{
|
{
|
||||||
/* The child is really already in the pipe structure, so
|
/* The command is really already in the pipe structure, so
|
||||||
* advance the pipe counter and make a new, null child. */
|
* advance the pipe counter and make a new, null command. */
|
||||||
struct pipe *pi = ctx->pipe;
|
struct pipe *pi = ctx->pipe;
|
||||||
struct child_prog *child = ctx->child;
|
struct command *command = ctx->command;
|
||||||
|
|
||||||
if (child) {
|
if (command) {
|
||||||
if (child->group == NULL
|
if (command->group == NULL
|
||||||
&& child->argv == NULL
|
&& command->argv == NULL
|
||||||
&& child->redirects == NULL
|
&& command->redirects == NULL
|
||||||
) {
|
) {
|
||||||
debug_printf_parse("done_command: skipping null cmd, num_progs=%d\n", pi->num_progs);
|
debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
|
||||||
return pi->num_progs;
|
return pi->num_cmds;
|
||||||
}
|
}
|
||||||
pi->num_progs++;
|
pi->num_cmds++;
|
||||||
debug_printf_parse("done_command: ++num_progs=%d\n", pi->num_progs);
|
debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
|
||||||
} else {
|
} else {
|
||||||
debug_printf_parse("done_command: initializing, num_progs=%d\n", pi->num_progs);
|
debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only real trickiness here is that the uncommitted
|
/* Only real trickiness here is that the uncommitted
|
||||||
* child structure is not counted in pi->num_progs. */
|
* command structure is not counted in pi->num_cmds. */
|
||||||
pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
|
pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
|
||||||
child = &pi->progs[pi->num_progs];
|
command = &pi->cmds[pi->num_cmds];
|
||||||
memset(child, 0, sizeof(*child));
|
memset(command, 0, sizeof(*command));
|
||||||
|
|
||||||
ctx->child = child;
|
ctx->command = command;
|
||||||
/* but ctx->pipe and ctx->list_head remain unchanged */
|
/* but ctx->pipe and ctx->list_head remain unchanged */
|
||||||
|
|
||||||
return pi->num_progs; /* used only for 0/nonzero check */
|
return pi->num_cmds; /* used only for 0/nonzero check */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void done_pipe(struct p_context *ctx, pipe_style type)
|
static void done_pipe(struct parse_context *ctx, pipe_style type)
|
||||||
{
|
{
|
||||||
int not_null;
|
int not_null;
|
||||||
|
|
||||||
@ -3189,7 +3188,7 @@ static void done_pipe(struct p_context *ctx, pipe_style type)
|
|||||||
new_p = new_pipe();
|
new_p = new_pipe();
|
||||||
ctx->pipe->next = new_p;
|
ctx->pipe->next = new_p;
|
||||||
ctx->pipe = new_p;
|
ctx->pipe = new_p;
|
||||||
ctx->child = NULL; /* needed! */
|
ctx->command = NULL; /* needed! */
|
||||||
/* RES_THEN, RES_DO etc are "sticky" -
|
/* RES_THEN, RES_DO etc are "sticky" -
|
||||||
* they remain set for commands inside if/while.
|
* they remain set for commands inside if/while.
|
||||||
* This is used to control execution.
|
* This is used to control execution.
|
||||||
@ -3205,9 +3204,9 @@ static void done_pipe(struct p_context *ctx, pipe_style type)
|
|||||||
if (ctx->ctx_res_w == RES_MATCH)
|
if (ctx->ctx_res_w == RES_MATCH)
|
||||||
ctx->ctx_res_w = RES_CASEI;
|
ctx->ctx_res_w = RES_CASEI;
|
||||||
#endif
|
#endif
|
||||||
/* Create the memory for child, roughly:
|
/* Create the memory for command, roughly:
|
||||||
* ctx->pipe->progs = new struct child_prog;
|
* ctx->pipe->cmds = new struct command;
|
||||||
* ctx->child = &ctx->pipe->progs[0];
|
* ctx->command = &ctx->pipe->cmds[0];
|
||||||
*/
|
*/
|
||||||
done_command(ctx);
|
done_command(ctx);
|
||||||
}
|
}
|
||||||
@ -3320,7 +3319,7 @@ static int process_command_subs(o_string *dest,
|
|||||||
{
|
{
|
||||||
int retcode, ch, eol_cnt;
|
int retcode, ch, eol_cnt;
|
||||||
o_string result = NULL_O_STRING;
|
o_string result = NULL_O_STRING;
|
||||||
struct p_context inner;
|
struct parse_context inner;
|
||||||
FILE *p;
|
FILE *p;
|
||||||
struct in_str pipe_str;
|
struct in_str pipe_str;
|
||||||
|
|
||||||
@ -3367,7 +3366,7 @@ static int process_command_subs(o_string *dest,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int parse_group(o_string *dest, struct p_context *ctx,
|
static int parse_group(o_string *dest, struct parse_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,
|
/* NB: parse_group may create and use its own o_string,
|
||||||
@ -3375,11 +3374,14 @@ static int parse_group(o_string *dest, struct p_context *ctx,
|
|||||||
* if we (ab)use caller's one. */
|
* if we (ab)use caller's one. */
|
||||||
int rcode;
|
int rcode;
|
||||||
const char *endch = NULL;
|
const char *endch = NULL;
|
||||||
struct p_context sub;
|
struct parse_context sub;
|
||||||
struct child_prog *child = ctx->child;
|
struct command *command = ctx->command;
|
||||||
|
|
||||||
debug_printf_parse("parse_group entered\n");
|
debug_printf_parse("parse_group entered\n");
|
||||||
if (child->argv) {
|
if (command->argv /* word [word](... */
|
||||||
|
|| dest->length /* word(... */
|
||||||
|
|| dest->nonnull /* ""(... */
|
||||||
|
) {
|
||||||
syntax(NULL);
|
syntax(NULL);
|
||||||
debug_printf_parse("parse_group return 1: syntax error, groups and arglists don't mix\n");
|
debug_printf_parse("parse_group return 1: syntax error, groups and arglists don't mix\n");
|
||||||
return 1;
|
return 1;
|
||||||
@ -3388,17 +3390,17 @@ static int parse_group(o_string *dest, struct p_context *ctx,
|
|||||||
endch = "}";
|
endch = "}";
|
||||||
if (ch == '(') {
|
if (ch == '(') {
|
||||||
endch = ")";
|
endch = ")";
|
||||||
child->subshell = 1;
|
command->subshell = 1;
|
||||||
}
|
}
|
||||||
rcode = parse_stream(dest, &sub, input, endch);
|
rcode = parse_stream(dest, &sub, input, endch);
|
||||||
if (rcode == 0) {
|
if (rcode == 0) {
|
||||||
done_word(dest, &sub); /* finish off the final word in the subcontext */
|
done_word(dest, &sub); /* finish off the final word in the subcontext */
|
||||||
done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */
|
done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */
|
||||||
child->group = sub.list_head;
|
command->group = sub.list_head;
|
||||||
}
|
}
|
||||||
debug_printf_parse("parse_group return %d\n", rcode);
|
debug_printf_parse("parse_group return %d\n", rcode);
|
||||||
return rcode;
|
return rcode;
|
||||||
/* child remains "open", available for possible redirects */
|
/* command remains "open", available for possible redirects */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Basically useful version until someone wants to get fancier,
|
/* Basically useful version until someone wants to get fancier,
|
||||||
@ -3617,7 +3619,7 @@ static int handle_dollar(o_string *dest, struct in_str *input)
|
|||||||
* Return code is 0 if end_trigger char is met,
|
* Return code is 0 if end_trigger char is met,
|
||||||
* -1 on EOF (but if end_trigger == NULL then return 0),
|
* -1 on EOF (but if end_trigger == NULL then return 0),
|
||||||
* 1 for syntax error */
|
* 1 for syntax error */
|
||||||
static int parse_stream(o_string *dest, struct p_context *ctx,
|
static int parse_stream(o_string *dest, struct parse_context *ctx,
|
||||||
struct in_str *input, const char *end_trigger)
|
struct in_str *input, const char *end_trigger)
|
||||||
{
|
{
|
||||||
int ch, m;
|
int ch, m;
|
||||||
@ -3883,8 +3885,10 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
case '(':
|
case '(':
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
/* "case... in [(]word)..." - skip '(' */
|
/* "case... in [(]word)..." - skip '(' */
|
||||||
if (dest->length == 0 // && argv[0] == NULL
|
if (dest->length == 0 /* not word(... */
|
||||||
|
&& dest->nonnull == 0 /* not ""(... */
|
||||||
&& ctx->ctx_res_w == RES_MATCH
|
&& ctx->ctx_res_w == RES_MATCH
|
||||||
|
&& ctx->command->argv == NULL /* not (word|(... */
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -3947,7 +3951,7 @@ static void update_charmap(void)
|
|||||||
* from builtin_source() and builtin_eval() */
|
* from builtin_source() and builtin_eval() */
|
||||||
static int parse_and_run_stream(struct in_str *inp, int parse_flag)
|
static int parse_and_run_stream(struct in_str *inp, int parse_flag)
|
||||||
{
|
{
|
||||||
struct p_context ctx;
|
struct parse_context ctx;
|
||||||
o_string temp = NULL_O_STRING;
|
o_string temp = NULL_O_STRING;
|
||||||
int rcode;
|
int rcode;
|
||||||
|
|
||||||
@ -4408,12 +4412,12 @@ static int builtin_fg_bg(char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Restart the processes in the job */
|
/* Restart the processes in the job */
|
||||||
debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_progs, pi->pgrp);
|
debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp);
|
||||||
for (i = 0; i < pi->num_progs; i++) {
|
for (i = 0; i < pi->num_cmds; i++) {
|
||||||
debug_printf_jobs("reviving pid %d\n", pi->progs[i].pid);
|
debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid);
|
||||||
pi->progs[i].is_stopped = 0;
|
pi->cmds[i].is_stopped = 0;
|
||||||
}
|
}
|
||||||
pi->stopped_progs = 0;
|
pi->stopped_cmds = 0;
|
||||||
|
|
||||||
i = kill(- pi->pgrp, SIGCONT);
|
i = kill(- pi->pgrp, SIGCONT);
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
@ -4455,7 +4459,7 @@ static int builtin_jobs(char **argv UNUSED_PARAM)
|
|||||||
const char *status_string;
|
const char *status_string;
|
||||||
|
|
||||||
for (job = G.job_list; job; job = job->next) {
|
for (job = G.job_list; job; job = job->next) {
|
||||||
if (job->alive_progs == job->stopped_progs)
|
if (job->alive_cmds == job->stopped_cmds)
|
||||||
status_string = "Stopped";
|
status_string = "Stopped";
|
||||||
else
|
else
|
||||||
status_string = "Running";
|
status_string = "Running";
|
||||||
|
Loading…
Reference in New Issue
Block a user