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:
Denis Vlasenko 2008-10-09 12:54:58 +00:00
parent 578de8644c
commit 9af22c7626

View File

@ -45,12 +45,11 @@
* aliases
* Arithmetic Expansion
* <(list) and >(list) Process Substitution
* reserved words: case, esac, select, function
* reserved words: select, function
* Here Documents ( << word )
* Functions
* Major bugs:
* job handling woefully incomplete and buggy (improved --vda)
* reserved word execution woefully incomplete and buggy
* to-do:
* port selected bugfixes from post-0.49 busybox lash - done?
* change { and } from special chars to reserved words
@ -291,23 +290,6 @@ typedef enum reserved_style {
RES_SNTX
} 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 *next;
char *rd_filename; /* filename */
@ -316,14 +298,14 @@ struct redir_struct {
smallint /*enum redir_type*/ rd_type;
};
struct child_prog {
struct command {
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 command currently running? */
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; /* command name and arguments */
struct redir_struct *redirects; /* I/O redirections */
};
/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
@ -335,20 +317,37 @@ struct child_prog {
struct pipe {
struct pipe *next;
int num_progs; /* total number of programs in job */
int alive_progs; /* number of programs running (not exited) */
int stopped_progs; /* number of programs alive, but stopped */
int num_cmds; /* total number of commands in job */
int alive_cmds; /* number of commands running (not exited) */
int stopped_cmds; /* number of commands alive, but stopped */
#if ENABLE_HUSH_JOB
int jobid; /* job number */
pid_t pgrp; /* process group ID for the job */
char *cmdtext; /* name of job */
#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 */
IF_HAS_KEYWORDS(smallint pi_inverted;) /* "! cmd | cmd" */
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.
* putenv adds new pointers into it, unsetenv removes them.
* 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(struct pipe *pi, int indent);
/* 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);
#if BB_MMU
#define pseudo_exec_argv(ptrs2free, argv, assignment_cnt, argv_expanded) \
pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
#define pseudo_exec(ptrs2free, child, argv_expanded) \
pseudo_exec(child, argv_expanded)
#define pseudo_exec(ptrs2free, command, argv_expanded) \
pseudo_exec(command, argv_expanded)
#endif
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);
/* data structure manipulation: */
static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
static void initialize_context(struct p_context *ctx);
static int done_word(o_string *dest, struct p_context *ctx);
static int done_command(struct p_context *ctx);
static void done_pipe(struct p_context *ctx, pipe_style type);
static int setup_redirect(struct parse_context *ctx, int fd, redir_type style, struct in_str *input);
static void initialize_context(struct parse_context *ctx);
static int done_word(o_string *dest, struct parse_context *ctx);
static int done_command(struct parse_context *ctx);
static void done_pipe(struct parse_context *ctx, pipe_style type);
/* primary string parsing: */
static int redirect_dup_num(struct in_str *input);
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,
struct in_str *input, const char *subst_end);
#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 int handle_dollar(o_string *dest,
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: */
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);
@ -837,7 +836,7 @@ static void handler_ctrl_z(int sig UNUSED_PARAM)
/* parent */
/* finish filling up pipe info */
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.
* 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
@ -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,
* 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;
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()
*/
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)
pseudo_exec_argv(ptrs2free, child->argv, child->assignment_cnt, argv_expanded);
if (command->argv)
pseudo_exec_argv(ptrs2free, command->argv, command->assignment_cnt, argv_expanded);
if (child->group) {
if (command->group) {
#if !BB_MMU
bb_error_msg_and_die("nested lists are not supported on NOMMU");
#else
int rcode;
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,
* since this process is about to exit */
_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 */
if (pi->cmdtext)
return pi->cmdtext;
argv = pi->progs[0].argv;
argv = pi->cmds[0].argv;
if (!argv || !argv[0]) {
pi->cmdtext = xzalloc(1);
return pi->cmdtext;
@ -1511,7 +1510,7 @@ static const char *get_cmdtext(struct pipe *pi)
len = 0;
do len += strlen(*argv) + 1; while (*++argv);
pi->cmdtext = p = xmalloc(len);
argv = pi->progs[0].argv;
argv = pi->cmds[0].argv;
do {
len = strlen(*argv);
memcpy(p, *argv, len);
@ -1545,12 +1544,12 @@ static void insert_bg_job(struct pipe *pi)
/* Physically copy the struct job */
memcpy(thejob, pi, sizeof(struct pipe));
thejob->progs = xzalloc(sizeof(pi->progs[0]) * pi->num_progs);
/* We cannot copy entire pi->progs[] vector! Double free()s will happen */
for (i = 0; i < pi->num_progs; i++) {
thejob->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
/* We cannot copy entire pi->cmds[] vector! Double free()s will happen */
for (i = 0; i < pi->num_cmds; i++) {
// TODO: do we really need to have so many fields which are just dead weight
// 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 */
}
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
to the list of backgrounded thejobs and leave it alone */
printf("[%d] %d %s\n", thejob->jobid, thejob->progs[0].pid, thejob->cmdtext);
G.last_bg_pid = thejob->progs[0].pid;
printf("[%d] %d %s\n", thejob->jobid, thejob->cmds[0].pid, thejob->cmdtext);
G.last_bg_pid = thejob->cmds[0].pid;
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)
{
remove_bg_job(pi);
pi->stopped_progs = 0;
pi->stopped_cmds = 0;
free_pipe(pi, 0);
free(pi);
}
@ -1637,29 +1636,29 @@ static int checkjobs(struct pipe* fg_pipe)
#endif
/* Were we asked to wait for fg pipe? */
if (fg_pipe) {
for (i = 0; i < fg_pipe->num_progs; i++) {
debug_printf_jobs("check pid %d\n", fg_pipe->progs[i].pid);
if (fg_pipe->progs[i].pid != childpid)
for (i = 0; i < fg_pipe->num_cmds; i++) {
debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
if (fg_pipe->cmds[i].pid != childpid)
continue;
/* printf("process %d exit %d\n", i, WEXITSTATUS(status)); */
if (dead) {
fg_pipe->progs[i].pid = 0;
fg_pipe->alive_progs--;
if (i == fg_pipe->num_progs - 1) {
fg_pipe->cmds[i].pid = 0;
fg_pipe->alive_cmds--;
if (i == fg_pipe->num_cmds - 1) {
/* last process gives overall exitstatus */
rcode = WEXITSTATUS(status);
IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
}
} else {
fg_pipe->progs[i].is_stopped = 1;
fg_pipe->stopped_progs++;
fg_pipe->cmds[i].is_stopped = 1;
fg_pipe->stopped_cmds++;
}
debug_printf_jobs("fg_pipe: alive_progs %d stopped_progs %d\n",
fg_pipe->alive_progs, fg_pipe->stopped_progs);
if (fg_pipe->alive_progs - fg_pipe->stopped_progs <= 0) {
debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
if (fg_pipe->alive_cmds - fg_pipe->stopped_cmds <= 0) {
/* All processes in fg pipe have exited/stopped */
#if ENABLE_HUSH_JOB
if (fg_pipe->alive_progs)
if (fg_pipe->alive_cmds)
insert_bg_job(fg_pipe);
#endif
return rcode;
@ -1674,8 +1673,8 @@ static int checkjobs(struct pipe* fg_pipe)
/* We asked to wait for bg or orphaned children */
/* No need to remember exitcode in this case */
for (pi = G.job_list; pi; pi = pi->next) {
for (i = 0; i < pi->num_progs; i++) {
if (pi->progs[i].pid == childpid)
for (i = 0; i < pi->num_cmds; i++) {
if (pi->cmds[i].pid == childpid)
goto found_pi_and_prognum;
}
}
@ -1686,17 +1685,17 @@ static int checkjobs(struct pipe* fg_pipe)
found_pi_and_prognum:
if (dead) {
/* child exited */
pi->progs[i].pid = 0;
pi->alive_progs--;
if (!pi->alive_progs) {
pi->cmds[i].pid = 0;
pi->alive_cmds--;
if (!pi->alive_cmds) {
printf(JOB_STATUS_FORMAT, pi->jobid,
"Done", pi->cmdtext);
delete_finished_bg_job(pi);
}
} else {
/* child stopped */
pi->progs[i].is_stopped = 1;
pi->stopped_progs++;
pi->cmds[i].is_stopped = 1;
pi->stopped_cmds++;
}
#endif
} /* while (waitpid succeeds)... */
@ -1745,7 +1744,7 @@ static int run_pipe(struct pipe *pi)
int i;
int nextin;
int pipefds[2]; /* pipefds[0] is for reading */
struct child_prog *child;
struct command *command;
char **argv_expanded = NULL;
char **argv;
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 */
int squirrel[] = { -1, -1, -1 };
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);
#if ENABLE_HUSH_JOB
pi->pgrp = -1;
#endif
pi->alive_progs = 1;
pi->stopped_progs = 0;
pi->alive_cmds = 1;
pi->stopped_cmds = 0;
/* Check if this is a simple builtin (not part of a pipe).
* Builtins within pipes have to fork anyway, and are handled in
* pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
*/
child = &(pi->progs[0]);
if (single_and_fg && child->group && child->subshell == 0) {
command = &(pi->cmds[0]);
if (single_and_fg && command->group && command->subshell == 0) {
debug_printf("non-subshell grouping\n");
setup_redirects(child, squirrel);
setup_redirects(command, squirrel);
debug_printf_exec(": run_list\n");
rcode = run_list(child->group) & 0xff;
rcode = run_list(command->group) & 0xff;
restore_redirects(squirrel);
debug_printf_exec("run_pipe return %d\n", rcode);
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
return rcode;
}
argv = child->argv;
argv = command->argv;
if (single_and_fg && argv != NULL) {
i = child->assignment_cnt;
i = command->assignment_cnt;
if (i != 0 && argv[i] == NULL) {
/* assignments, but no command: set local environment */
for (i = 0; argv[i] != NULL; i++) {
@ -1794,7 +1793,7 @@ static int run_pipe(struct pipe *pi)
}
/* 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]);
putenv(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 (x->function == builtin_exec && argv_expanded[1] == NULL) {
debug_printf("magic exec\n");
setup_redirects(child, NULL);
setup_redirects(command, NULL);
return EXIT_SUCCESS;
}
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().
* Is it really safe for inline use? Experimentally,
* 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]);
rcode = x->function(argv_expanded) & 0xff;
free(argv_expanded);
@ -1829,7 +1828,7 @@ static int run_pipe(struct pipe *pi)
{
int a = find_applet_by_name(argv_expanded[0]);
if (a >= 0 && APPLET_IS_NOFORK(a)) {
setup_redirects(child, squirrel);
setup_redirects(command, squirrel);
save_nofork_data(&G.nofork_save);
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);
@ -1852,18 +1851,18 @@ static int run_pipe(struct pipe *pi)
set_jobctrl_sighandler(SIG_IGN);
/* Going to fork a child per each pipe member */
pi->alive_progs = 0;
pi->alive_cmds = 0;
nextin = 0;
for (i = 0; i < pi->num_progs; i++) {
for (i = 0; i < pi->num_cmds; i++) {
#if !BB_MMU
char **ptrs2free = NULL;
#endif
child = &(pi->progs[i]);
if (child->argv) {
debug_printf_exec(": pipe member '%s' '%s'...\n", child->argv[0], child->argv[1]);
command = &(pi->cmds[i]);
if (command->argv) {
debug_printf_exec(": pipe member '%s' '%s'...\n", command->argv[0], command->argv[1]);
#if !BB_MMU
ptrs2free = alloc_ptrs(child->argv);
ptrs2free = alloc_ptrs(command->argv);
#endif
} else
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 */
pipefds[0] = 0;
pipefds[1] = 1;
if ((i + 1) < pi->num_progs)
if ((i + 1) < pi->num_cmds)
xpipe(pipefds);
child->pid = BB_MMU ? fork() : vfork();
if (!child->pid) { /* child */
command->pid = BB_MMU ? fork() : vfork();
if (!command->pid) { /* child */
if (ENABLE_HUSH_JOB)
die_sleep = 0; /* let nofork's xfuncs die */
#if ENABLE_HUSH_JOB
@ -1901,45 +1900,45 @@ static int run_pipe(struct pipe *pi)
close(pipefds[0]); /* read end */
/* Like bash, explicit redirects override pipes,
* and the pipe fd is available for dup'ing. */
setup_redirects(child, NULL);
setup_redirects(command, NULL);
/* Restore default handlers just prior to exec */
set_jobctrl_sighandler(SIG_DFL);
set_misc_sighandler(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);
argv_expanded = NULL;
#if !BB_MMU
free_strings(ptrs2free);
#endif
if (child->pid < 0) { /* [v]fork failed */
if (command->pid < 0) { /* [v]fork failed */
/* Clearly indicate, was it fork or vfork */
bb_perror_msg(BB_MMU ? "fork" : "vfork");
} else {
pi->alive_progs++;
pi->alive_cmds++;
#if ENABLE_HUSH_JOB
/* Second and next children need to know pid of first one */
if (pi->pgrp < 0)
pi->pgrp = child->pid;
pi->pgrp = command->pid;
#endif
}
if (i)
close(nextin);
if ((i + 1) < pi->num_progs)
if ((i + 1) < pi->num_cmds)
close(pipefds[1]); /* write end */
/* Pass read (output) pipe end to next iteration */
nextin = pipefds[0];
}
if (!pi->alive_progs) {
if (!pi->alive_cmds) {
debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
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;
}
@ -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, "",
pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]);
prn = 0;
while (prn < pi->num_progs) {
struct child_prog *child = &pi->progs[prn];
char **argv = child->argv;
while (prn < pi->num_cmds) {
struct command *command = &pi->cmds[prn];
char **argv = command->argv;
fprintf(stderr, "%*s prog %d assignment_cnt:%d", lvl*2, "", prn, child->assignment_cnt);
if (child->group) {
fprintf(stderr, "%*s prog %d assignment_cnt:%d", lvl*2, "", prn, command->assignment_cnt);
if (command->group) {
fprintf(stderr, " group %s: (argv=%p)\n",
(child->subshell ? "()" : "{}"),
(command->subshell ? "()" : "{}"),
argv);
debug_print_tree(child->group, lvl+1);
debug_print_tree(command->group, lvl+1);
prn++;
continue;
}
@ -2151,7 +2150,7 @@ static int run_list(struct pipe *pi)
}
#endif
#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) {
/* first loop through for */
@ -2166,31 +2165,31 @@ static int run_list(struct pipe *pi)
vals = (char**)encoded_dollar_at_argv;
if (pi->next->res_word == RES_IN) {
/* if no variable values after "in" we skip "for" */
if (!pi->next->progs[0].argv)
if (!pi->next->cmds[0].argv)
break;
vals = pi->next->progs[0].argv;
vals = pi->next->cmds[0].argv;
} /* else: "for var; do..." -> assume "$@" list */
/* create list of variable values */
debug_print_strings("for_list made from", vals);
for_list = expand_strvec_to_strvec(vals);
for_lcur = for_list;
debug_print_strings("for_list", for_list);
for_varname = pi->progs[0].argv[0];
pi->progs[0].argv[0] = NULL;
for_varname = pi->cmds[0].argv[0];
pi->cmds[0].argv[0] = NULL;
}
free(pi->progs[0].argv[0]);
free(pi->cmds[0].argv[0]);
if (!*for_lcur) {
/* "for" loop is over, clean up */
free(for_list);
for_list = NULL;
for_lcur = NULL;
pi->progs[0].argv[0] = for_varname;
pi->cmds[0].argv[0] = for_varname;
break;
}
/* insert next value from for_lcur */
//TODO: does it need escaping?
pi->progs[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
pi->progs[0].assignment_cnt = 1;
pi->cmds[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
pi->cmds[0].assignment_cnt = 1;
}
if (rword == RES_IN) /* "for v IN list;..." - "in" has no cmds anyway */
continue;
@ -2200,7 +2199,7 @@ static int run_list(struct pipe *pi)
#endif
#if ENABLE_HUSH_CASE
if (rword == RES_CASE) {
case_word = expand_strvec_to_string(pi->progs->argv);
case_word = expand_strvec_to_string(pi->cmds->argv);
continue;
}
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 */
break;
/* all prev words didn't match, does this one match? */
argv = pi->progs->argv;
argv = pi->cmds->argv;
while (*argv) {
char *pattern = expand_string_to_string(*argv);
/* 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 */
}
#endif
if (pi->num_progs == 0)
if (pi->num_cmds == 0)
continue;
/* After analyzing all keywords and conditions, we decided
* to execute this pipe. NB: has to do checkjobs(NULL)
* after run_pipe() to collect any background children,
* 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;
#if ENABLE_HUSH_LOOPS
@ -2349,30 +2348,30 @@ static int run_list(struct pipe *pi)
static int free_pipe(struct pipe *pi, int indent)
{
char **p;
struct child_prog *child;
struct command *command;
struct redir_struct *r, *rnext;
int a, i, ret_code = 0;
if (pi->stopped_progs > 0)
if (pi->stopped_cmds > 0)
return ret_code;
debug_printf_clean("%s run pipe: (pid %d)\n", indenter(indent), getpid());
for (i = 0; i < pi->num_progs; i++) {
child = &pi->progs[i];
for (i = 0; i < pi->num_cmds; i++) {
command = &pi->cmds[i];
debug_printf_clean("%s command %d:\n", indenter(indent), i);
if (child->argv) {
for (a = 0, p = child->argv; *p; a++, p++) {
if (command->argv) {
for (a = 0, p = command->argv; *p; a++, p++) {
debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p);
}
free_strings(child->argv);
child->argv = NULL;
} else if (child->group) {
debug_printf_clean("%s begin group (subshell:%d)\n", indenter(indent), child->subshell);
ret_code = free_pipe_list(child->group, indent+3);
free_strings(command->argv);
command->argv = NULL;
} else if (command->group) {
debug_printf_clean("%s begin group (subshell:%d)\n", indenter(indent), command->subshell);
ret_code = free_pipe_list(command->group, indent+3);
debug_printf_clean("%s end group\n", indenter(indent));
} else {
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);
if (r->dup == -1) {
/* 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;
free(r);
}
child->redirects = NULL;
command->redirects = NULL;
}
free(pi->progs); /* children are an array, they get freed all at once */
pi->progs = NULL;
free(pi->cmds); /* children are an array, they get freed all at once */
pi->cmds = NULL;
#if ENABLE_HUSH_JOB
free(pi->cmdtext);
pi->cmdtext = NULL;
@ -2422,7 +2421,7 @@ static int run_and_free_list(struct pipe *pi)
int rcode = 0;
debug_printf_exec("run_and_free_list entered\n");
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);
}
/* 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".
* Return code is 0 normally, 1 if a syntax error is detected in src.
* 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 child_prog *child = ctx->child;
struct redir_struct *redir = child->redirects;
struct command *command = ctx->command;
struct redir_struct *redir = command->redirects;
struct redir_struct *last_redir = NULL;
/* 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) {
last_redir->next = redir;
} else {
child->redirects = redir;
command->redirects = redir;
}
redir->rd_type = style;
@ -2887,13 +2886,13 @@ static struct pipe *new_pipe(void)
return pi;
}
static void initialize_context(struct p_context *ctx)
static void initialize_context(struct parse_context *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->pipe = ctx->list_head = new_pipe();
/* Create the memory for child, roughly:
* ctx->pipe->progs = new struct child_prog;
* ctx->child = &ctx->pipe->progs[0];
/* Create the memory for command, roughly:
* ctx->pipe->cmds = new struct command;
* ctx->command = &ctx->pipe->cmds[0];
*/
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.
*/
#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 {
char literal[6];
@ -2988,7 +2987,7 @@ static int reserved_word(o_string *word, struct p_context *ctx)
return 1;
}
if (r->flag & FLAG_START) {
struct p_context *new;
struct parse_context *new;
debug_printf("push stack\n");
new = xmalloc(sizeof(*new));
*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->old_flag = r->flag;
if (ctx->old_flag & FLAG_END) {
struct p_context *old;
struct parse_context *old;
debug_printf("pop stack\n");
done_pipe(ctx, PIPE_SEQ);
old = ctx->stack;
old->child->group = ctx->list_head;
old->child->subshell = 0;
old->command->group = ctx->list_head;
old->command->subshell = 0;
*ctx = *old; /* physical copy */
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.
* 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) {
debug_printf_parse("done_word return 0: true null, ignored\n");
return 0;
@ -3037,7 +3036,7 @@ static int done_word(o_string *word, struct p_context *ctx)
word->o_assignment = NOT_ASSIGNMENT;
} else {
if (word->o_assignment == DEFINITELY_ASSIGNMENT)
child->assignment_cnt++;
command->assignment_cnt++;
word->o_assignment = MAYBE_ASSIGNMENT;
}
@ -3050,7 +3049,7 @@ static int done_word(o_string *word, struct p_context *ctx)
} else {
/* "{ echo foo; } echo bar" - bad */
/* NB: bash allows e.g. "if true; then { echo foo; } fi". TODO? */
if (child->group) {
if (command->group) {
syntax(NULL);
debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n");
return 1;
@ -3066,7 +3065,7 @@ static int done_word(o_string *word, struct p_context *ctx)
} else
#endif
if (!child->argv /* if it's the first word... */
if (!command->argv /* if it's the first word... */
#if ENABLE_HUSH_LOOPS
&& ctx->ctx_res_w != RES_FOR /* ...not after FOR or 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);
}
}
child->argv = add_malloced_string_to_strings(child->argv, xstrdup(word->data));
debug_print_strings("word appended to argv", child->argv);
command->argv = add_malloced_string_to_strings(command->argv, xstrdup(word->data));
debug_print_strings("word appended to argv", command->argv);
}
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
* in parse tree. */
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);
}
#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
* 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
* advance the pipe counter and make a new, null child. */
/* The command is really already in the pipe structure, so
* advance the pipe counter and make a new, null command. */
struct pipe *pi = ctx->pipe;
struct child_prog *child = ctx->child;
struct command *command = ctx->command;
if (child) {
if (child->group == NULL
&& child->argv == NULL
&& child->redirects == NULL
if (command) {
if (command->group == NULL
&& command->argv == NULL
&& command->redirects == NULL
) {
debug_printf_parse("done_command: skipping null cmd, num_progs=%d\n", pi->num_progs);
return pi->num_progs;
debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
return pi->num_cmds;
}
pi->num_progs++;
debug_printf_parse("done_command: ++num_progs=%d\n", pi->num_progs);
pi->num_cmds++;
debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
} 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
* child structure is not counted in pi->num_progs. */
pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
child = &pi->progs[pi->num_progs];
memset(child, 0, sizeof(*child));
* command structure is not counted in pi->num_cmds. */
pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
command = &pi->cmds[pi->num_cmds];
memset(command, 0, sizeof(*command));
ctx->child = child;
ctx->command = command;
/* 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;
@ -3189,7 +3188,7 @@ static void done_pipe(struct p_context *ctx, pipe_style type)
new_p = new_pipe();
ctx->pipe->next = new_p;
ctx->pipe = new_p;
ctx->child = NULL; /* needed! */
ctx->command = NULL; /* needed! */
/* RES_THEN, RES_DO etc are "sticky" -
* they remain set for commands inside if/while.
* 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)
ctx->ctx_res_w = RES_CASEI;
#endif
/* Create the memory for child, roughly:
* ctx->pipe->progs = new struct child_prog;
* ctx->child = &ctx->pipe->progs[0];
/* Create the memory for command, roughly:
* ctx->pipe->cmds = new struct command;
* ctx->command = &ctx->pipe->cmds[0];
*/
done_command(ctx);
}
@ -3320,7 +3319,7 @@ static int process_command_subs(o_string *dest,
{
int retcode, ch, eol_cnt;
o_string result = NULL_O_STRING;
struct p_context inner;
struct parse_context inner;
FILE *p;
struct in_str pipe_str;
@ -3367,7 +3366,7 @@ static int process_command_subs(o_string *dest,
}
#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)
{
/* 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. */
int rcode;
const char *endch = NULL;
struct p_context sub;
struct child_prog *child = ctx->child;
struct parse_context sub;
struct command *command = ctx->command;
debug_printf_parse("parse_group entered\n");
if (child->argv) {
if (command->argv /* word [word](... */
|| dest->length /* word(... */
|| dest->nonnull /* ""(... */
) {
syntax(NULL);
debug_printf_parse("parse_group return 1: syntax error, groups and arglists don't mix\n");
return 1;
@ -3388,17 +3390,17 @@ static int parse_group(o_string *dest, struct p_context *ctx,
endch = "}";
if (ch == '(') {
endch = ")";
child->subshell = 1;
command->subshell = 1;
}
rcode = parse_stream(dest, &sub, input, endch);
if (rcode == 0) {
done_word(dest, &sub); /* finish off the final word in the subcontext */
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);
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,
@ -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,
* -1 on EOF (but if end_trigger == NULL then return 0),
* 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)
{
int ch, m;
@ -3883,8 +3885,10 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
case '(':
#if ENABLE_HUSH_CASE
/* "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->command->argv == NULL /* not (word|(... */
) {
continue;
}
@ -3947,7 +3951,7 @@ static void update_charmap(void)
* from builtin_source() and builtin_eval() */
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;
int rcode;
@ -4408,12 +4412,12 @@ static int builtin_fg_bg(char **argv)
}
/* Restart the processes in the job */
debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_progs, pi->pgrp);
for (i = 0; i < pi->num_progs; i++) {
debug_printf_jobs("reviving pid %d\n", pi->progs[i].pid);
pi->progs[i].is_stopped = 0;
debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp);
for (i = 0; i < pi->num_cmds; i++) {
debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid);
pi->cmds[i].is_stopped = 0;
}
pi->stopped_progs = 0;
pi->stopped_cmds = 0;
i = kill(- pi->pgrp, SIGCONT);
if (i < 0) {
@ -4455,7 +4459,7 @@ static int builtin_jobs(char **argv UNUSED_PARAM)
const char *status_string;
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";
else
status_string = "Running";