hush: fix "var=val >file" not creating file
function old new delta static.null_ptr - 4 +4 run_list 2018 2020 +2 handle_dollar 667 626 -41 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/1 up/down: 6/-41) Total: -35 bytes
This commit is contained in:
parent
a24c8caeb2
commit
552433bc5a
128
shell/hush.c
128
shell/hush.c
@ -593,7 +593,6 @@ static const struct built_in_command bltins[] = {
|
||||
};
|
||||
|
||||
|
||||
/* Normal */
|
||||
static void maybe_die(const char *notice, const char *msg)
|
||||
{
|
||||
/* Was using fancy stuff:
|
||||
@ -615,6 +614,7 @@ static void maybe_die(const char *notice, const char *msg)
|
||||
#define syntax(msg) _syntax(msg, __LINE__)
|
||||
#endif
|
||||
|
||||
|
||||
static int glob_needed(const char *s)
|
||||
{
|
||||
while (*s) {
|
||||
@ -816,7 +816,7 @@ static void free_strings(char **strings)
|
||||
* sigset_t blocked_set: current blocked signal set
|
||||
*
|
||||
* "trap - SIGxxx":
|
||||
* clear bit in blocked_set unless it is also in non_DFL
|
||||
* clear bit in blocked_set unless it is also in non_DFL_mask
|
||||
* "trap 'cmd' SIGxxx":
|
||||
* set bit in blocked_set (even if 'cmd' is '')
|
||||
* after [v]fork, if we plan to be a shell:
|
||||
@ -2292,24 +2292,33 @@ static void pseudo_exec(nommu_save_t *nommu_save,
|
||||
struct command *command,
|
||||
char **argv_expanded)
|
||||
{
|
||||
if (command->argv)
|
||||
pseudo_exec_argv(nommu_save, command->argv, command->assignment_cnt, argv_expanded);
|
||||
if (command->argv) {
|
||||
pseudo_exec_argv(nommu_save, command->argv,
|
||||
command->assignment_cnt, argv_expanded);
|
||||
}
|
||||
|
||||
if (command->group) {
|
||||
#if !BB_MMU
|
||||
bb_error_msg_and_die("nested lists are not supported on NOMMU");
|
||||
#else
|
||||
/* Cases when we are here:
|
||||
* ( list )
|
||||
* { list } &
|
||||
* ... | ( list ) | ...
|
||||
* ... | { list } | ...
|
||||
*/
|
||||
#if BB_MMU
|
||||
int rcode;
|
||||
debug_printf_exec("pseudo_exec: run_list\n");
|
||||
rcode = run_list(command->group);
|
||||
/* OK to leak memory by not calling free_pipe_list,
|
||||
* since this process is about to exit */
|
||||
_exit(rcode);
|
||||
#else
|
||||
//TODO: re-exec "hush -c command->group_as_a_string"
|
||||
bb_error_msg_and_die("nested lists are not supported on NOMMU");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Can happen. See what bash does with ">foo" by itself. */
|
||||
debug_printf("trying to pseudo_exec null command\n");
|
||||
debug_printf("pseudo_exec'ed null command\n");
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@ -2563,61 +2572,72 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
|
||||
/* run_pipe() starts all the jobs, but doesn't wait for anything
|
||||
* to finish. See checkjobs().
|
||||
*
|
||||
* return code is normally -1, when the caller has to wait for children
|
||||
* Return code is normally -1, when the caller has to wait for children
|
||||
* to finish to determine the exit status of the pipe. If the pipe
|
||||
* is a simple builtin command, however, the action is done by the
|
||||
* time run_pipe returns, and the exit code is provided as the
|
||||
* return value.
|
||||
*
|
||||
* The input of the pipe is always stdin, the output is always
|
||||
* stdout. The outpipe[] mechanism in BusyBox-0.48 lash is bogus,
|
||||
* because it tries to avoid running the command substitution in
|
||||
* subshell, when that is in fact necessary. The subshell process
|
||||
* now has its stdout directed to the input of the appropriate pipe,
|
||||
* so this routine is noticeably simpler.
|
||||
*
|
||||
* Returns -1 only if started some children. IOW: we have to
|
||||
* mask out retvals of builtins etc with 0xff!
|
||||
*
|
||||
* The only case when we do not need to [v]fork is when the pipe
|
||||
* is single, non-backgrounded, non-subshell command. Examples:
|
||||
* cmd ; ... { list } ; ...
|
||||
* cmd && ... { list } && ...
|
||||
* cmd || ... { list } || ...
|
||||
* If it is, then we can run cmd as a builtin, NOFORK [do we do this?],
|
||||
* or (if SH_STANDALONE) an applet, and we can run the { list }
|
||||
* with run_list(). Otherwise, we fork and exec cmd.
|
||||
*
|
||||
* Cases when we must fork:
|
||||
* non-single: cmd | cmd
|
||||
* backgrounded: cmd & { list } &
|
||||
* subshell: ( list ) [&]
|
||||
*/
|
||||
static int run_pipe(struct pipe *pi)
|
||||
{
|
||||
static const char *const null_ptr = NULL;
|
||||
int i;
|
||||
int nextin;
|
||||
int pipefds[2]; /* pipefds[0] is for reading */
|
||||
struct command *command;
|
||||
char **argv_expanded;
|
||||
char **argv;
|
||||
const struct built_in_command *x;
|
||||
char *p;
|
||||
/* it is not always needed, but we aim to smaller code */
|
||||
int squirrel[] = { -1, -1, -1 };
|
||||
int rcode;
|
||||
const int single_and_fg = (pi->num_cmds == 1 && pi->followup != PIPE_BG);
|
||||
|
||||
debug_printf_exec("run_pipe start: single_and_fg=%d\n", single_and_fg);
|
||||
debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
|
||||
|
||||
#if ENABLE_HUSH_JOB
|
||||
pi->pgrp = -1;
|
||||
#endif
|
||||
pi->alive_cmds = 1;
|
||||
USE_HUSH_JOB(pi->pgrp = -1;)
|
||||
pi->stopped_cmds = 0;
|
||||
|
||||
/* Check if this is a simple builtin (not part of a pipe).
|
||||
* Builtins within pipes have to fork anyway, and are handled in
|
||||
* pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
|
||||
*/
|
||||
command = &(pi->cmds[0]);
|
||||
argv_expanded = NULL;
|
||||
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
if (single_and_fg && command->group && command->grp_type == GRP_FUNCTION) {
|
||||
/* We "execute" function definition */
|
||||
bb_error_msg("here we ought to remember function definition, and go on");
|
||||
return EXIT_SUCCESS;
|
||||
if (pi->num_cmds != 1
|
||||
|| pi->followup == PIPE_BG
|
||||
|| command->grp_type == GRP_SUBSHELL
|
||||
) {
|
||||
goto must_fork;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (single_and_fg && command->group && command->grp_type == GRP_NORMAL) {
|
||||
debug_printf("non-subshell grouping\n");
|
||||
pi->alive_cmds = 1;
|
||||
|
||||
debug_printf_exec(": group:%p argv:'%s'\n",
|
||||
command->group, command->argv ? command->argv[0] : "NONE");
|
||||
|
||||
if (command->group) {
|
||||
#if ENABLE_HUSH_FUNCTIONS
|
||||
if (command->grp_type == GRP_FUNCTION) {
|
||||
/* func () { list } */
|
||||
bb_error_msg("here we ought to remember function definition, and go on");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
/* { list } */
|
||||
debug_printf("non-subshell group\n");
|
||||
setup_redirects(command, squirrel);
|
||||
debug_printf_exec(": run_list\n");
|
||||
rcode = run_list(command->group) & 0xff;
|
||||
@ -2627,26 +2647,33 @@ static int run_pipe(struct pipe *pi)
|
||||
return rcode;
|
||||
}
|
||||
|
||||
argv = command->argv;
|
||||
argv_expanded = NULL;
|
||||
|
||||
if (single_and_fg && argv != NULL) {
|
||||
argv = command->argv ? command->argv : (char **) &null_ptr;
|
||||
{
|
||||
const struct built_in_command *x;
|
||||
char **new_env = NULL;
|
||||
char **old_env = NULL;
|
||||
|
||||
i = command->assignment_cnt;
|
||||
if (i != 0 && argv[i] == NULL) {
|
||||
/* assignments, but no command: set local environment */
|
||||
for (i = 0; argv[i] != NULL; i++) {
|
||||
debug_printf("local environment set: %s\n", argv[i]);
|
||||
p = expand_string_to_string(argv[i]);
|
||||
if (argv[command->assignment_cnt] == NULL) {
|
||||
/* Assignments, but no command */
|
||||
/* Ensure redirects take effect. Try "a=t >file" */
|
||||
setup_redirects(command, squirrel);
|
||||
restore_redirects(squirrel);
|
||||
/* Set shell variables */
|
||||
while (*argv) {
|
||||
p = expand_string_to_string(*argv);
|
||||
debug_printf_exec("set shell var:'%s'->'%s'\n",
|
||||
*argv, p);
|
||||
set_local_var(p, 0);
|
||||
argv++;
|
||||
}
|
||||
return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
|
||||
/* Do we need to flag set_local_var() errors?
|
||||
* "assignment to readonly var" and "putenv error"
|
||||
*/
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Expand the rest into (possibly) many strings each */
|
||||
argv_expanded = expand_strvec_to_strvec(argv + i);
|
||||
argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
|
||||
|
||||
for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) {
|
||||
if (strcmp(argv_expanded[0], x->cmd) != 0)
|
||||
@ -2694,8 +2721,10 @@ static int run_pipe(struct pipe *pi)
|
||||
goto clean_up_and_ret;
|
||||
}
|
||||
#endif
|
||||
/* It is neither builtin nor applet. We must fork. */
|
||||
}
|
||||
|
||||
must_fork:
|
||||
/* NB: argv_expanded may already be created, and that
|
||||
* might include `cmd` runs! Do not rerun it! We *must*
|
||||
* use argv_expanded if it's non-NULL */
|
||||
@ -2715,8 +2744,9 @@ static int run_pipe(struct pipe *pi)
|
||||
if (command->argv) {
|
||||
debug_printf_exec(": pipe member '%s' '%s'...\n",
|
||||
command->argv[0], command->argv[1]);
|
||||
} else
|
||||
} else {
|
||||
debug_printf_exec(": pipe member with no argv\n");
|
||||
}
|
||||
|
||||
/* pipes are inserted between pairs of commands */
|
||||
pipefds[0] = 0;
|
||||
|
10
shell/hush_test/hush-misc/redir1.right
Normal file
10
shell/hush_test/hush-misc/redir1.right
Normal file
@ -0,0 +1,10 @@
|
||||
Test 1: var:ok
|
||||
File created:ok
|
||||
Test 2: var:ok
|
||||
File created:ok
|
||||
Test 3: var:ok
|
||||
File created:ok
|
||||
Test 4: var:ok
|
||||
File created:ok
|
||||
Test 5: var:ok
|
||||
File created:ok
|
32
shell/hush_test/hush-misc/redir1.tests
Executable file
32
shell/hush_test/hush-misc/redir1.tests
Executable file
@ -0,0 +1,32 @@
|
||||
rm shell_test_$$ 2>/dev/null
|
||||
var=bad
|
||||
var=ok >shell_test_$$
|
||||
echo "Test 1: var:$var"
|
||||
test -f shell_test_$$ && echo "File created:ok"
|
||||
|
||||
rm shell_test_$$ 2>/dev/null
|
||||
var=ok
|
||||
true | var=bad >shell_test_$$
|
||||
echo "Test 2: var:$var"
|
||||
test -f shell_test_$$ && echo "File created:ok"
|
||||
|
||||
rm shell_test_$$ 2>/dev/null
|
||||
var=bad
|
||||
{ var=ok >shell_test_$$; }
|
||||
echo "Test 3: var:$var"
|
||||
test -f shell_test_$$ && echo "File created:ok"
|
||||
|
||||
rm shell_test_$$ 2>/dev/null
|
||||
var=ok
|
||||
{ var=bad >shell_test_$$; } &
|
||||
usleep 100000
|
||||
echo "Test 4: var:$var"
|
||||
test -f shell_test_$$ && echo "File created:ok"
|
||||
|
||||
rm shell_test_$$ 2>/dev/null
|
||||
var=ok
|
||||
( var=bad >shell_test_$$ )
|
||||
echo "Test 5: var:$var"
|
||||
test -f shell_test_$$ && echo "File created:ok"
|
||||
|
||||
rm shell_test_$$ 2>/dev/null
|
Loading…
Reference in New Issue
Block a user