hush: fix corner cases with exec in empty expansions

Cases like these:

var=val exec >redir

var=val `` >redir

function                                             old     new   delta
run_pipe                                            1701    1723     +22
redirect_and_varexp_helper                            56      55      -1
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 22/-1)              Total: 21 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2018-04-05 14:41:21 +02:00
parent 929a41d577
commit 41d8f10813
5 changed files with 35 additions and 17 deletions

View File

@ -0,0 +1,2 @@
redir_exec1.tests: line 1: can't create /cant/be/created: nonexistent directory
First

View File

@ -0,0 +1,2 @@
v=`echo First >&2` exec >/cant/be/created
echo One:$?

View File

@ -8200,19 +8200,21 @@ static int redirect_and_varexp_helper(
struct squirrel **sqp, struct squirrel **sqp,
char **argv_expanded) char **argv_expanded)
{ {
/* setup_redirects acts on file descriptors, not FILEs. /* Assignments occur before redirects. Try:
* This is perfect for work that comes after exec(). * a=`sleep 1` sleep 2 3>/qwe/rty
* Is it really safe for inline use? Experimentally, */
* things seem to work. */
int rcode = setup_redirects(command, sqp);
if (rcode == 0) {
char **new_env = expand_assignments(command->argv, command->assignment_cnt); char **new_env = expand_assignments(command->argv, command->assignment_cnt);
dump_cmd_in_x_mode(new_env); dump_cmd_in_x_mode(new_env);
dump_cmd_in_x_mode(argv_expanded); dump_cmd_in_x_mode(argv_expanded);
/* this takes ownership of new_env[i] elements, and frees new_env: */ /* this takes ownership of new_env[i] elements, and frees new_env: */
set_vars_and_save_old(new_env); set_vars_and_save_old(new_env);
}
return rcode; /* 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. */
return setup_redirects(command, sqp);
} }
static NOINLINE int run_pipe(struct pipe *pi) static NOINLINE int run_pipe(struct pipe *pi)
{ {
@ -8315,6 +8317,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
* Ensure redirects take effect (that is, create files). * Ensure redirects take effect (that is, create files).
* Try "a=t >file" * Try "a=t >file"
*/ */
only_assignments:
G.expand_exitcode = 0; G.expand_exitcode = 0;
rcode = setup_redirects(command, &squirrel); rcode = setup_redirects(command, &squirrel);
@ -8359,12 +8362,10 @@ static NOINLINE int run_pipe(struct pipe *pi)
#endif #endif
argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
/* if someone gives us an empty string: `cmd with empty output` */ /* If someone gives us an empty string: `cmd with empty output` */
//TODO: what about: var=EXPR `` >FILE ? will var be set? Will FILE be created?
if (!argv_expanded[0]) { if (!argv_expanded[0]) {
free(argv_expanded); free(argv_expanded);
debug_leave(); goto only_assignments;
return G.last_exitcode;
} }
old_vars = NULL; old_vars = NULL;
@ -8378,9 +8379,17 @@ static NOINLINE int run_pipe(struct pipe *pi)
if (x || funcp) { if (x || funcp) {
if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) { if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) {
debug_printf("exec with redirects only\n"); debug_printf("exec with redirects only\n");
rcode = setup_redirects(command, NULL); /*
//TODO: what about: var=EXPR exec >FILE ? will var be set? * Variable assignments are executed, but then "forgotten":
* a=`sleep 1;echo A` exec 3>&-; echo $a
* sleeps, but prints nothing.
*/
enter_var_nest_level();
G.shadowed_vars_pp = &old_vars;
rcode = redirect_and_varexp_helper(command, /*squirrel:*/ NULL, argv_expanded);
G.shadowed_vars_pp = sv_shadowed;
/* rcode=1 can be if redir file can't be opened */ /* rcode=1 can be if redir file can't be opened */
goto clean_up_and_ret1; goto clean_up_and_ret1;
} }
@ -8452,9 +8461,10 @@ static NOINLINE int run_pipe(struct pipe *pi)
} else } else
goto must_fork; goto must_fork;
restore_redirects(squirrel);
clean_up_and_ret1:
leave_var_nest_level(); leave_var_nest_level();
add_vars(old_vars); add_vars(old_vars);
restore_redirects(squirrel);
/* /*
* Try "usleep 99999999" + ^C + "echo $?" * Try "usleep 99999999" + ^C + "echo $?"
@ -8474,7 +8484,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
if (sigismember(&G.pending_set, SIGINT)) if (sigismember(&G.pending_set, SIGINT))
rcode = 128 + SIGINT; rcode = 128 + SIGINT;
} }
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;)
debug_leave(); debug_leave();

View File

@ -0,0 +1,3 @@
First
hush: can't open '/cant/be/created': No such file or directory
One:1

View File

@ -0,0 +1,2 @@
v=`echo First >&2` exec >/cant/be/created
echo One:$?