summaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/hush.c432
1 files changed, 198 insertions, 234 deletions
diff --git a/shell/hush.c b/shell/hush.c
index f654055..b471bd8 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -427,9 +427,10 @@ struct globals {
line_input_t *line_input_state;
#endif
pid_t root_pid;
+ pid_t last_bg_pid;
#if ENABLE_HUSH_JOB
int run_list_level;
- pid_t saved_task_pgrp;
+// pid_t saved_task_pgrp;
pid_t saved_tty_pgrp;
int last_jobid;
struct pipe *job_list;
@@ -448,10 +449,9 @@ struct globals {
unsigned depth_break_continue;
unsigned depth_of_loop;
#endif
- pid_t last_bg_pid;
const char *ifs;
const char *cwd;
- struct variable *top_var; /* = &shell_ver (set in main()) */
+ struct variable *top_var; /* = &G.shell_ver (set in main()) */
struct variable shell_ver;
#if ENABLE_FEATURE_SH_STANDALONE
struct nofork_save_area nofork_save;
@@ -464,53 +464,9 @@ struct globals {
};
#define G (*ptr_to_globals)
-
-#if !ENABLE_HUSH_INTERACTIVE
-enum { interactive_fd = 0 };
-#endif
-#if !ENABLE_HUSH_JOB
-enum { run_list_level = 0 };
-#endif
-
-#if ENABLE_HUSH_INTERACTIVE
-#define interactive_fd (G.interactive_fd )
-#define PS1 (G.PS1 )
-#define PS2 (G.PS2 )
-#endif
-#if ENABLE_FEATURE_EDITING
-#define line_input_state (G.line_input_state)
-#endif
-#define root_pid (G.root_pid )
-#if ENABLE_HUSH_JOB
-#define run_list_level (G.run_list_level )
-#define saved_task_pgrp (G.saved_task_pgrp )
-#define saved_tty_pgrp (G.saved_tty_pgrp )
-#define last_jobid (G.last_jobid )
-#define job_list (G.job_list )
-#define toplevel_list (G.toplevel_list )
-#define toplevel_jb (G.toplevel_jb )
-#define ctrl_z_flag (G.ctrl_z_flag )
-#endif /* JOB */
-#define global_argv (G.global_argv )
-#define global_argc (G.global_argc )
-#define last_return_code (G.last_return_code)
-#define ifs (G.ifs )
-#define flag_break_continue (G.flag_break_continue )
-#define depth_break_continue (G.depth_break_continue)
-#define depth_of_loop (G.depth_of_loop )
-#define fake_mode (G.fake_mode )
-#define cwd (G.cwd )
-#define last_bg_pid (G.last_bg_pid )
-#define top_var (G.top_var )
-#define shell_ver (G.shell_ver )
-#if ENABLE_FEATURE_SH_STANDALONE
-#define nofork_save (G.nofork_save )
-#endif
-#if ENABLE_HUSH_JOB
-#define toplevel_jb (G.toplevel_jb )
-#endif
-#define charmap (G.charmap )
-#define user_input_buf (G.user_input_buf )
+/* Not #defining name to G.name - this quickly gets unwieldy
+ * (too many defines). Also, I actually prefer to see when a variable
+ * is global, thus "G." prefix is a useful hint */
#define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
} while (0)
@@ -522,23 +478,29 @@ enum { run_list_level = 0 };
/* Normal */
static void syntax(const char *msg)
{
+#if ENABLE_HUSH_INTERACTIVE
/* Was using fancy stuff:
- * (interactive_fd ? bb_error_msg : bb_error_msg_and_die)(...params...)
+ * (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die)(...params...)
* but it SEGVs. ?! Oh well... explicit temp ptr works around that */
void FAST_FUNC (*fp)(const char *s, ...);
-
- fp = (interactive_fd ? bb_error_msg : bb_error_msg_and_die);
+ fp = (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die);
fp(msg ? "%s: %s" : "syntax error", "syntax error", msg);
+#else
+ bb_error_msg_and_die(msg ? "%s: %s" : "syntax error", "syntax error", msg);
+#endif
}
#else
/* Debug */
static void syntax_lineno(int line)
{
+#if ENABLE_HUSH_INTERACTIVE
void FAST_FUNC (*fp)(const char *s, ...);
-
- fp = (interactive_fd ? bb_error_msg : bb_error_msg_and_die);
+ fp = (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die);
fp("syntax error hush.c:%d", line);
+#else
+ bb_error_msg_and_die("syntax error hush.c:%d", line);
+#endif
}
#define syntax(str) syntax_lineno(__LINE__)
#endif
@@ -844,7 +806,7 @@ static void handler_ctrl_c(int sig UNUSED_PARAM)
{
debug_printf_jobs("got sig %d\n", sig);
// as usual we can have all kinds of nasty problems with leaked malloc data here
- siglongjmp(toplevel_jb, 1);
+ siglongjmp(G.toplevel_jb, 1);
}
static void handler_ctrl_z(int sig UNUSED_PARAM)
@@ -855,7 +817,7 @@ static void handler_ctrl_z(int sig UNUSED_PARAM)
pid = fork();
if (pid < 0) /* can't fork. Pretend there was no ctrl-Z */
return;
- ctrl_z_flag = 1;
+ G.ctrl_z_flag = 1;
if (!pid) { /* child */
if (ENABLE_HUSH_JOB)
die_sleep = 0; /* let nofork's xfuncs die */
@@ -870,13 +832,13 @@ static void handler_ctrl_z(int sig UNUSED_PARAM)
}
/* parent */
/* finish filling up pipe info */
- toplevel_list->pgrp = pid; /* child is in its own pgrp */
- toplevel_list->progs[0].pid = pid;
+ G.toplevel_list->pgrp = pid; /* child is in its own pgrp */
+ G.toplevel_list->progs[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
debug_printf_jobs("siglongjmp in parent\n");
- siglongjmp(toplevel_jb, 1);
+ siglongjmp(G.toplevel_jb, 1);
}
/* Restores tty foreground process group, and exits.
@@ -890,8 +852,10 @@ static void sigexit(int sig)
/* Disable all signals: job control, SIGPIPE, etc. */
sigprocmask_allsigs(SIG_BLOCK);
- if (interactive_fd)
- tcsetpgrp(interactive_fd, saved_tty_pgrp);
+#if ENABLE_HUSH_INTERACTIVE
+ if (G.interactive_fd)
+ tcsetpgrp(G.interactive_fd, G.saved_tty_pgrp);
+#endif
/* Not a signal, just exit */
if (sig <= 0)
@@ -919,12 +883,12 @@ static void hush_exit(int exitcode)
static const char *set_cwd(void)
{
- if (cwd == bb_msg_unknown)
- cwd = NULL; /* xrealloc_getcwd_or_warn(arg) calls free(arg)! */
- cwd = xrealloc_getcwd_or_warn((char *)cwd);
- if (!cwd)
- cwd = bb_msg_unknown;
- return cwd;
+ if (G.cwd == bb_msg_unknown)
+ G.cwd = NULL; /* xrealloc_getcwd_or_warn(arg) calls free(arg)! */
+ G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd);
+ if (!G.cwd)
+ G.cwd = bb_msg_unknown;
+ return G.cwd;
}
@@ -1224,11 +1188,11 @@ static int static_peek(struct in_str *i)
static void cmdedit_set_initial_prompt(void)
{
#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
- PS1 = NULL;
+ G.PS1 = NULL;
#else
- PS1 = getenv("PS1");
- if (PS1 == NULL)
- PS1 = "\\w \\$ ";
+ G.PS1 = getenv("PS1");
+ if (G.PS1 == NULL)
+ G.PS1 = "\\w \\$ ";
#endif
}
#endif /* EDITING */
@@ -1240,14 +1204,14 @@ static const char* setup_prompt_string(int promptmode)
#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
/* Set up the prompt */
if (promptmode == 0) { /* PS1 */
- free((char*)PS1);
- PS1 = xasprintf("%s %c ", cwd, (geteuid() != 0) ? '$' : '#');
- prompt_str = PS1;
+ free((char*)G.PS1);
+ G.PS1 = xasprintf("%s %c ", G.cwd, (geteuid() != 0) ? '$' : '#');
+ prompt_str = G.PS1;
} else {
- prompt_str = PS2;
+ prompt_str = G.PS2;
}
#else
- prompt_str = (promptmode == 0) ? PS1 : PS2;
+ prompt_str = (promptmode == 0) ? G.PS1 : G.PS2;
#endif
debug_printf("result '%s'\n", prompt_str);
return prompt_str;
@@ -1263,21 +1227,21 @@ static void get_user_input(struct in_str *i)
/* Enable command line editing only while a command line
* is actually being read */
do {
- r = read_line_input(prompt_str, user_input_buf, BUFSIZ-1, line_input_state);
+ r = read_line_input(prompt_str, G.user_input_buf, BUFSIZ-1, G.line_input_state);
} while (r == 0); /* repeat if Ctrl-C */
i->eof_flag = (r < 0);
if (i->eof_flag) { /* EOF/error detected */
- user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */
- user_input_buf[1] = '\0';
+ G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */
+ G.user_input_buf[1] = '\0';
}
#else
fputs(prompt_str, stdout);
fflush(stdout);
- user_input_buf[0] = r = fgetc(i->file);
- /*user_input_buf[1] = '\0'; - already is and never changed */
+ G.user_input_buf[0] = r = fgetc(i->file);
+ /*G.user_input_buf[1] = '\0'; - already is and never changed */
i->eof_flag = (r == EOF);
#endif
- i->p = user_input_buf;
+ i->p = G.user_input_buf;
}
#endif /* INTERACTIVE */
@@ -1300,7 +1264,7 @@ static int file_get(struct in_str *i)
/* need to double check i->file because we might be doing something
* more complicated by now, like sourcing or substituting. */
#if ENABLE_HUSH_INTERACTIVE
- if (interactive_fd && i->promptme && i->file == stdin) {
+ if (G.interactive_fd && i->promptme && i->file == stdin) {
do {
get_user_input(i);
} while (!*i->p); /* need non-empty line */
@@ -1561,15 +1525,15 @@ static void insert_bg_job(struct pipe *pi)
/* Linear search for the ID of the job to use */
pi->jobid = 1;
- for (thejob = job_list; thejob; thejob = thejob->next)
+ for (thejob = G.job_list; thejob; thejob = thejob->next)
if (thejob->jobid >= pi->jobid)
pi->jobid = thejob->jobid + 1;
/* Add thejob to the list of running jobs */
- if (!job_list) {
- thejob = job_list = xmalloc(sizeof(*thejob));
+ if (!G.job_list) {
+ thejob = G.job_list = xmalloc(sizeof(*thejob));
} else {
- for (thejob = job_list; thejob->next; thejob = thejob->next)
+ for (thejob = G.job_list; thejob->next; thejob = thejob->next)
continue;
thejob->next = xmalloc(sizeof(*thejob));
thejob = thejob->next;
@@ -1591,26 +1555,26 @@ 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);
- last_bg_pid = thejob->progs[0].pid;
- last_jobid = thejob->jobid;
+ G.last_bg_pid = thejob->progs[0].pid;
+ G.last_jobid = thejob->jobid;
}
static void remove_bg_job(struct pipe *pi)
{
struct pipe *prev_pipe;
- if (pi == job_list) {
- job_list = pi->next;
+ if (pi == G.job_list) {
+ G.job_list = pi->next;
} else {
- prev_pipe = job_list;
+ prev_pipe = G.job_list;
while (prev_pipe->next != pi)
prev_pipe = prev_pipe->next;
prev_pipe->next = pi->next;
}
- if (job_list)
- last_jobid = job_list->jobid;
+ if (G.job_list)
+ G.last_jobid = G.job_list->jobid;
else
- last_jobid = 0;
+ G.last_jobid = 0;
}
/* Remove a backgrounded job */
@@ -1705,7 +1669,7 @@ static int checkjobs(struct pipe* fg_pipe)
#if ENABLE_HUSH_JOB
/* We asked to wait for bg or orphaned children */
/* No need to remember exitcode in this case */
- for (pi = job_list; pi; pi = pi->next) {
+ for (pi = G.job_list; pi; pi = pi->next) {
for (i = 0; i < pi->num_progs; i++) {
if (pi->progs[i].pid == childpid)
goto found_pi_and_prognum;
@@ -1748,8 +1712,8 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
/* Job finished, move the shell to the foreground */
p = getpgid(0); /* pgid of our process */
debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p);
- tcsetpgrp(interactive_fd, p);
-// if (tcsetpgrp(interactive_fd, p) && errno != ENOTTY)
+ tcsetpgrp(G.interactive_fd, p);
+// if (tcsetpgrp(G.interactive_fd, p) && errno != ENOTTY)
// bb_perror_msg("tcsetpgrp-4a");
return rcode;
}
@@ -1865,9 +1829,9 @@ 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);
- save_nofork_data(&nofork_save);
+ 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(&nofork_save, a, argv_expanded);
+ rcode = run_nofork_applet_prime(&G.nofork_save, a, argv_expanded);
free(argv_expanded);
restore_redirects(squirrel);
debug_printf_exec("run_pipe return %d\n", rcode);
@@ -1916,7 +1880,7 @@ static int run_pipe(struct pipe *pi)
#if ENABLE_HUSH_JOB
/* Every child adds itself to new process group
* with pgid == pid_of_first_child_in_pipe */
- if (run_list_level == 1 && interactive_fd) {
+ if (G.run_list_level == 1 && G.interactive_fd) {
pid_t pgrp;
/* Don't do pgrp restore anymore on fatal signals */
set_fatal_sighandler(SIG_DFL);
@@ -1926,7 +1890,7 @@ static int run_pipe(struct pipe *pi)
if (setpgid(0, pgrp) == 0 && pi->followup != PIPE_BG) {
/* We do it in *every* child, not just first,
* to avoid races */
- tcsetpgrp(interactive_fd, pgrp);
+ tcsetpgrp(G.interactive_fd, pgrp);
}
}
#endif
@@ -2072,7 +2036,7 @@ static int run_list(struct pipe *pi)
/*enum reserved_style*/ smallint rword = RES_NONE;
/*enum reserved_style*/ smallint skip_more_for_this_rword = RES_XXXX;
- debug_printf_exec("run_list start lvl %d\n", run_list_level + 1);
+ debug_printf_exec("run_list start lvl %d\n", G.run_list_level + 1);
#if ENABLE_HUSH_LOOPS
/* Check syntax for "for" */
@@ -2082,7 +2046,7 @@ static int run_list(struct pipe *pi)
/* current word is FOR or IN (BOLD in comments below) */
if (cpipe->next == NULL) {
syntax("malformed for");
- debug_printf_exec("run_list lvl %d return 1\n", run_list_level);
+ debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
return 1;
}
/* "FOR v; do ..." and "for v IN a b; do..." are ok */
@@ -2093,7 +2057,7 @@ static int run_list(struct pipe *pi)
|| cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
) {
syntax("malformed for");
- debug_printf_exec("run_list lvl %d return 1\n", run_list_level);
+ debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
return 1;
}
}
@@ -2108,22 +2072,22 @@ static int run_list(struct pipe *pi)
* We are saving state before entering outermost list ("while...done")
* so that ctrl-Z will correctly background _entire_ outermost list,
* not just a part of it (like "sleep 1 | exit 2") */
- if (++run_list_level == 1 && interactive_fd) {
- if (sigsetjmp(toplevel_jb, 1)) {
+ if (++G.run_list_level == 1 && G.interactive_fd) {
+ if (sigsetjmp(G.toplevel_jb, 1)) {
/* ctrl-Z forked and we are parent; or ctrl-C.
* Sighandler has longjmped us here */
signal(SIGINT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
/* Restore level (we can be coming from deep inside
* nested levels) */
- run_list_level = 1;
+ G.run_list_level = 1;
#if ENABLE_FEATURE_SH_STANDALONE
- if (nofork_save.saved) { /* if save area is valid */
+ if (G.nofork_save.saved) { /* if save area is valid */
debug_printf_jobs("exiting nofork early\n");
- restore_nofork_data(&nofork_save);
+ restore_nofork_data(&G.nofork_save);
}
#endif
- if (ctrl_z_flag) {
+ if (G.ctrl_z_flag) {
/* ctrl-Z has forked and stored pid of the child in pi->pid.
* Remember this child as background job */
insert_bg_job(pi);
@@ -2132,15 +2096,15 @@ static int run_list(struct pipe *pi)
bb_putchar('\n');
}
USE_HUSH_LOOPS(loop_top = NULL;)
- USE_HUSH_LOOPS(depth_of_loop = 0;)
+ USE_HUSH_LOOPS(G.depth_of_loop = 0;)
rcode = 0;
goto ret;
}
/* ctrl-Z handler will store pid etc in pi */
- toplevel_list = pi;
- ctrl_z_flag = 0;
+ G.toplevel_list = pi;
+ G.ctrl_z_flag = 0;
#if ENABLE_FEATURE_SH_STANDALONE
- nofork_save.saved = 0; /* in case we will run a nofork later */
+ G.nofork_save.saved = 0; /* in case we will run a nofork later */
#endif
signal_SA_RESTART_empty_mask(SIGTSTP, handler_ctrl_z);
signal(SIGINT, handler_ctrl_c);
@@ -2155,11 +2119,11 @@ static int run_list(struct pipe *pi)
rword, cond_code, skip_more_for_this_rword);
#if ENABLE_HUSH_LOOPS
if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
- && loop_top == NULL /* avoid bumping depth_of_loop twice */
+ && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
) {
/* start of a loop: remember where loop starts */
loop_top = pi;
- depth_of_loop++;
+ G.depth_of_loop++;
}
#endif
if (rword == skip_more_for_this_rword && flag_skip) {
@@ -2268,7 +2232,7 @@ static int run_list(struct pipe *pi)
{
int r;
#if ENABLE_HUSH_LOOPS
- flag_break_continue = 0;
+ G.flag_break_continue = 0;
#endif
rcode = r = run_pipe(pi); /* NB: rcode is a smallint */
if (r != -1) {
@@ -2276,17 +2240,17 @@ static int run_list(struct pipe *pi)
* and we don't need to wait for anything. */
#if ENABLE_HUSH_LOOPS
/* was it "break" or "continue"? */
- if (flag_break_continue) {
- smallint fbc = flag_break_continue;
+ if (G.flag_break_continue) {
+ smallint fbc = G.flag_break_continue;
/* we might fall into outer *loop*,
* don't want to break it too */
if (loop_top) {
- depth_break_continue--;
- if (depth_break_continue == 0)
- flag_break_continue = 0;
+ G.depth_break_continue--;
+ if (G.depth_break_continue == 0)
+ G.flag_break_continue = 0;
/* else: e.g. "continue 2" should *break* once, *then* continue */
} /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
- if (depth_break_continue != 0 || fbc == BC_BREAK)
+ if (G.depth_break_continue != 0 || fbc == BC_BREAK)
goto check_jobs_and_break;
/* "continue": simulate end of loop */
rword = RES_DONE;
@@ -2299,13 +2263,13 @@ static int run_list(struct pipe *pi)
* try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
* I'm NOT treating inner &'s as jobs */
#if ENABLE_HUSH_JOB
- if (run_list_level == 1)
+ if (G.run_list_level == 1)
insert_bg_job(pi);
#endif
rcode = 0; /* EXIT_SUCCESS */
} else {
#if ENABLE_HUSH_JOB
- if (run_list_level == 1 && interactive_fd) {
+ if (G.run_list_level == 1 && G.interactive_fd) {
/* waits for completion, then fg's main shell */
rcode = checkjobs_and_fg_shell(pi);
debug_printf_exec(": checkjobs_and_fg_shell returned %d\n", rcode);
@@ -2318,7 +2282,7 @@ static int run_list(struct pipe *pi)
}
}
debug_printf_exec(": setting last_return_code=%d\n", rcode);
- last_return_code = rcode;
+ G.last_return_code = rcode;
/* Analyze how result affects subsequent commands */
#if ENABLE_HUSH_IF
@@ -2349,22 +2313,22 @@ static int run_list(struct pipe *pi)
} /* for (pi) */
#if ENABLE_HUSH_JOB
- if (ctrl_z_flag) {
+ if (G.ctrl_z_flag) {
/* ctrl-Z forked somewhere in the past, we are the child,
* and now we completed running the list. Exit. */
//TODO: _exit?
exit(rcode);
}
ret:
- if (!--run_list_level && interactive_fd) {
+ if (!--G.run_list_level && G.interactive_fd) {
signal(SIGTSTP, SIG_IGN);
signal(SIGINT, SIG_IGN);
}
#endif
- debug_printf_exec("run_list lvl %d return %d\n", run_list_level + 1, rcode);
+ debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode);
#if ENABLE_HUSH_LOOPS
if (loop_top)
- depth_of_loop--;
+ G.depth_of_loop--;
free(for_list);
#endif
#if ENABLE_HUSH_CASE
@@ -2449,7 +2413,7 @@ static int run_and_free_list(struct pipe *pi)
{
int rcode = 0;
debug_printf_exec("run_and_free_list entered\n");
- if (!fake_mode) {
+ if (!G.fake_mode) {
debug_printf_exec(": run_list with %d members\n", pi->num_progs);
rcode = run_list(pi);
}
@@ -2472,12 +2436,12 @@ static int run_and_free_list(struct pipe *pi)
* Caller can deallocate entire list by single free(list). */
/* Store given string, finalizing the word and starting new one whenever
- * we encounter ifs char(s). This is used for expanding variable values.
+ * we encounter IFS char(s). This is used for expanding variable values.
* End-of-string does NOT finalize word: think about 'echo -$VAR-' */
static int expand_on_ifs(o_string *output, int n, const char *str)
{
while (1) {
- int word_len = strcspn(str, ifs);
+ int word_len = strcspn(str, G.ifs);
if (word_len) {
if (output->o_quote || !output->o_glob)
o_addQstr(output, str, word_len);
@@ -2490,7 +2454,7 @@ static int expand_on_ifs(o_string *output, int n, const char *str)
o_addchr(output, '\0');
debug_print_list("expand_on_ifs", output, n);
n = o_save_ptr(output, n);
- str += strspn(str, ifs); /* skip ifs chars */
+ str += strspn(str, G.ifs); /* skip ifs chars */
}
debug_print_list("expand_on_ifs[1]", output, n);
return n;
@@ -2540,31 +2504,31 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
switch (first_ch & 0x7f) {
/* Highest bit in first_ch indicates that var is double-quoted */
case '$': /* pid */
- val = utoa(root_pid);
+ val = utoa(G.root_pid);
break;
case '!': /* bg pid */
- val = last_bg_pid ? utoa(last_bg_pid) : (char*)"";
+ val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)"";
break;
case '?': /* exitcode */
- val = utoa(last_return_code);
+ val = utoa(G.last_return_code);
break;
case '#': /* argc */
- val = utoa(global_argc ? global_argc-1 : 0);
+ val = utoa(G.global_argc ? G.global_argc-1 : 0);
break;
case '*':
case '@':
i = 1;
- if (!global_argv[i])
+ if (!G.global_argv[i])
break;
ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
smallint sv = output->o_quote;
/* unquoted var's contents should be globbed, so don't quote */
output->o_quote = 0;
- while (global_argv[i]) {
- n = expand_on_ifs(output, n, global_argv[i]);
- debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, global_argc-1);
- if (global_argv[i++][0] && global_argv[i]) {
+ while (G.global_argv[i]) {
+ n = expand_on_ifs(output, n, G.global_argv[i]);
+ debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
+ if (G.global_argv[i++][0] && G.global_argv[i]) {
/* this argv[] is not empty and not last:
* put terminating NUL, start new word */
o_addchr(output, '\0');
@@ -2579,8 +2543,8 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
* and in this case should treat it like '$*' - see 'else...' below */
if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */
while (1) {
- o_addQstr(output, global_argv[i], strlen(global_argv[i]));
- if (++i >= global_argc)
+ o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i]));
+ if (++i >= G.global_argc)
break;
o_addchr(output, '\0');
debug_print_list("expand_vars_to_list[4]", output, n);
@@ -2588,11 +2552,11 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
}
} else { /* quoted $*: add as one word */
while (1) {
- o_addQstr(output, global_argv[i], strlen(global_argv[i]));
- if (!global_argv[++i])
+ o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i]));
+ if (!G.global_argv[++i])
break;
- if (ifs[0])
- o_addchr(output, ifs[0]);
+ if (G.ifs[0])
+ o_addchr(output, G.ifs[0]);
}
}
break;
@@ -2620,8 +2584,8 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
arg[0] = first_ch & 0x7f;
if (isdigit(arg[0])) {
i = xatoi_u(arg);
- if (i < global_argc)
- val = global_argv[i];
+ if (i < G.global_argc)
+ val = G.global_argv[i];
/* else val remains NULL: $N with too big N */
} else
val = lookup_param(arg);
@@ -2736,7 +2700,7 @@ static char* expand_strvec_to_string(char **argv)
if (HUSH_DEBUG)
if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
bb_error_msg_and_die("BUG in varexp3");
- list[n][-1] = ' '; /* TODO: or to ifs[0]? */
+ list[n][-1] = ' '; /* TODO: or to G.ifs[0]? */
n++;
}
}
@@ -2755,7 +2719,7 @@ static struct variable *get_local_var(const char *name)
if (!name)
return NULL;
len = strlen(name);
- for (cur = top_var; cur; cur = cur->next) {
+ for (cur = G.top_var; cur; cur = cur->next) {
if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
return cur;
}
@@ -2777,7 +2741,7 @@ static int set_local_var(char *str, int flg_export)
}
name_len = value - str + 1; /* including '=' */
- cur = top_var; /* cannot be NULL (we have HUSH_VERSION and it's RO) */
+ cur = G.top_var; /* cannot be NULL (we have HUSH_VERSION and it's RO) */
while (1) {
if (strncmp(cur->varstr, str, name_len) != 0) {
if (!cur->next) {
@@ -2838,7 +2802,7 @@ static void unset_local_var(const char *name)
if (!name)
return;
name_len = strlen(name);
- cur = top_var;
+ cur = G.top_var;
while (cur) {
if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
if (cur->flg_read_only) {
@@ -3319,7 +3283,7 @@ static FILE *generate_stream_from_list(struct pipe *head)
xmove_fd(channel[1], 1);
/* Prevent it from trying to handle ctrl-z etc */
#if ENABLE_HUSH_JOB
- run_list_level = 1;
+ G.run_list_level = 1;
#endif
/* Process substitution is not considered to be usual
* 'command execution'.
@@ -3654,7 +3618,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
next = '\0';
ch = i_getch(input);
if (ch != EOF) {
- m = charmap[ch];
+ m = G.charmap[ch];
if (ch != '\n') {
next = i_peek(input);
}
@@ -3915,29 +3879,28 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
static void set_in_charmap(const char *set, int code)
{
while (*set)
- charmap[(unsigned char)*set++] = code;
+ G.charmap[(unsigned char)*set++] = code;
}
static void update_charmap(void)
{
- /* char *ifs and char charmap[256] are both globals. */
- ifs = getenv("IFS");
- if (ifs == NULL)
- ifs = " \t\n";
+ G.ifs = getenv("IFS");
+ if (G.ifs == NULL)
+ G.ifs = " \t\n";
/* Precompute a list of 'flow through' behavior so it can be treated
* quickly up front. Computation is necessary because of IFS.
* Special case handling of IFS == " \t\n" is not implemented.
* The charmap[] array only really needs two bits each,
* and on most machines that would be faster (reduced L1 cache use).
*/
- memset(charmap, CHAR_ORDINARY, sizeof(charmap));
+ memset(G.charmap, CHAR_ORDINARY, sizeof(G.charmap));
#if ENABLE_HUSH_TICK
set_in_charmap("\\$\"`", CHAR_SPECIAL);
#else
set_in_charmap("\\$\"", CHAR_SPECIAL);
#endif
set_in_charmap("<>;&|(){}#'", CHAR_ORDINARY_IF_QUOTED);
- set_in_charmap(ifs, CHAR_IFS); /* are ordinary if quoted */
+ set_in_charmap(G.ifs, CHAR_IFS); /* are ordinary if quoted */
}
/* Most recursion does not come through here, the exception is
@@ -4016,13 +3979,14 @@ static void setup_job_control(void)
{
pid_t shell_pgrp;
- saved_task_pgrp = shell_pgrp = getpgrp();
- debug_printf_jobs("saved_task_pgrp=%d\n", saved_task_pgrp);
- close_on_exec_on(interactive_fd);
+// G.saved_task_pgrp =
+ shell_pgrp = getpgrp();
+// debug_printf_jobs("saved_task_pgrp=%d\n", G.saved_task_pgrp);
+ close_on_exec_on(G.interactive_fd);
/* If we were ran as 'hush &',
* sleep until we are in the foreground. */
- while (tcgetpgrp(interactive_fd) != shell_pgrp) {
+ while (tcgetpgrp(G.interactive_fd) != shell_pgrp) {
/* Send TTIN to ourself (should stop us) */
kill(- shell_pgrp, SIGTTIN);
shell_pgrp = getpgrp();
@@ -4039,7 +4003,7 @@ static void setup_job_control(void)
/* Put ourselves in our own process group. */
setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
/* Grab control of the terminal. */
- tcsetpgrp(interactive_fd, getpid());
+ tcsetpgrp(G.interactive_fd, getpid());
}
#endif
@@ -4063,15 +4027,15 @@ int hush_main(int argc, char **argv)
INIT_G();
- root_pid = getpid();
+ G.root_pid = getpid();
/* Deal with HUSH_VERSION */
- shell_ver = const_shell_ver; /* copying struct here */
- top_var = &shell_ver;
+ G.shell_ver = const_shell_ver; /* copying struct here */
+ G.top_var = &G.shell_ver;
unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
/* Initialize our shell local variables with the values
* currently living in the environment */
- cur_var = top_var;
+ cur_var = G.top_var;
e = environ;
if (e) while (*e) {
char *value = strchr(*e, '=');
@@ -4087,22 +4051,22 @@ int hush_main(int argc, char **argv)
putenv((char *)version_str); /* reinstate HUSH_VERSION */
#if ENABLE_FEATURE_EDITING
- line_input_state = new_line_input_t(FOR_SHELL);
+ G.line_input_state = new_line_input_t(FOR_SHELL);
#endif
/* XXX what should these be while sourcing /etc/profile? */
- global_argc = argc;
- global_argv = argv;
+ G.global_argc = argc;
+ G.global_argv = argv;
/* Initialize some more globals to non-zero values */
set_cwd();
#if ENABLE_HUSH_INTERACTIVE
#if ENABLE_FEATURE_EDITING
cmdedit_set_initial_prompt();
#endif
- PS2 = "> ";
+ G.PS2 = "> ";
#endif
if (EXIT_SUCCESS) /* otherwise is already done */
- last_return_code = EXIT_SUCCESS;
+ G.last_return_code = EXIT_SUCCESS;
if (argv[0] && argv[0][0] == '-') {
debug_printf("sourcing /etc/profile\n");
@@ -4118,17 +4082,17 @@ int hush_main(int argc, char **argv)
while ((opt = getopt(argc, argv, "c:xif")) > 0) {
switch (opt) {
case 'c':
- global_argv = argv + optind;
- global_argc = argc - optind;
+ G.global_argv = argv + optind;
+ G.global_argc = argc - optind;
opt = parse_and_run_string(optarg, 0 /* parse_flag */);
goto final_return;
case 'i':
/* Well, we cannot just declare interactiveness,
* we have to have some stuff (ctty, etc) */
- /* interactive_fd++; */
+ /* G.interactive_fd++; */
break;
case 'f':
- fake_mode = 1;
+ G.fake_mode = 1;
break;
default:
#ifndef BB_VER
@@ -4151,25 +4115,25 @@ int hush_main(int argc, char **argv)
if (argv[optind] == NULL && input == stdin
&& isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
) {
- saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
- debug_printf("saved_tty_pgrp=%d\n", saved_tty_pgrp);
- if (saved_tty_pgrp >= 0) {
+ G.saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
+ debug_printf("saved_tty_pgrp=%d\n", G.saved_tty_pgrp);
+ if (G.saved_tty_pgrp >= 0) {
/* try to dup to high fd#, >= 255 */
- interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
- if (interactive_fd < 0) {
+ G.interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
+ if (G.interactive_fd < 0) {
/* try to dup to any fd */
- interactive_fd = dup(STDIN_FILENO);
- if (interactive_fd < 0)
+ G.interactive_fd = dup(STDIN_FILENO);
+ if (G.interactive_fd < 0)
/* give up */
- interactive_fd = 0;
+ G.interactive_fd = 0;
}
// TODO: track & disallow any attempts of user
// to (inadvertently) close/redirect it
}
}
- debug_printf("interactive_fd=%d\n", interactive_fd);
- if (interactive_fd) {
- fcntl(interactive_fd, F_SETFD, FD_CLOEXEC);
+ debug_printf("G.interactive_fd=%d\n", G.interactive_fd);
+ if (G.interactive_fd) {
+ fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC);
/* Looks like they want an interactive shell */
setup_job_control();
/* -1 is special - makes xfuncs longjmp, not exit
@@ -4189,16 +4153,16 @@ int hush_main(int argc, char **argv)
if (argv[optind] == NULL && input == stdin
&& isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
) {
- interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
- if (interactive_fd < 0) {
+ G.interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
+ if (G.interactive_fd < 0) {
/* try to dup to any fd */
- interactive_fd = dup(STDIN_FILENO);
- if (interactive_fd < 0)
+ G.interactive_fd = dup(STDIN_FILENO);
+ if (G.interactive_fd < 0)
/* give up */
- interactive_fd = 0;
+ G.interactive_fd = 0;
}
- if (interactive_fd) {
- fcntl(interactive_fd, F_SETFD, FD_CLOEXEC);
+ if (G.interactive_fd) {
+ fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC);
set_misc_sighandler(SIG_IGN);
}
}
@@ -4208,8 +4172,8 @@ int hush_main(int argc, char **argv)
opt = parse_and_run_file(stdin);
} else {
debug_printf("\nrunning script '%s'\n", argv[optind]);
- global_argv = argv + optind;
- global_argc = argc - optind;
+ G.global_argv = argv + optind;
+ G.global_argc = argc - optind;
input = xfopen_for_read(argv[optind]);
fcntl(fileno(input), F_SETFD, FD_CLOEXEC);
opt = parse_and_run_file(input);
@@ -4219,9 +4183,9 @@ int hush_main(int argc, char **argv)
#if ENABLE_FEATURE_CLEAN_UP
fclose(input);
- if (cwd != bb_msg_unknown)
- free((char*)cwd);
- cur_var = top_var->next;
+ if (G.cwd != bb_msg_unknown)
+ free((char*)G.cwd);
+ cur_var = G.top_var->next;
while (cur_var) {
struct variable *tmp = cur_var;
if (!cur_var->max_len)
@@ -4230,7 +4194,7 @@ int hush_main(int argc, char **argv)
free(tmp);
}
#endif
- hush_exit(opt ? opt : last_return_code);
+ hush_exit(opt ? opt : G.last_return_code);
}
@@ -4280,7 +4244,7 @@ static int builtin_eval(char **argv)
char *str = expand_strvec_to_string(argv + 1);
parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP);
free(str);
- rcode = last_return_code;
+ rcode = G.last_return_code;
}
return rcode;
}
@@ -4323,7 +4287,7 @@ static int builtin_exit(char **argv)
// TODO: warn if we have background jobs: "There are stopped jobs"
// On second consecutive 'exit', exit anyway.
if (argv[1] == NULL)
- hush_exit(last_return_code);
+ hush_exit(G.last_return_code);
/* mimic bash: exit 123abc == exit 255 + error msg */
xfunc_error_retval = 255;
/* bash: exit -2 == exit 254, no error msg */
@@ -4373,12 +4337,12 @@ static int builtin_fg_bg(char **argv)
int i, jobnum;
struct pipe *pi;
- if (!interactive_fd)
+ if (!G.interactive_fd)
return EXIT_FAILURE;
/* If they gave us no args, assume they want the last backgrounded task */
if (!argv[1]) {
- for (pi = job_list; pi; pi = pi->next) {
- if (pi->jobid == last_jobid) {
+ for (pi = G.job_list; pi; pi = pi->next) {
+ if (pi->jobid == G.last_jobid) {
goto found;
}
}
@@ -4389,7 +4353,7 @@ static int builtin_fg_bg(char **argv)
bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]);
return EXIT_FAILURE;
}
- for (pi = job_list; pi; pi = pi->next) {
+ for (pi = G.job_list; pi; pi = pi->next) {
if (pi->jobid == jobnum) {
goto found;
}
@@ -4401,7 +4365,7 @@ static int builtin_fg_bg(char **argv)
// of job being foregrounded (like "sleep 1 | cat")
if (*argv[0] == 'f') {
/* Put the job into the foreground. */
- tcsetpgrp(interactive_fd, pi->pgrp);
+ tcsetpgrp(G.interactive_fd, pi->pgrp);
}
/* Restart the processes in the job */
@@ -4451,7 +4415,7 @@ static int builtin_jobs(char **argv UNUSED_PARAM)
struct pipe *job;
const char *status_string;
- for (job = job_list; job; job = job->next) {
+ for (job = G.job_list; job; job = job->next) {
if (job->alive_progs == job->stopped_progs)
status_string = "Stopped";
else
@@ -4485,7 +4449,7 @@ static int builtin_set(char **argv)
struct variable *e;
if (temp == NULL)
- for (e = top_var; e; e = e->next)
+ for (e = G.top_var; e; e = e->next)
puts(e->varstr);
else
set_local_var(xstrdup(temp), 0);
@@ -4499,10 +4463,10 @@ static int builtin_shift(char **argv)
if (argv[1]) {
n = atoi(argv[1]);
}
- if (n >= 0 && n < global_argc) {
- global_argv[n] = global_argv[0];
- global_argc -= n;
- global_argv += n;
+ if (n >= 0 && n < G.global_argc) {
+ G.global_argv[n] = G.global_argv[0];
+ G.global_argc -= n;
+ G.global_argv += n;
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
@@ -4525,9 +4489,9 @@ static int builtin_source(char **argv)
close_on_exec_on(fileno(input));
/* Now run the file */
- /* XXX argv and argc are broken; need to save old global_argv
+ /* XXX argv and argc are broken; need to save old G.global_argv
* (pointer only is OK!) on this stack frame,
- * set global_argv=argv+1, recurse, and restore. */
+ * set G.global_argv=argv+1, recurse, and restore. */
status = parse_and_run_file(input);
fclose(input);
return status;
@@ -4561,29 +4525,29 @@ static int builtin_unset(char **argv)
#if ENABLE_HUSH_LOOPS
static int builtin_break(char **argv)
{
- if (depth_of_loop == 0) {
+ if (G.depth_of_loop == 0) {
bb_error_msg("%s: only meaningful in a loop", "break");
return EXIT_SUCCESS; /* bash compat */
}
- flag_break_continue++; /* BC_BREAK = 1 */
- depth_break_continue = 1;
+ G.flag_break_continue++; /* BC_BREAK = 1 */
+ G.depth_break_continue = 1;
if (argv[1]) {
- depth_break_continue = bb_strtou(argv[1], NULL, 10);
- if (errno || !depth_break_continue || argv[2]) {
+ G.depth_break_continue = bb_strtou(argv[1], NULL, 10);
+ if (errno || !G.depth_break_continue || argv[2]) {
bb_error_msg("bad arguments");
- flag_break_continue = BC_BREAK;
- depth_break_continue = UINT_MAX;
+ G.flag_break_continue = BC_BREAK;
+ G.depth_break_continue = UINT_MAX;
}
}
- if (depth_of_loop < depth_break_continue)
- depth_break_continue = depth_of_loop;
+ if (G.depth_of_loop < G.depth_break_continue)
+ G.depth_break_continue = G.depth_of_loop;
return EXIT_SUCCESS;
}
static int builtin_continue(char **argv)
{
- if (depth_of_loop) {
- flag_break_continue++; /* BC_CONTINUE = 2 = 1+1 */
+ if (G.depth_of_loop) {
+ G.flag_break_continue = 1; /* BC_CONTINUE = 2 = 1+1 */
return builtin_break(argv);
}
bb_error_msg("%s: only meaningful in a loop", "continue");