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 '('
No binary (build failed)
—
(log yok)
| @@ -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"; | |