mirror of
https://github.com/sheumann/hush.git
synced 2025-01-14 12:30:40 +00:00
hush: add support for "set -o pipefail"
function old new delta checkjobs 467 517 +50 builtin_set 259 286 +27 o_opt_strings - 10 +10 hush_main 1011 1013 +2 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 3/0 up/down: 89/0) Total: 89 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
c08c3f5d26
commit
6696eac274
124
shell/hush.c
124
shell/hush.c
@ -507,6 +507,7 @@ struct command {
|
|||||||
# define CMD_FUNCDEF 3
|
# define CMD_FUNCDEF 3
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
smalluint cmd_exitcode;
|
||||||
/* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
|
/* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
|
||||||
struct pipe *group;
|
struct pipe *group;
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
@ -637,6 +638,43 @@ struct function {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* set -/+o OPT support. (TODO: make it optional)
|
||||||
|
* bash supports the following opts:
|
||||||
|
* allexport off
|
||||||
|
* braceexpand on
|
||||||
|
* emacs on
|
||||||
|
* errexit off
|
||||||
|
* errtrace off
|
||||||
|
* functrace off
|
||||||
|
* hashall on
|
||||||
|
* histexpand off
|
||||||
|
* history on
|
||||||
|
* ignoreeof off
|
||||||
|
* interactive-comments on
|
||||||
|
* keyword off
|
||||||
|
* monitor on
|
||||||
|
* noclobber off
|
||||||
|
* noexec off
|
||||||
|
* noglob off
|
||||||
|
* nolog off
|
||||||
|
* notify off
|
||||||
|
* nounset off
|
||||||
|
* onecmd off
|
||||||
|
* physical off
|
||||||
|
* pipefail off
|
||||||
|
* posix off
|
||||||
|
* privileged off
|
||||||
|
* verbose off
|
||||||
|
* vi off
|
||||||
|
* xtrace off
|
||||||
|
*/
|
||||||
|
static const char o_opt_strings[] ALIGN1 = "pipefail\0";
|
||||||
|
enum {
|
||||||
|
OPT_O_PIPEFAIL,
|
||||||
|
NUM_OPT_O
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* "Globals" within this file */
|
/* "Globals" within this file */
|
||||||
/* Sorted roughly by size (smaller offsets == smaller code) */
|
/* Sorted roughly by size (smaller offsets == smaller code) */
|
||||||
struct globals {
|
struct globals {
|
||||||
@ -675,6 +713,7 @@ struct globals {
|
|||||||
int last_jobid;
|
int last_jobid;
|
||||||
pid_t saved_tty_pgrp;
|
pid_t saved_tty_pgrp;
|
||||||
struct pipe *job_list;
|
struct pipe *job_list;
|
||||||
|
char o_opt[NUM_OPT_O];
|
||||||
# define G_saved_tty_pgrp (G.saved_tty_pgrp)
|
# define G_saved_tty_pgrp (G.saved_tty_pgrp)
|
||||||
#else
|
#else
|
||||||
# define G_saved_tty_pgrp 0
|
# define G_saved_tty_pgrp 0
|
||||||
@ -6315,24 +6354,23 @@ static int checkjobs(struct pipe *fg_pipe)
|
|||||||
if (fg_pipe->cmds[i].pid != childpid)
|
if (fg_pipe->cmds[i].pid != childpid)
|
||||||
continue;
|
continue;
|
||||||
if (dead) {
|
if (dead) {
|
||||||
|
int ex;
|
||||||
fg_pipe->cmds[i].pid = 0;
|
fg_pipe->cmds[i].pid = 0;
|
||||||
fg_pipe->alive_cmds--;
|
fg_pipe->alive_cmds--;
|
||||||
if (i == fg_pipe->num_cmds - 1) {
|
ex = WEXITSTATUS(status);
|
||||||
/* last process gives overall exitstatus */
|
/* bash prints killer signal's name for *last*
|
||||||
rcode = WEXITSTATUS(status);
|
* process in pipe (prints just newline for SIGINT).
|
||||||
/* bash prints killer signal's name for *last*
|
* Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
|
||||||
* process in pipe (prints just newline for SIGINT).
|
*/
|
||||||
* Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
|
if (WIFSIGNALED(status)) {
|
||||||
*/
|
int sig = WTERMSIG(status);
|
||||||
if (WIFSIGNALED(status)) {
|
if (i == fg_pipe->num_cmds-1)
|
||||||
int sig = WTERMSIG(status);
|
|
||||||
printf("%s\n", sig == SIGINT ? "" : get_signame(sig));
|
printf("%s\n", sig == SIGINT ? "" : get_signame(sig));
|
||||||
/* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
|
/* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
|
||||||
* Maybe we need to use sig | 128? */
|
* Maybe we need to use sig | 128? */
|
||||||
rcode = sig + 128;
|
ex = sig + 128;
|
||||||
}
|
|
||||||
IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
|
|
||||||
}
|
}
|
||||||
|
fg_pipe->cmds[i].cmd_exitcode = ex;
|
||||||
} else {
|
} else {
|
||||||
fg_pipe->cmds[i].is_stopped = 1;
|
fg_pipe->cmds[i].is_stopped = 1;
|
||||||
fg_pipe->stopped_cmds++;
|
fg_pipe->stopped_cmds++;
|
||||||
@ -6341,6 +6379,15 @@ static int checkjobs(struct pipe *fg_pipe)
|
|||||||
fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
|
fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
|
||||||
if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) {
|
if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) {
|
||||||
/* All processes in fg pipe have exited or stopped */
|
/* All processes in fg pipe have exited or stopped */
|
||||||
|
i = fg_pipe->num_cmds;
|
||||||
|
while (--i >= 0) {
|
||||||
|
rcode = fg_pipe->cmds[i].cmd_exitcode;
|
||||||
|
/* usually last process gives overall exitstatus,
|
||||||
|
* but with "set -o pipefail", last *failed* process does */
|
||||||
|
if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
|
||||||
/* Note: *non-interactive* bash does not continue if all processes in fg pipe
|
/* Note: *non-interactive* bash does not continue if all processes in fg pipe
|
||||||
* are stopped. Testcase: "cat | cat" in a script (not on command line!)
|
* are stopped. Testcase: "cat | cat" in a script (not on command line!)
|
||||||
* and "killall -STOP cat" */
|
* and "killall -STOP cat" */
|
||||||
@ -7340,13 +7387,41 @@ static void set_fatal_handlers(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int set_mode(const char cstate, const char mode)
|
static int set_mode(int state, char mode, const char *o_opt)
|
||||||
{
|
{
|
||||||
int state = (cstate == '-' ? 1 : 0);
|
int idx;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'n': G.n_mode = state; break;
|
case 'n':
|
||||||
case 'x': IF_HUSH_MODE_X(G_x_mode = state;) break;
|
G.n_mode = state;
|
||||||
default: return EXIT_FAILURE;
|
break;
|
||||||
|
case 'x':
|
||||||
|
IF_HUSH_MODE_X(G_x_mode = state;)
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
if (!o_opt) {
|
||||||
|
/* "set -+o" without parameter.
|
||||||
|
* in bash, set -o produces this output:
|
||||||
|
* pipefail off
|
||||||
|
* and set +o:
|
||||||
|
* set +o pipefail
|
||||||
|
* We always use the second form.
|
||||||
|
*/
|
||||||
|
const char *p = o_opt_strings;
|
||||||
|
idx = 0;
|
||||||
|
while (*p) {
|
||||||
|
printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p);
|
||||||
|
idx++;
|
||||||
|
p += strlen(p) + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
idx = index_in_strings(o_opt_strings, o_opt);
|
||||||
|
if (idx >= 0) {
|
||||||
|
G.o_opt[idx] = state;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -7586,7 +7661,7 @@ int hush_main(int argc, char **argv)
|
|||||||
#endif
|
#endif
|
||||||
case 'n':
|
case 'n':
|
||||||
case 'x':
|
case 'x':
|
||||||
if (set_mode('-', opt) == 0) /* no error */
|
if (set_mode(1, opt, NULL) == 0) /* no error */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
#ifndef BB_VER
|
#ifndef BB_VER
|
||||||
@ -8376,15 +8451,18 @@ static int FAST_FUNC builtin_set(char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (!strcmp(arg, "--")) {
|
if (strcmp(arg, "--") == 0) {
|
||||||
++argv;
|
++argv;
|
||||||
goto set_argv;
|
goto set_argv;
|
||||||
}
|
}
|
||||||
if (arg[0] != '+' && arg[0] != '-')
|
if (arg[0] != '+' && arg[0] != '-')
|
||||||
break;
|
break;
|
||||||
for (n = 1; arg[n]; ++n)
|
for (n = 1; arg[n]; ++n) {
|
||||||
if (set_mode(arg[0], arg[n]))
|
if (set_mode((arg[0] == '-'), arg[n], argv[1]))
|
||||||
goto error;
|
goto error;
|
||||||
|
if (arg[n] == 'o' && argv[1])
|
||||||
|
argv++;
|
||||||
|
}
|
||||||
} while ((arg = *++argv) != NULL);
|
} while ((arg = *++argv) != NULL);
|
||||||
/* Now argv[0] is 1st argument */
|
/* Now argv[0] is 1st argument */
|
||||||
|
|
||||||
|
40
shell/hush_test/hush-misc/pipefail.right
Normal file
40
shell/hush_test/hush-misc/pipefail.right
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
Default:
|
||||||
|
true | true:
|
||||||
|
0
|
||||||
|
1
|
||||||
|
true | false:
|
||||||
|
1
|
||||||
|
0
|
||||||
|
false | true:
|
||||||
|
0
|
||||||
|
1
|
||||||
|
exit 2 | exit 3 | exit 4:
|
||||||
|
4
|
||||||
|
0
|
||||||
|
Pipefail on:
|
||||||
|
true | true:
|
||||||
|
0
|
||||||
|
1
|
||||||
|
true | false:
|
||||||
|
1
|
||||||
|
0
|
||||||
|
false | true:
|
||||||
|
1
|
||||||
|
0
|
||||||
|
exit 2 | exit 3 | exit 4:
|
||||||
|
4
|
||||||
|
0
|
||||||
|
Pipefail off:
|
||||||
|
true | true:
|
||||||
|
0
|
||||||
|
1
|
||||||
|
true | false:
|
||||||
|
1
|
||||||
|
0
|
||||||
|
false | true:
|
||||||
|
0
|
||||||
|
1
|
||||||
|
exit 2 | exit 3 | exit 4:
|
||||||
|
4
|
||||||
|
0
|
||||||
|
Done
|
45
shell/hush_test/hush-misc/pipefail.tests
Executable file
45
shell/hush_test/hush-misc/pipefail.tests
Executable file
@ -0,0 +1,45 @@
|
|||||||
|
echo Default:
|
||||||
|
echo "true | true:"
|
||||||
|
true | true; echo $?
|
||||||
|
! true | true; echo $?
|
||||||
|
echo "true | false:"
|
||||||
|
true | false; echo $?
|
||||||
|
! true | false; echo $?
|
||||||
|
echo "false | true:"
|
||||||
|
false | true; echo $?
|
||||||
|
! false | true; echo $?
|
||||||
|
echo "exit 2 | exit 3 | exit 4:"
|
||||||
|
exit 2 | exit 3 | exit 4; echo $?
|
||||||
|
! exit 2 | exit 3 | exit 4; echo $?
|
||||||
|
|
||||||
|
echo Pipefail on:
|
||||||
|
set -o pipefail
|
||||||
|
echo "true | true:"
|
||||||
|
true | true; echo $?
|
||||||
|
! true | true; echo $?
|
||||||
|
echo "true | false:"
|
||||||
|
true | false; echo $?
|
||||||
|
! true | false; echo $?
|
||||||
|
echo "false | true:"
|
||||||
|
false | true; echo $?
|
||||||
|
! false | true; echo $?
|
||||||
|
echo "exit 2 | exit 3 | exit 4:"
|
||||||
|
exit 2 | exit 3 | exit 4; echo $?
|
||||||
|
! exit 2 | exit 3 | exit 4; echo $?
|
||||||
|
|
||||||
|
echo Pipefail off:
|
||||||
|
set +o pipefail
|
||||||
|
echo "true | true:"
|
||||||
|
true | true; echo $?
|
||||||
|
! true | true; echo $?
|
||||||
|
echo "true | false:"
|
||||||
|
true | false; echo $?
|
||||||
|
! true | false; echo $?
|
||||||
|
echo "false | true:"
|
||||||
|
false | true; echo $?
|
||||||
|
! false | true; echo $?
|
||||||
|
echo "exit 2 | exit 3 | exit 4:"
|
||||||
|
exit 2 | exit 3 | exit 4; echo $?
|
||||||
|
! exit 2 | exit 3 | exit 4; echo $?
|
||||||
|
|
||||||
|
echo Done
|
Loading…
x
Reference in New Issue
Block a user