aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/hush.c108
-rw-r--r--shell/hush_test/hush-vars/var_serial.right5
-rwxr-xr-xshell/hush_test/hush-vars/var_serial.tests22
3 files changed, 107 insertions, 28 deletions
diff --git a/shell/hush.c b/shell/hush.c
index c67aebdd7..c5a8ea617 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -221,7 +221,8 @@
221//config: default y 221//config: default y
222//config: depends on HUSH 222//config: depends on HUSH
223//config: help 223//config: help
224//config: This instructs hush to print commands before execution. Adds ~300 bytes. 224//config: This instructs hush to print commands before execution.
225//config: Adds ~300 bytes.
225//config: 226//config:
226 227
227//usage:#define hush_trivial_usage NOUSAGE_STR 228//usage:#define hush_trivial_usage NOUSAGE_STR
@@ -664,7 +665,7 @@ struct globals {
664 smallint n_mode; 665 smallint n_mode;
665#if ENABLE_HUSH_MODE_X 666#if ENABLE_HUSH_MODE_X
666 smallint x_mode; 667 smallint x_mode;
667# define G_x_mode G.x_mode 668# define G_x_mode (G.x_mode)
668#else 669#else
669# define G_x_mode 0 670# define G_x_mode 0
670#endif 671#endif
@@ -688,6 +689,7 @@ struct globals {
688 const char *cwd; 689 const char *cwd;
689 struct variable *top_var; /* = &G.shell_ver (set in main()) */ 690 struct variable *top_var; /* = &G.shell_ver (set in main()) */
690 struct variable shell_ver; 691 struct variable shell_ver;
692 char **expanded_assignments;
691#if ENABLE_HUSH_FUNCTIONS 693#if ENABLE_HUSH_FUNCTIONS
692 struct function *top_func; 694 struct function *top_func;
693# if ENABLE_HUSH_LOCAL 695# if ENABLE_HUSH_LOCAL
@@ -1459,9 +1461,23 @@ static struct variable *get_local_var(const char *name)
1459 1461
1460static const char* FAST_FUNC get_local_var_value(const char *name) 1462static const char* FAST_FUNC get_local_var_value(const char *name)
1461{ 1463{
1462 struct variable **pp = get_ptr_to_local_var(name); 1464 struct variable **vpp;
1463 if (pp) 1465
1464 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
1465 if (strcmp(name, "PPID") == 0) 1481 if (strcmp(name, "PPID") == 0)
1466 return utoa(G.root_ppid); 1482 return utoa(G.root_ppid);
1467 // bash compat: UID? EUID? 1483 // bash compat: UID? EUID?
@@ -3090,11 +3106,14 @@ static char* expand_strvec_to_string(char **argv)
3090static char **expand_assignments(char **argv, int count) 3106static char **expand_assignments(char **argv, int count)
3091{ 3107{
3092 int i; 3108 int i;
3093 char **p = NULL; 3109 char **p;
3110
3111 G.expanded_assignments = p = NULL;
3094 /* Expand assignments into one string each */ 3112 /* Expand assignments into one string each */
3095 for (i = 0; i < count; i++) { 3113 for (i = 0; i < count; i++) {
3096 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]));
3097 } 3115 }
3116 G.expanded_assignments = NULL;
3098 return p; 3117 return p;
3099} 3118}
3100 3119
@@ -4316,6 +4335,27 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
4316 * backgrounded: cmd & { list } & 4335 * backgrounded: cmd & { list } &
4317 * subshell: ( list ) [&] 4336 * subshell: ( list ) [&]
4318 */ 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}
4319static NOINLINE int run_pipe(struct pipe *pi) 4359static NOINLINE int run_pipe(struct pipe *pi)
4320{ 4360{
4321 static const char *const null_ptr = NULL; 4361 static const char *const null_ptr = NULL;
@@ -4325,7 +4365,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
4325 struct command *command; 4365 struct command *command;
4326 char **argv_expanded; 4366 char **argv_expanded;
4327 char **argv; 4367 char **argv;
4328 char *p;
4329 /* it is not always needed, but we aim to smaller code */ 4368 /* it is not always needed, but we aim to smaller code */
4330 int squirrel[] = { -1, -1, -1 }; 4369 int squirrel[] = { -1, -1, -1 };
4331 int rcode; 4370 int rcode;
@@ -4402,19 +4441,45 @@ static NOINLINE int run_pipe(struct pipe *pi)
4402 if (argv[command->assignment_cnt] == NULL) { 4441 if (argv[command->assignment_cnt] == NULL) {
4403 /* Assignments, but no command */ 4442 /* Assignments, but no command */
4404 /* Ensure redirects take effect (that is, create files). 4443 /* Ensure redirects take effect (that is, create files).
4405 * 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
4406 rcode = setup_redirects(command, squirrel); 4468 rcode = setup_redirects(command, squirrel);
4407 restore_redirects(squirrel); 4469 restore_redirects(squirrel);
4408 /* Set shell variables */ 4470 /* Set shell variables */
4409 if (G_x_mode) 4471 if (G_x_mode)
4410 bb_putchar_stderr('+'); 4472 bb_putchar_stderr('+');
4411 while (*argv) { 4473 while (*argv) {
4412 p = expand_string_to_string(*argv); 4474 char *p = expand_string_to_string(*argv);
4413 if (G_x_mode) 4475 if (G_x_mode)
4414 fprintf(stderr, " %s", p); 4476 fprintf(stderr, " %s", p);
4415 debug_printf_exec("set shell var:'%s'->'%s'\n", 4477 debug_printf_exec("set shell var:'%s'->'%s'\n",
4416 *argv, p); 4478 *argv, p);
4417 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 */
4418 argv++; 4483 argv++;
4419 } 4484 }
4420 if (G_x_mode) 4485 if (G_x_mode)
@@ -4424,13 +4489,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
4424 * Try "false; q=`exit 2`; echo $?" - should print 2: */ 4489 * Try "false; q=`exit 2`; echo $?" - should print 2: */
4425 if (rcode == 0) 4490 if (rcode == 0)
4426 rcode = G.last_exitcode; 4491 rcode = G.last_exitcode;
4427 /* Do we need to flag set_local_var() errors?
4428 * "assignment to readonly var" and "putenv error"
4429 */
4430 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 4492 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
4431 debug_leave(); 4493 debug_leave();
4432 debug_printf_exec("run_pipe: return %d\n", rcode); 4494 debug_printf_exec("run_pipe: return %d\n", rcode);
4433 return rcode; 4495 return rcode;
4496#endif
4434 } 4497 }
4435 4498
4436 /* Expand the rest into (possibly) many strings each */ 4499 /* Expand the rest into (possibly) many strings each */
@@ -4471,16 +4534,8 @@ static NOINLINE int run_pipe(struct pipe *pi)
4471 goto clean_up_and_ret1; 4534 goto clean_up_and_ret1;
4472 } 4535 }
4473 } 4536 }
4474 /* setup_redirects acts on file descriptors, not FILEs. 4537 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
4475 * This is perfect for work that comes after exec().
4476 * Is it really safe for inline use? Experimentally,
4477 * things seem to work. */
4478 rcode = setup_redirects(command, squirrel);
4479 if (rcode == 0) { 4538 if (rcode == 0) {
4480 new_env = expand_assignments(argv, command->assignment_cnt);
4481 dump_cmd_in_x_mode(new_env);
4482 dump_cmd_in_x_mode(argv_expanded);
4483 old_vars = set_vars_and_save_old(new_env);
4484 if (!funcp) { 4539 if (!funcp) {
4485 debug_printf_exec(": builtin '%s' '%s'...\n", 4540 debug_printf_exec(": builtin '%s' '%s'...\n",
4486 x->b_cmd, argv_expanded[1]); 4541 x->b_cmd, argv_expanded[1]);
@@ -4504,9 +4559,10 @@ static NOINLINE int run_pipe(struct pipe *pi)
4504#endif 4559#endif
4505 } 4560 }
4506 clean_up_and_ret: 4561 clean_up_and_ret:
4507 restore_redirects(squirrel);
4508 unset_vars(new_env); 4562 unset_vars(new_env);
4509 add_vars(old_vars); 4563 add_vars(old_vars);
4564/* clean_up_and_ret0: */
4565 restore_redirects(squirrel);
4510 clean_up_and_ret1: 4566 clean_up_and_ret1:
4511 free(argv_expanded); 4567 free(argv_expanded);
4512 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 4568 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
@@ -4518,12 +4574,8 @@ static NOINLINE int run_pipe(struct pipe *pi)
4518 if (ENABLE_FEATURE_SH_STANDALONE) { 4574 if (ENABLE_FEATURE_SH_STANDALONE) {
4519 int n = find_applet_by_name(argv_expanded[0]); 4575 int n = find_applet_by_name(argv_expanded[0]);
4520 if (n >= 0 && APPLET_IS_NOFORK(n)) { 4576 if (n >= 0 && APPLET_IS_NOFORK(n)) {
4521 rcode = setup_redirects(command, squirrel); 4577 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
4522 if (rcode == 0) { 4578 if (rcode == 0) {
4523 new_env = expand_assignments(argv, command->assignment_cnt);
4524 dump_cmd_in_x_mode(new_env);
4525 dump_cmd_in_x_mode(argv_expanded);
4526 old_vars = set_vars_and_save_old(new_env);
4527 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", 4579 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
4528 argv_expanded[0], argv_expanded[1]); 4580 argv_expanded[0], argv_expanded[1]);
4529 rcode = run_nofork_applet(n, argv_expanded); 4581 rcode = run_nofork_applet(n, argv_expanded);
diff --git a/shell/hush_test/hush-vars/var_serial.right b/shell/hush_test/hush-vars/var_serial.right
new file mode 100644
index 000000000..42aa33057
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_serial.right
@@ -0,0 +1,5 @@
1Assignments only: c=a
2Assignments and a command: c=a
3Assignments and a builtin: c=a
4Assignments and a function: c=a
5Done
diff --git a/shell/hush_test/hush-vars/var_serial.tests b/shell/hush_test/hush-vars/var_serial.tests
new file mode 100755
index 000000000..6b4a4cdf7
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_serial.tests
@@ -0,0 +1,22 @@
1a=a
2
3b=b
4c=c
5# Second assignment depends on the first:
6b=$a c=$b
7echo Assignments only: c=$c
8
9b=b
10c=c
11b=$a c=$b "$THIS_SH" -c 'echo Assignments and a command: c=$c'
12
13b=b
14c=c
15b=$a c=$b eval 'echo Assignments and a builtin: c=$c'
16
17b=b
18c=c
19f() { echo Assignments and a function: c=$c; }
20b=$a c=$b f
21
22echo Done