mirror of
https://github.com/sheumann/hush.git
synced 2024-08-20 04:28:57 +00:00
hush: fix multimple dependent variable expansion cases
function old new delta get_local_var_value 100 171 +71 expand_assignments 46 76 +30 reset_traps_to_defaults 229 238 +9 maybe_set_to_sigexit 47 50 +3 init_sigmasks 211 214 +3 builtin_trap 462 465 +3 expand_vars_to_list 2412 2408 -4 run_pipe 1568 1533 -35 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 6/2 up/down: 119/-39) Total: 80 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
202a2d1219
commit
29082231d0
108
shell/hush.c
108
shell/hush.c
@ -221,7 +221,8 @@
|
|||||||
//config: default y
|
//config: default y
|
||||||
//config: depends on HUSH
|
//config: depends on HUSH
|
||||||
//config: help
|
//config: help
|
||||||
//config: This instructs hush to print commands before execution. Adds ~300 bytes.
|
//config: This instructs hush to print commands before execution.
|
||||||
|
//config: Adds ~300 bytes.
|
||||||
//config:
|
//config:
|
||||||
|
|
||||||
//usage:#define hush_trivial_usage NOUSAGE_STR
|
//usage:#define hush_trivial_usage NOUSAGE_STR
|
||||||
@ -664,7 +665,7 @@ struct globals {
|
|||||||
smallint n_mode;
|
smallint n_mode;
|
||||||
#if ENABLE_HUSH_MODE_X
|
#if ENABLE_HUSH_MODE_X
|
||||||
smallint x_mode;
|
smallint x_mode;
|
||||||
# define G_x_mode G.x_mode
|
# define G_x_mode (G.x_mode)
|
||||||
#else
|
#else
|
||||||
# define G_x_mode 0
|
# define G_x_mode 0
|
||||||
#endif
|
#endif
|
||||||
@ -688,6 +689,7 @@ struct globals {
|
|||||||
const char *cwd;
|
const char *cwd;
|
||||||
struct variable *top_var; /* = &G.shell_ver (set in main()) */
|
struct variable *top_var; /* = &G.shell_ver (set in main()) */
|
||||||
struct variable shell_ver;
|
struct variable shell_ver;
|
||||||
|
char **expanded_assignments;
|
||||||
#if ENABLE_HUSH_FUNCTIONS
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
struct function *top_func;
|
struct function *top_func;
|
||||||
# if ENABLE_HUSH_LOCAL
|
# if ENABLE_HUSH_LOCAL
|
||||||
@ -1459,9 +1461,23 @@ static struct variable *get_local_var(const char *name)
|
|||||||
|
|
||||||
static const char* FAST_FUNC get_local_var_value(const char *name)
|
static const char* FAST_FUNC get_local_var_value(const char *name)
|
||||||
{
|
{
|
||||||
struct variable **pp = get_ptr_to_local_var(name);
|
struct variable **vpp;
|
||||||
if (pp)
|
|
||||||
return strchr((*pp)->varstr, '=') + 1;
|
if (G.expanded_assignments) {
|
||||||
|
char **cpp = G.expanded_assignments;
|
||||||
|
int len = strlen(name);
|
||||||
|
while (*cpp) {
|
||||||
|
char *cp = *cpp;
|
||||||
|
if (strncmp(cp, name, len) == 0 && cp[len] == '=')
|
||||||
|
return cp + len + 1;
|
||||||
|
cpp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vpp = get_ptr_to_local_var(name);
|
||||||
|
if (vpp)
|
||||||
|
return strchr((*vpp)->varstr, '=') + 1;
|
||||||
|
|
||||||
if (strcmp(name, "PPID") == 0)
|
if (strcmp(name, "PPID") == 0)
|
||||||
return utoa(G.root_ppid);
|
return utoa(G.root_ppid);
|
||||||
// bash compat: UID? EUID?
|
// bash compat: UID? EUID?
|
||||||
@ -3090,11 +3106,14 @@ static char* expand_strvec_to_string(char **argv)
|
|||||||
static char **expand_assignments(char **argv, int count)
|
static char **expand_assignments(char **argv, int count)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
char **p = NULL;
|
char **p;
|
||||||
|
|
||||||
|
G.expanded_assignments = p = NULL;
|
||||||
/* Expand assignments into one string each */
|
/* Expand assignments into one string each */
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
p = add_string_to_strings(p, expand_string_to_string(argv[i]));
|
G.expanded_assignments = p = add_string_to_strings(p, expand_string_to_string(argv[i]));
|
||||||
}
|
}
|
||||||
|
G.expanded_assignments = NULL;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4316,6 +4335,27 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
|
|||||||
* backgrounded: cmd & { list } &
|
* backgrounded: cmd & { list } &
|
||||||
* subshell: ( list ) [&]
|
* subshell: ( list ) [&]
|
||||||
*/
|
*/
|
||||||
|
#if !ENABLE_HUSH_MODE_X
|
||||||
|
#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, char argv_expanded) \
|
||||||
|
redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel)
|
||||||
|
#endif
|
||||||
|
static int redirect_and_varexp_helper(char ***new_env_p, struct variable **old_vars_p, struct command *command, int squirrel[3], char **argv_expanded)
|
||||||
|
{
|
||||||
|
/* setup_redirects acts on file descriptors, not FILEs.
|
||||||
|
* This is perfect for work that comes after exec().
|
||||||
|
* Is it really safe for inline use? Experimentally,
|
||||||
|
* things seem to work. */
|
||||||
|
int rcode = setup_redirects(command, squirrel);
|
||||||
|
if (rcode == 0) {
|
||||||
|
char **new_env = expand_assignments(command->argv, command->assignment_cnt);
|
||||||
|
*new_env_p = new_env;
|
||||||
|
dump_cmd_in_x_mode(new_env);
|
||||||
|
dump_cmd_in_x_mode(argv_expanded);
|
||||||
|
if (old_vars_p)
|
||||||
|
*old_vars_p = set_vars_and_save_old(new_env);
|
||||||
|
}
|
||||||
|
return rcode;
|
||||||
|
}
|
||||||
static NOINLINE int run_pipe(struct pipe *pi)
|
static NOINLINE int run_pipe(struct pipe *pi)
|
||||||
{
|
{
|
||||||
static const char *const null_ptr = NULL;
|
static const char *const null_ptr = NULL;
|
||||||
@ -4325,7 +4365,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
|
|||||||
struct command *command;
|
struct command *command;
|
||||||
char **argv_expanded;
|
char **argv_expanded;
|
||||||
char **argv;
|
char **argv;
|
||||||
char *p;
|
|
||||||
/* it is not always needed, but we aim to smaller code */
|
/* it is not always needed, but we aim to smaller code */
|
||||||
int squirrel[] = { -1, -1, -1 };
|
int squirrel[] = { -1, -1, -1 };
|
||||||
int rcode;
|
int rcode;
|
||||||
@ -4402,19 +4441,45 @@ static NOINLINE int run_pipe(struct pipe *pi)
|
|||||||
if (argv[command->assignment_cnt] == NULL) {
|
if (argv[command->assignment_cnt] == NULL) {
|
||||||
/* Assignments, but no command */
|
/* Assignments, but no command */
|
||||||
/* Ensure redirects take effect (that is, create files).
|
/* Ensure redirects take effect (that is, create files).
|
||||||
* Try "a=t >file": */
|
* Try "a=t >file" */
|
||||||
|
#if 0 /* A few cases in testsuite fail with this code. FIXME */
|
||||||
|
rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, squirrel, /*argv_expanded:*/ NULL);
|
||||||
|
/* Set shell variables */
|
||||||
|
if (new_env) {
|
||||||
|
argv = new_env;
|
||||||
|
while (*argv) {
|
||||||
|
set_local_var(*argv, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
|
||||||
|
/* Do we need to flag set_local_var() errors?
|
||||||
|
* "assignment to readonly var" and "putenv error"
|
||||||
|
*/
|
||||||
|
argv++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Redirect error sets $? to 1. Otherwise,
|
||||||
|
* if evaluating assignment value set $?, retain it.
|
||||||
|
* Try "false; q=`exit 2`; echo $?" - should print 2: */
|
||||||
|
if (rcode == 0)
|
||||||
|
rcode = G.last_exitcode;
|
||||||
|
/* Exit, _skipping_ variable restoring code: */
|
||||||
|
goto clean_up_and_ret0;
|
||||||
|
|
||||||
|
#else /* Older, bigger, but more correct code */
|
||||||
|
|
||||||
rcode = setup_redirects(command, squirrel);
|
rcode = setup_redirects(command, squirrel);
|
||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
/* Set shell variables */
|
/* Set shell variables */
|
||||||
if (G_x_mode)
|
if (G_x_mode)
|
||||||
bb_putchar_stderr('+');
|
bb_putchar_stderr('+');
|
||||||
while (*argv) {
|
while (*argv) {
|
||||||
p = expand_string_to_string(*argv);
|
char *p = expand_string_to_string(*argv);
|
||||||
if (G_x_mode)
|
if (G_x_mode)
|
||||||
fprintf(stderr, " %s", p);
|
fprintf(stderr, " %s", p);
|
||||||
debug_printf_exec("set shell var:'%s'->'%s'\n",
|
debug_printf_exec("set shell var:'%s'->'%s'\n",
|
||||||
*argv, p);
|
*argv, p);
|
||||||
set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
|
set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
|
||||||
|
/* Do we need to flag set_local_var() errors?
|
||||||
|
* "assignment to readonly var" and "putenv error"
|
||||||
|
*/
|
||||||
argv++;
|
argv++;
|
||||||
}
|
}
|
||||||
if (G_x_mode)
|
if (G_x_mode)
|
||||||
@ -4424,13 +4489,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
|
|||||||
* Try "false; q=`exit 2`; echo $?" - should print 2: */
|
* Try "false; q=`exit 2`; echo $?" - should print 2: */
|
||||||
if (rcode == 0)
|
if (rcode == 0)
|
||||||
rcode = G.last_exitcode;
|
rcode = G.last_exitcode;
|
||||||
/* Do we need to flag set_local_var() errors?
|
|
||||||
* "assignment to readonly var" and "putenv error"
|
|
||||||
*/
|
|
||||||
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
|
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
|
||||||
debug_leave();
|
debug_leave();
|
||||||
debug_printf_exec("run_pipe: return %d\n", rcode);
|
debug_printf_exec("run_pipe: return %d\n", rcode);
|
||||||
return rcode;
|
return rcode;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expand the rest into (possibly) many strings each */
|
/* Expand the rest into (possibly) many strings each */
|
||||||
@ -4471,16 +4534,8 @@ static NOINLINE int run_pipe(struct pipe *pi)
|
|||||||
goto clean_up_and_ret1;
|
goto clean_up_and_ret1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* setup_redirects acts on file descriptors, not FILEs.
|
rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
|
||||||
* This is perfect for work that comes after exec().
|
|
||||||
* Is it really safe for inline use? Experimentally,
|
|
||||||
* things seem to work. */
|
|
||||||
rcode = setup_redirects(command, squirrel);
|
|
||||||
if (rcode == 0) {
|
if (rcode == 0) {
|
||||||
new_env = expand_assignments(argv, command->assignment_cnt);
|
|
||||||
dump_cmd_in_x_mode(new_env);
|
|
||||||
dump_cmd_in_x_mode(argv_expanded);
|
|
||||||
old_vars = set_vars_and_save_old(new_env);
|
|
||||||
if (!funcp) {
|
if (!funcp) {
|
||||||
debug_printf_exec(": builtin '%s' '%s'...\n",
|
debug_printf_exec(": builtin '%s' '%s'...\n",
|
||||||
x->b_cmd, argv_expanded[1]);
|
x->b_cmd, argv_expanded[1]);
|
||||||
@ -4504,9 +4559,10 @@ static NOINLINE int run_pipe(struct pipe *pi)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
clean_up_and_ret:
|
clean_up_and_ret:
|
||||||
restore_redirects(squirrel);
|
|
||||||
unset_vars(new_env);
|
unset_vars(new_env);
|
||||||
add_vars(old_vars);
|
add_vars(old_vars);
|
||||||
|
/* clean_up_and_ret0: */
|
||||||
|
restore_redirects(squirrel);
|
||||||
clean_up_and_ret1:
|
clean_up_and_ret1:
|
||||||
free(argv_expanded);
|
free(argv_expanded);
|
||||||
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
|
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
|
||||||
@ -4518,12 +4574,8 @@ static NOINLINE int run_pipe(struct pipe *pi)
|
|||||||
if (ENABLE_FEATURE_SH_STANDALONE) {
|
if (ENABLE_FEATURE_SH_STANDALONE) {
|
||||||
int n = find_applet_by_name(argv_expanded[0]);
|
int n = find_applet_by_name(argv_expanded[0]);
|
||||||
if (n >= 0 && APPLET_IS_NOFORK(n)) {
|
if (n >= 0 && APPLET_IS_NOFORK(n)) {
|
||||||
rcode = setup_redirects(command, squirrel);
|
rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
|
||||||
if (rcode == 0) {
|
if (rcode == 0) {
|
||||||
new_env = expand_assignments(argv, command->assignment_cnt);
|
|
||||||
dump_cmd_in_x_mode(new_env);
|
|
||||||
dump_cmd_in_x_mode(argv_expanded);
|
|
||||||
old_vars = set_vars_and_save_old(new_env);
|
|
||||||
debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
|
debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
|
||||||
argv_expanded[0], argv_expanded[1]);
|
argv_expanded[0], argv_expanded[1]);
|
||||||
rcode = run_nofork_applet(n, argv_expanded);
|
rcode = run_nofork_applet(n, argv_expanded);
|
||||||
|
5
shell/hush_test/hush-vars/var_serial.right
Normal file
5
shell/hush_test/hush-vars/var_serial.right
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Assignments only: c=a
|
||||||
|
Assignments and a command: c=a
|
||||||
|
Assignments and a builtin: c=a
|
||||||
|
Assignments and a function: c=a
|
||||||
|
Done
|
22
shell/hush_test/hush-vars/var_serial.tests
Executable file
22
shell/hush_test/hush-vars/var_serial.tests
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
a=a
|
||||||
|
|
||||||
|
b=b
|
||||||
|
c=c
|
||||||
|
# Second assignment depends on the first:
|
||||||
|
b=$a c=$b
|
||||||
|
echo Assignments only: c=$c
|
||||||
|
|
||||||
|
b=b
|
||||||
|
c=c
|
||||||
|
b=$a c=$b "$THIS_SH" -c 'echo Assignments and a command: c=$c'
|
||||||
|
|
||||||
|
b=b
|
||||||
|
c=c
|
||||||
|
b=$a c=$b eval 'echo Assignments and a builtin: c=$c'
|
||||||
|
|
||||||
|
b=b
|
||||||
|
c=c
|
||||||
|
f() { echo Assignments and a function: c=$c; }
|
||||||
|
b=$a c=$b f
|
||||||
|
|
||||||
|
echo Done
|
Loading…
Reference in New Issue
Block a user