aboutsummaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/hush.c')
-rw-r--r--shell/hush.c384
1 files changed, 310 insertions, 74 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 4832e2c48..c5a8ea617 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -101,6 +101,137 @@
101# define PIPE_BUF 4096 /* amount of buffering in a pipe */ 101# define PIPE_BUF 4096 /* amount of buffering in a pipe */
102#endif 102#endif
103 103
104//applet:IF_HUSH(APPLET(hush, _BB_DIR_BIN, _BB_SUID_DROP))
105//applet:IF_MSH(APPLET(msh, _BB_DIR_BIN, _BB_SUID_DROP))
106//applet:IF_LASH(APPLET(lash, _BB_DIR_BIN, _BB_SUID_DROP))
107//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, _BB_DIR_BIN, _BB_SUID_DROP, sh))
108//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash))
109
110//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
111//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
112
113//config:config HUSH
114//config: bool "hush"
115//config: default y
116//config: help
117//config: hush is a small shell (22k). It handles the normal flow control
118//config: constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
119//config: case/esac. Redirections, here documents, $((arithmetic))
120//config: and functions are supported.
121//config:
122//config: It will compile and work on no-mmu systems.
123//config:
124//config: It does not handle select, aliases, brace expansion,
125//config: tilde expansion, &>file and >&file redirection of stdout+stderr.
126//config:
127//config:config HUSH_BASH_COMPAT
128//config: bool "bash-compatible extensions"
129//config: default y
130//config: depends on HUSH
131//config: help
132//config: Enable bash-compatible extensions.
133//config:
134//config:config HUSH_HELP
135//config: bool "help builtin"
136//config: default y
137//config: depends on HUSH
138//config: help
139//config: Enable help builtin in hush. Code size + ~1 kbyte.
140//config:
141//config:config HUSH_INTERACTIVE
142//config: bool "Interactive mode"
143//config: default y
144//config: depends on HUSH
145//config: help
146//config: Enable interactive mode (prompt and command editing).
147//config: Without this, hush simply reads and executes commands
148//config: from stdin just like a shell script from a file.
149//config: No prompt, no PS1/PS2 magic shell variables.
150//config:
151//config:config HUSH_JOB
152//config: bool "Job control"
153//config: default y
154//config: depends on HUSH_INTERACTIVE
155//config: help
156//config: Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
157//config: command (not entire shell), fg/bg builtins work. Without this option,
158//config: "cmd &" still works by simply spawning a process and immediately
159//config: prompting for next command (or executing next command in a script),
160//config: but no separate process group is formed.
161//config:
162//config:config HUSH_TICK
163//config: bool "Process substitution"
164//config: default y
165//config: depends on HUSH
166//config: help
167//config: Enable process substitution `command` and $(command) in hush.
168//config:
169//config:config HUSH_IF
170//config: bool "Support if/then/elif/else/fi"
171//config: default y
172//config: depends on HUSH
173//config: help
174//config: Enable if/then/elif/else/fi in hush.
175//config:
176//config:config HUSH_LOOPS
177//config: bool "Support for, while and until loops"
178//config: default y
179//config: depends on HUSH
180//config: help
181//config: Enable for, while and until loops in hush.
182//config:
183//config:config HUSH_CASE
184//config: bool "Support case ... esac statement"
185//config: default y
186//config: depends on HUSH
187//config: help
188//config: Enable case ... esac statement in hush. +400 bytes.
189//config:
190//config:config HUSH_FUNCTIONS
191//config: bool "Support funcname() { commands; } syntax"
192//config: default y
193//config: depends on HUSH
194//config: help
195//config: Enable support for shell functions in hush. +800 bytes.
196//config:
197//config:config HUSH_LOCAL
198//config: bool "Support local builtin"
199//config: default y
200//config: depends on HUSH_FUNCTIONS
201//config: help
202//config: Enable support for local variables in functions.
203//config:
204//config:config HUSH_RANDOM_SUPPORT
205//config: bool "Pseudorandom generator and $RANDOM variable"
206//config: default y
207//config: depends on HUSH
208//config: help
209//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
210//config: Each read of "$RANDOM" will generate a new pseudorandom value.
211//config:
212//config:config HUSH_EXPORT_N
213//config: bool "Support 'export -n' option"
214//config: default y
215//config: depends on HUSH
216//config: help
217//config: export -n unexports variables. It is a bash extension.
218//config:
219//config:config HUSH_MODE_X
220//config: bool "Support 'hush -x' option and 'set -x' command"
221//config: default y
222//config: depends on HUSH
223//config: help
224//config: This instructs hush to print commands before execution.
225//config: Adds ~300 bytes.
226//config:
227
228//usage:#define hush_trivial_usage NOUSAGE_STR
229//usage:#define hush_full_usage ""
230//usage:#define lash_trivial_usage NOUSAGE_STR
231//usage:#define lash_full_usage ""
232//usage:#define msh_trivial_usage NOUSAGE_STR
233//usage:#define msh_full_usage ""
234
104 235
105/* Build knobs */ 236/* Build knobs */
106#define LEAK_HUNTING 0 237#define LEAK_HUNTING 0
@@ -117,6 +248,10 @@
117 * and therefore waitpid will return the same result as last time) 248 * and therefore waitpid will return the same result as last time)
118 */ 249 */
119#define ENABLE_HUSH_FAST 0 250#define ENABLE_HUSH_FAST 0
251/* TODO: implement simplified code for users which do not need ${var%...} ops
252 * So far ${var%...} ops are always enabled:
253 */
254#define ENABLE_HUSH_DOLLAR_OPS 1
120 255
121 256
122#if BUILD_AS_NOMMU 257#if BUILD_AS_NOMMU
@@ -128,9 +263,7 @@
128# define USE_FOR_MMU(...) 263# define USE_FOR_MMU(...)
129#endif 264#endif
130 265
131#define SKIP_definitions 1 266#include "NUM_APPLETS.h"
132#include "applet_tables.h"
133#undef SKIP_definitions
134#if NUM_APPLETS == 1 267#if NUM_APPLETS == 1
135/* STANDALONE does not make sense, and won't compile */ 268/* STANDALONE does not make sense, and won't compile */
136# undef CONFIG_FEATURE_SH_STANDALONE 269# undef CONFIG_FEATURE_SH_STANDALONE
@@ -529,7 +662,13 @@ struct globals {
529 */ 662 */
530 smallint flag_return_in_progress; 663 smallint flag_return_in_progress;
531#endif 664#endif
532 smallint fake_mode; 665 smallint n_mode;
666#if ENABLE_HUSH_MODE_X
667 smallint x_mode;
668# define G_x_mode (G.x_mode)
669#else
670# define G_x_mode 0
671#endif
533 smallint exiting; /* used to prevent EXIT trap recursion */ 672 smallint exiting; /* used to prevent EXIT trap recursion */
534 /* These four support $?, $#, and $1 */ 673 /* These four support $?, $#, and $1 */
535 smalluint last_exitcode; 674 smalluint last_exitcode;
@@ -550,6 +689,7 @@ struct globals {
550 const char *cwd; 689 const char *cwd;
551 struct variable *top_var; /* = &G.shell_ver (set in main()) */ 690 struct variable *top_var; /* = &G.shell_ver (set in main()) */
552 struct variable shell_ver; 691 struct variable shell_ver;
692 char **expanded_assignments;
553#if ENABLE_HUSH_FUNCTIONS 693#if ENABLE_HUSH_FUNCTIONS
554 struct function *top_func; 694 struct function *top_func;
555# if ENABLE_HUSH_LOCAL 695# if ENABLE_HUSH_LOCAL
@@ -1217,7 +1357,7 @@ static void hush_exit(int exitcode)
1217 1357
1218static int check_and_run_traps(int sig) 1358static int check_and_run_traps(int sig)
1219{ 1359{
1220 static const struct timespec zero_timespec = { 0, 0 }; 1360 static const struct timespec zero_timespec;
1221 smalluint save_rcode; 1361 smalluint save_rcode;
1222 int last_sig = 0; 1362 int last_sig = 0;
1223 1363
@@ -1321,9 +1461,23 @@ static struct variable *get_local_var(const char *name)
1321 1461
1322static const char* FAST_FUNC get_local_var_value(const char *name) 1462static const char* FAST_FUNC get_local_var_value(const char *name)
1323{ 1463{
1324 struct variable **pp = get_ptr_to_local_var(name); 1464 struct variable **vpp;
1325 if (pp) 1465
1326 return strchr((*pp)->varstr, '=') + 1; 1466 if (G.expanded_assignments) {
1467 char **cpp = G.expanded_assignments;
1468 int len = strlen(name);
1469 while (*cpp) {
1470 char *cp = *cpp;
1471 if (strncmp(cp, name, len) == 0 && cp[len] == '=')
1472 return cp + len + 1;
1473 cpp++;
1474 }
1475 }
1476
1477 vpp = get_ptr_to_local_var(name);
1478 if (vpp)
1479 return strchr((*vpp)->varstr, '=') + 1;
1480
1327 if (strcmp(name, "PPID") == 0) 1481 if (strcmp(name, "PPID") == 0)
1328 return utoa(G.root_ppid); 1482 return utoa(G.root_ppid);
1329 // bash compat: UID? EUID? 1483 // bash compat: UID? EUID?
@@ -2683,7 +2837,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2683 } 2837 }
2684 } 2838 }
2685 } else if (exp_op == ':') { 2839 } else if (exp_op == ':') {
2686#if ENABLE_HUSH_BASH_COMPAT 2840#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT
2687 /* It's ${var:N[:M]} bashism. 2841 /* It's ${var:N[:M]} bashism.
2688 * Note that in encoded form it has TWO parts: 2842 * Note that in encoded form it has TWO parts:
2689 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> 2843 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
@@ -2952,11 +3106,14 @@ static char* expand_strvec_to_string(char **argv)
2952static char **expand_assignments(char **argv, int count) 3106static char **expand_assignments(char **argv, int count)
2953{ 3107{
2954 int i; 3108 int i;
2955 char **p = NULL; 3109 char **p;
3110
3111 G.expanded_assignments = p = NULL;
2956 /* Expand assignments into one string each */ 3112 /* Expand assignments into one string each */
2957 for (i = 0; i < count; i++) { 3113 for (i = 0; i < count; i++) {
2958 p = add_string_to_strings(p, expand_string_to_string(argv[i])); 3114 G.expanded_assignments = p = add_string_to_strings(p, expand_string_to_string(argv[i]));
2959 } 3115 }
3116 G.expanded_assignments = NULL;
2960 return p; 3117 return p;
2961} 3118}
2962 3119
@@ -3210,15 +3367,11 @@ static void setup_heredoc(struct redir_struct *redir)
3210#if !BB_MMU 3367#if !BB_MMU
3211 to_free = NULL; 3368 to_free = NULL;
3212#endif 3369#endif
3213 pid = vfork(); 3370 pid = xvfork();
3214 if (pid < 0)
3215 bb_perror_msg_and_die("vfork");
3216 if (pid == 0) { 3371 if (pid == 0) {
3217 /* child */ 3372 /* child */
3218 disable_restore_tty_pgrp_on_exit(); 3373 disable_restore_tty_pgrp_on_exit();
3219 pid = BB_MMU ? fork() : vfork(); 3374 pid = BB_MMU ? xfork() : xvfork();
3220 if (pid < 0)
3221 bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
3222 if (pid != 0) 3375 if (pid != 0)
3223 _exit(0); 3376 _exit(0);
3224 /* grandchild */ 3377 /* grandchild */
@@ -3694,6 +3847,35 @@ static void execvp_or_die(char **argv)
3694 _exit(127); /* bash compat */ 3847 _exit(127); /* bash compat */
3695} 3848}
3696 3849
3850#if ENABLE_HUSH_MODE_X
3851static void dump_cmd_in_x_mode(char **argv)
3852{
3853 if (G_x_mode && argv) {
3854 /* We want to output the line in one write op */
3855 char *buf, *p;
3856 int len;
3857 int n;
3858
3859 len = 3;
3860 n = 0;
3861 while (argv[n])
3862 len += strlen(argv[n++]) + 1;
3863 buf = xmalloc(len);
3864 buf[0] = '+';
3865 p = buf + 1;
3866 n = 0;
3867 while (argv[n])
3868 p += sprintf(p, " %s", argv[n++]);
3869 *p++ = '\n';
3870 *p = '\0';
3871 fputs(buf, stderr);
3872 free(buf);
3873 }
3874}
3875#else
3876# define dump_cmd_in_x_mode(argv) ((void)0)
3877#endif
3878
3697#if BB_MMU 3879#if BB_MMU
3698#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ 3880#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
3699 pseudo_exec_argv(argv, assignment_cnt, argv_expanded) 3881 pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
@@ -3715,11 +3897,18 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
3715{ 3897{
3716 char **new_env; 3898 char **new_env;
3717 3899
3718 /* Case when we are here: ... | var=val | ... */ 3900 new_env = expand_assignments(argv, assignment_cnt);
3719 if (!argv[assignment_cnt]) 3901 dump_cmd_in_x_mode(new_env);
3902
3903 if (!argv[assignment_cnt]) {
3904 /* Case when we are here: ... | var=val | ...
3905 * (note that we do not exit early, i.e., do not optimize out
3906 * expand_assignments(): think about ... | var=`sleep 1` | ...
3907 */
3908 free_strings(new_env);
3720 _exit(EXIT_SUCCESS); 3909 _exit(EXIT_SUCCESS);
3910 }
3721 3911
3722 new_env = expand_assignments(argv, assignment_cnt);
3723#if BB_MMU 3912#if BB_MMU
3724 set_vars_and_save_old(new_env); 3913 set_vars_and_save_old(new_env);
3725 free(new_env); /* optional */ 3914 free(new_env); /* optional */
@@ -3729,6 +3918,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
3729 nommu_save->new_env = new_env; 3918 nommu_save->new_env = new_env;
3730 nommu_save->old_vars = set_vars_and_save_old(new_env); 3919 nommu_save->old_vars = set_vars_and_save_old(new_env);
3731#endif 3920#endif
3921
3732 if (argv_expanded) { 3922 if (argv_expanded) {
3733 argv = argv_expanded; 3923 argv = argv_expanded;
3734 } else { 3924 } else {
@@ -3737,6 +3927,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
3737 nommu_save->argv = argv; 3927 nommu_save->argv = argv;
3738#endif 3928#endif
3739 } 3929 }
3930 dump_cmd_in_x_mode(argv);
3740 3931
3741#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU 3932#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
3742 if (strchr(argv[0], '/') != NULL) 3933 if (strchr(argv[0], '/') != NULL)
@@ -4144,15 +4335,36 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
4144 * backgrounded: cmd & { list } & 4335 * backgrounded: cmd & { list } &
4145 * subshell: ( list ) [&] 4336 * subshell: ( list ) [&]
4146 */ 4337 */
4338#if !ENABLE_HUSH_MODE_X
4339#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, char argv_expanded) \
4340 redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel)
4341#endif
4342static int redirect_and_varexp_helper(char ***new_env_p, struct variable **old_vars_p, struct command *command, int squirrel[3], char **argv_expanded)
4343{
4344 /* setup_redirects acts on file descriptors, not FILEs.
4345 * This is perfect for work that comes after exec().
4346 * Is it really safe for inline use? Experimentally,
4347 * things seem to work. */
4348 int rcode = setup_redirects(command, squirrel);
4349 if (rcode == 0) {
4350 char **new_env = expand_assignments(command->argv, command->assignment_cnt);
4351 *new_env_p = new_env;
4352 dump_cmd_in_x_mode(new_env);
4353 dump_cmd_in_x_mode(argv_expanded);
4354 if (old_vars_p)
4355 *old_vars_p = set_vars_and_save_old(new_env);
4356 }
4357 return rcode;
4358}
4147static NOINLINE int run_pipe(struct pipe *pi) 4359static NOINLINE int run_pipe(struct pipe *pi)
4148{ 4360{
4149 static const char *const null_ptr = NULL; 4361 static const char *const null_ptr = NULL;
4150 int i; 4362
4151 int nextin; 4363 int cmd_no;
4364 int next_infd;
4152 struct command *command; 4365 struct command *command;
4153 char **argv_expanded; 4366 char **argv_expanded;
4154 char **argv; 4367 char **argv;
4155 char *p;
4156 /* it is not always needed, but we aim to smaller code */ 4368 /* it is not always needed, but we aim to smaller code */
4157 int squirrel[] = { -1, -1, -1 }; 4369 int squirrel[] = { -1, -1, -1 };
4158 int rcode; 4370 int rcode;
@@ -4162,7 +4374,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4162 4374
4163 IF_HUSH_JOB(pi->pgrp = -1;) 4375 IF_HUSH_JOB(pi->pgrp = -1;)
4164 pi->stopped_cmds = 0; 4376 pi->stopped_cmds = 0;
4165 command = &(pi->cmds[0]); 4377 command = &pi->cmds[0];
4166 argv_expanded = NULL; 4378 argv_expanded = NULL;
4167 4379
4168 if (pi->num_cmds != 1 4380 if (pi->num_cmds != 1
@@ -4229,29 +4441,59 @@ static NOINLINE int run_pipe(struct pipe *pi)
4229 if (argv[command->assignment_cnt] == NULL) { 4441 if (argv[command->assignment_cnt] == NULL) {
4230 /* Assignments, but no command */ 4442 /* Assignments, but no command */
4231 /* Ensure redirects take effect (that is, create files). 4443 /* Ensure redirects take effect (that is, create files).
4232 * Try "a=t >file": */ 4444 * Try "a=t >file" */
4445#if 0 /* A few cases in testsuite fail with this code. FIXME */
4446 rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, squirrel, /*argv_expanded:*/ NULL);
4447 /* Set shell variables */
4448 if (new_env) {
4449 argv = new_env;
4450 while (*argv) {
4451 set_local_var(*argv, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
4452 /* Do we need to flag set_local_var() errors?
4453 * "assignment to readonly var" and "putenv error"
4454 */
4455 argv++;
4456 }
4457 }
4458 /* Redirect error sets $? to 1. Otherwise,
4459 * if evaluating assignment value set $?, retain it.
4460 * Try "false; q=`exit 2`; echo $?" - should print 2: */
4461 if (rcode == 0)
4462 rcode = G.last_exitcode;
4463 /* Exit, _skipping_ variable restoring code: */
4464 goto clean_up_and_ret0;
4465
4466#else /* Older, bigger, but more correct code */
4467
4233 rcode = setup_redirects(command, squirrel); 4468 rcode = setup_redirects(command, squirrel);
4234 restore_redirects(squirrel); 4469 restore_redirects(squirrel);
4235 /* Set shell variables */ 4470 /* Set shell variables */
4471 if (G_x_mode)
4472 bb_putchar_stderr('+');
4236 while (*argv) { 4473 while (*argv) {
4237 p = expand_string_to_string(*argv); 4474 char *p = expand_string_to_string(*argv);
4475 if (G_x_mode)
4476 fprintf(stderr, " %s", p);
4238 debug_printf_exec("set shell var:'%s'->'%s'\n", 4477 debug_printf_exec("set shell var:'%s'->'%s'\n",
4239 *argv, p); 4478 *argv, p);
4240 set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); 4479 set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
4480 /* Do we need to flag set_local_var() errors?
4481 * "assignment to readonly var" and "putenv error"
4482 */
4241 argv++; 4483 argv++;
4242 } 4484 }
4243 /* Redirect error sets $? to 1. Othervise, 4485 if (G_x_mode)
4486 bb_putchar_stderr('\n');
4487 /* Redirect error sets $? to 1. Otherwise,
4244 * if evaluating assignment value set $?, retain it. 4488 * if evaluating assignment value set $?, retain it.
4245 * Try "false; q=`exit 2`; echo $?" - should print 2: */ 4489 * Try "false; q=`exit 2`; echo $?" - should print 2: */
4246 if (rcode == 0) 4490 if (rcode == 0)
4247 rcode = G.last_exitcode; 4491 rcode = G.last_exitcode;
4248 /* Do we need to flag set_local_var() errors?
4249 * "assignment to readonly var" and "putenv error"
4250 */
4251 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 4492 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
4252 debug_leave(); 4493 debug_leave();
4253 debug_printf_exec("run_pipe: return %d\n", rcode); 4494 debug_printf_exec("run_pipe: return %d\n", rcode);
4254 return rcode; 4495 return rcode;
4496#endif
4255 } 4497 }
4256 4498
4257 /* Expand the rest into (possibly) many strings each */ 4499 /* Expand the rest into (possibly) many strings each */
@@ -4292,14 +4534,8 @@ static NOINLINE int run_pipe(struct pipe *pi)
4292 goto clean_up_and_ret1; 4534 goto clean_up_and_ret1;
4293 } 4535 }
4294 } 4536 }
4295 /* setup_redirects acts on file descriptors, not FILEs. 4537 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
4296 * This is perfect for work that comes after exec().
4297 * Is it really safe for inline use? Experimentally,
4298 * things seem to work. */
4299 rcode = setup_redirects(command, squirrel);
4300 if (rcode == 0) { 4538 if (rcode == 0) {
4301 new_env = expand_assignments(argv, command->assignment_cnt);
4302 old_vars = set_vars_and_save_old(new_env);
4303 if (!funcp) { 4539 if (!funcp) {
4304 debug_printf_exec(": builtin '%s' '%s'...\n", 4540 debug_printf_exec(": builtin '%s' '%s'...\n",
4305 x->b_cmd, argv_expanded[1]); 4541 x->b_cmd, argv_expanded[1]);
@@ -4322,12 +4558,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
4322 } 4558 }
4323#endif 4559#endif
4324 } 4560 }
4325#if ENABLE_FEATURE_SH_STANDALONE
4326 clean_up_and_ret: 4561 clean_up_and_ret:
4327#endif
4328 restore_redirects(squirrel);
4329 unset_vars(new_env); 4562 unset_vars(new_env);
4330 add_vars(old_vars); 4563 add_vars(old_vars);
4564/* clean_up_and_ret0: */
4565 restore_redirects(squirrel);
4331 clean_up_and_ret1: 4566 clean_up_and_ret1:
4332 free(argv_expanded); 4567 free(argv_expanded);
4333 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 4568 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
@@ -4336,20 +4571,18 @@ static NOINLINE int run_pipe(struct pipe *pi)
4336 return rcode; 4571 return rcode;
4337 } 4572 }
4338 4573
4339#if ENABLE_FEATURE_SH_STANDALONE 4574 if (ENABLE_FEATURE_SH_STANDALONE) {
4340 i = find_applet_by_name(argv_expanded[0]); 4575 int n = find_applet_by_name(argv_expanded[0]);
4341 if (i >= 0 && APPLET_IS_NOFORK(i)) { 4576 if (n >= 0 && APPLET_IS_NOFORK(n)) {
4342 rcode = setup_redirects(command, squirrel); 4577 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
4343 if (rcode == 0) { 4578 if (rcode == 0) {
4344 new_env = expand_assignments(argv, command->assignment_cnt); 4579 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
4345 old_vars = set_vars_and_save_old(new_env); 4580 argv_expanded[0], argv_expanded[1]);
4346 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", 4581 rcode = run_nofork_applet(n, argv_expanded);
4347 argv_expanded[0], argv_expanded[1]); 4582 }
4348 rcode = run_nofork_applet(i, argv_expanded); 4583 goto clean_up_and_ret;
4349 } 4584 }
4350 goto clean_up_and_ret;
4351 } 4585 }
4352#endif
4353 /* It is neither builtin nor applet. We must fork. */ 4586 /* It is neither builtin nor applet. We must fork. */
4354 } 4587 }
4355 4588
@@ -4360,9 +4593,10 @@ static NOINLINE int run_pipe(struct pipe *pi)
4360 4593
4361 /* Going to fork a child per each pipe member */ 4594 /* Going to fork a child per each pipe member */
4362 pi->alive_cmds = 0; 4595 pi->alive_cmds = 0;
4363 nextin = 0; 4596 next_infd = 0;
4364 4597
4365 for (i = 0; i < pi->num_cmds; i++) { 4598 cmd_no = 0;
4599 while (cmd_no < pi->num_cmds) {
4366 struct fd_pair pipefds; 4600 struct fd_pair pipefds;
4367#if !BB_MMU 4601#if !BB_MMU
4368 volatile nommu_save_t nommu_save; 4602 volatile nommu_save_t nommu_save;
@@ -4371,7 +4605,8 @@ static NOINLINE int run_pipe(struct pipe *pi)
4371 nommu_save.argv = NULL; 4605 nommu_save.argv = NULL;
4372 nommu_save.argv_from_re_execing = NULL; 4606 nommu_save.argv_from_re_execing = NULL;
4373#endif 4607#endif
4374 command = &(pi->cmds[i]); 4608 command = &pi->cmds[cmd_no];
4609 cmd_no++;
4375 if (command->argv) { 4610 if (command->argv) {
4376 debug_printf_exec(": pipe member '%s' '%s'...\n", 4611 debug_printf_exec(": pipe member '%s' '%s'...\n",
4377 command->argv[0], command->argv[1]); 4612 command->argv[0], command->argv[1]);
@@ -4382,7 +4617,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4382 /* pipes are inserted between pairs of commands */ 4617 /* pipes are inserted between pairs of commands */
4383 pipefds.rd = 0; 4618 pipefds.rd = 0;
4384 pipefds.wr = 1; 4619 pipefds.wr = 1;
4385 if ((i + 1) < pi->num_cmds) 4620 if (cmd_no < pi->num_cmds)
4386 xpiped_pair(pipefds); 4621 xpiped_pair(pipefds);
4387 4622
4388 command->pid = BB_MMU ? fork() : vfork(); 4623 command->pid = BB_MMU ? fork() : vfork();
@@ -4415,7 +4650,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4415 if (open(bb_dev_null, O_RDONLY)) 4650 if (open(bb_dev_null, O_RDONLY))
4416 xopen("/", O_RDONLY); 4651 xopen("/", O_RDONLY);
4417 } else { 4652 } else {
4418 xmove_fd(nextin, 0); 4653 xmove_fd(next_infd, 0);
4419 } 4654 }
4420 xmove_fd(pipefds.wr, 1); 4655 xmove_fd(pipefds.wr, 1);
4421 if (pipefds.rd > 1) 4656 if (pipefds.rd > 1)
@@ -4452,7 +4687,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4452 argv_expanded = NULL; 4687 argv_expanded = NULL;
4453 if (command->pid < 0) { /* [v]fork failed */ 4688 if (command->pid < 0) { /* [v]fork failed */
4454 /* Clearly indicate, was it fork or vfork */ 4689 /* Clearly indicate, was it fork or vfork */
4455 bb_perror_msg(BB_MMU ? "fork" : "vfork"); 4690 bb_perror_msg(BB_MMU ? "vfork"+1 : "vfork");
4456 } else { 4691 } else {
4457 pi->alive_cmds++; 4692 pi->alive_cmds++;
4458#if ENABLE_HUSH_JOB 4693#if ENABLE_HUSH_JOB
@@ -4462,12 +4697,12 @@ static NOINLINE int run_pipe(struct pipe *pi)
4462#endif 4697#endif
4463 } 4698 }
4464 4699
4465 if (i) 4700 if (cmd_no > 1)
4466 close(nextin); 4701 close(next_infd);
4467 if ((i + 1) < pi->num_cmds) 4702 if (cmd_no < pi->num_cmds)
4468 close(pipefds.wr); 4703 close(pipefds.wr);
4469 /* Pass read (output) pipe end to next iteration */ 4704 /* Pass read (output) pipe end to next iteration */
4470 nextin = pipefds.rd; 4705 next_infd = pipefds.rd;
4471 } 4706 }
4472 4707
4473 if (!pi->alive_cmds) { 4708 if (!pi->alive_cmds) {
@@ -4899,7 +5134,7 @@ static int run_and_free_list(struct pipe *pi)
4899{ 5134{
4900 int rcode = 0; 5135 int rcode = 0;
4901 debug_printf_exec("run_and_free_list entered\n"); 5136 debug_printf_exec("run_and_free_list entered\n");
4902 if (!G.fake_mode) { 5137 if (!G.n_mode) {
4903 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds); 5138 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
4904 rcode = run_list(pi); 5139 rcode = run_list(pi);
4905 } 5140 }
@@ -5588,10 +5823,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
5588# endif 5823# endif
5589 5824
5590 xpipe(channel); 5825 xpipe(channel);
5591 pid = BB_MMU ? fork() : vfork(); 5826 pid = BB_MMU ? xfork() : xvfork();
5592 if (pid < 0)
5593 bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
5594
5595 if (pid == 0) { /* child */ 5827 if (pid == 0) { /* child */
5596 disable_restore_tty_pgrp_on_exit(); 5828 disable_restore_tty_pgrp_on_exit();
5597 /* Process substitution is not considered to be usual 5829 /* Process substitution is not considered to be usual
@@ -5829,7 +6061,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
5829 /* command remains "open", available for possible redirects */ 6061 /* command remains "open", available for possible redirects */
5830} 6062}
5831 6063
5832#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT 6064#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
5833/* Subroutines for copying $(...) and `...` things */ 6065/* Subroutines for copying $(...) and `...` things */
5834static void add_till_backquote(o_string *dest, struct in_str *input); 6066static void add_till_backquote(o_string *dest, struct in_str *input);
5835/* '...' */ 6067/* '...' */
@@ -5930,9 +6162,9 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
5930{ 6162{
5931 int ch; 6163 int ch;
5932 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; 6164 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
5933#if ENABLE_HUSH_BASH_COMPAT 6165# if ENABLE_HUSH_BASH_COMPAT
5934 char end_char2 = end_ch >> 8; 6166 char end_char2 = end_ch >> 8;
5935#endif 6167# endif
5936 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); 6168 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
5937 6169
5938 while (1) { 6170 while (1) {
@@ -5985,7 +6217,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
5985 } 6217 }
5986 return ch; 6218 return ch;
5987} 6219}
5988#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */ 6220#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */
5989 6221
5990/* Return code: 0 for OK, 1 for syntax error */ 6222/* Return code: 0 for OK, 1 for syntax error */
5991#if BB_MMU 6223#if BB_MMU
@@ -6091,7 +6323,11 @@ static int parse_dollar(o_string *as_string,
6091 again: 6323 again:
6092 if (!BB_MMU) 6324 if (!BB_MMU)
6093 pos = dest->length; 6325 pos = dest->length;
6326#if ENABLE_HUSH_DOLLAR_OPS
6094 last_ch = add_till_closing_bracket(dest, input, end_ch); 6327 last_ch = add_till_closing_bracket(dest, input, end_ch);
6328#else
6329#error Simple code to only allow ${var} is not implemented
6330#endif
6095 if (as_string) { 6331 if (as_string) {
6096 o_addstr(as_string, dest->data + pos); 6332 o_addstr(as_string, dest->data + pos);
6097 o_addchr(as_string, last_ch); 6333 o_addchr(as_string, last_ch);
@@ -6924,8 +7160,8 @@ static int set_mode(const char cstate, const char mode)
6924{ 7160{
6925 int state = (cstate == '-' ? 1 : 0); 7161 int state = (cstate == '-' ? 1 : 0);
6926 switch (mode) { 7162 switch (mode) {
6927 case 'n': G.fake_mode = state; break; 7163 case 'n': G.n_mode = state; break;
6928 case 'x': /*G.debug_mode = state;*/ break; 7164 case 'x': IF_HUSH_MODE_X(G_x_mode = state;) break;
6929 default: return EXIT_FAILURE; 7165 default: return EXIT_FAILURE;
6930 } 7166 }
6931 return EXIT_SUCCESS; 7167 return EXIT_SUCCESS;
@@ -7154,7 +7390,7 @@ int hush_main(int argc, char **argv)
7154#endif 7390#endif
7155 case 'n': 7391 case 'n':
7156 case 'x': 7392 case 'x':
7157 if (!set_mode('-', opt)) 7393 if (set_mode('-', opt) == 0) /* no error */
7158 break; 7394 break;
7159 default: 7395 default:
7160#ifndef BB_VER 7396#ifndef BB_VER