sh: Allow EV_EXIT through function calls, make {...} <redir more consistent.

If EV_EXIT causes an exit, use the exception mechanism to unwind
redirections and local variables. This way, if the final command is a
redirected command, an EXIT trap now executes without the redirections.

Because of these changes, EV_EXIT can now be inherited by the body of a
function, so do so. This means that a function no longer prevents a fork
before an exec being skipped, such as in
  f() { head -1 /etc/passwd; }; echo $(f)

Wrapping a single builtin in a function may still cause an otherwise
unnecessary fork with command substitution, however.

An exit command or -e failure still invokes the EXIT trap with the
original redirections and local variables in place.

Note: this depends on SHELLPROC being gone. A SHELLPROC depended on
keeping the redirections and local variables and only cleaning up the
state to restore them.
This commit is contained in:
Jilles Tjoelker 2011-04-23 22:28:56 +00:00
parent 86769ac0a4
commit 45496405c6
6 changed files with 62 additions and 13 deletions

View File

@ -57,6 +57,7 @@ extern volatile sig_atomic_t exception;
#define EXINT 0 /* SIGINT received */
#define EXERROR 1 /* a generic error */
#define EXEXEC 2 /* command execution failed */
#define EXEXIT 3 /* call exitshell(exitstatus) */
/*

View File

@ -179,7 +179,7 @@ evalstring(char *s, int flags)
if (!any)
exitstatus = 0;
if (flags_exit)
exitshell(exitstatus);
exraise(EXEXIT);
}
@ -285,8 +285,10 @@ evaltree(union node *n, int flags)
out:
if (pendingsigs)
dotrap();
if ((flags & EV_EXIT) || (eflag && exitstatus != 0 && do_etest))
if (eflag && exitstatus != 0 && do_etest)
exitshell(exitstatus);
if (flags & EV_EXIT)
exraise(EXEXIT);
}
@ -440,8 +442,8 @@ evalredir(union node *n, int flags)
handler = savehandler;
e = exception;
popredir();
if (e == EXERROR || e == EXEXEC) {
popredir();
if (in_redirect) {
exitstatus = 2;
return;
@ -927,8 +929,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
if (setjmp(jmploc.loc)) {
freeparam(&shellparam);
shellparam = saveparam;
if (exception == EXERROR || exception == EXEXEC)
popredir();
popredir();
unreffunc(cmdentry.u.func);
poplocalvars();
localvars = savelocalvars;
@ -943,10 +944,8 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
for (sp = varlist.list ; sp ; sp = sp->next)
mklocal(sp->text);
exitstatus = oexitstatus;
if (flags & EV_TESTED)
evaltree(getfuncnode(cmdentry.u.func), EV_TESTED);
else
evaltree(getfuncnode(cmdentry.u.func), 0);
evaltree(getfuncnode(cmdentry.u.func),
flags & (EV_TESTED | EV_EXIT));
INTOFF;
unreffunc(cmdentry.u.func);
poplocalvars();
@ -982,7 +981,10 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
savehandler = handler;
if (setjmp(jmploc.loc)) {
e = exception;
exitstatus = (e == EXINT)? SIGINT+128 : 2;
if (e == EXINT)
exitstatus = SIGINT+128;
else if (e != EXEXIT)
exitstatus = 2;
goto cmddone;
}
handler = &jmploc;
@ -1018,8 +1020,7 @@ cmddone:
backcmd->nleft = memout.nextc - memout.buf;
memout.buf = NULL;
}
if (cmdentry.u.index != EXECCMD &&
(e == -1 || e == EXERROR || e == EXEXEC))
if (cmdentry.u.index != EXECCMD)
popredir();
if (e != -1) {
if ((e != EXERROR && e != EXEXEC)

View File

@ -111,7 +111,8 @@ main(int argc, char *argv[])
break;
}
if (state == 0 || iflag == 0 || ! rootshell)
if (state == 0 || iflag == 0 || ! rootshell ||
exception == EXEXIT)
exitshell(exitstatus);
reset();
if (exception == EXINT)

View File

@ -0,0 +1,4 @@
# $FreeBSD$
result=$(${SH} -c 'f() { ps -p $$ -o comm=; }; f')
test "$result" = "ps"

View File

@ -0,0 +1,21 @@
# $FreeBSD$
failures=0
check() {
if [ "$2" != "$3" ]; then
echo "Failure at $1" >&2
failures=$((failures + 1))
fi
}
check $LINENO "$(trap "echo bye" EXIT; : >/dev/null)" bye
check $LINENO "$(trap "echo bye" EXIT; { :; } >/dev/null)" bye
check $LINENO "$(trap "echo bye" EXIT; (:) >/dev/null)" bye
check $LINENO "$(trap "echo bye" EXIT; (: >/dev/null))" bye
check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; : >/dev/null')" bye
check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; { :; } >/dev/null')" bye
check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; (:) >/dev/null')" bye
check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; (: >/dev/null)')" bye
exit $((failures > 0))

View File

@ -0,0 +1,21 @@
# $FreeBSD$
failures=0
check() {
if [ "$2" != "$3" ]; then
echo "Failure at $1" >&2
failures=$((failures + 1))
fi
}
check $LINENO "$(trap "echo bye" EXIT; f() { :; }; f >/dev/null)" bye
check $LINENO "$(trap "echo bye" EXIT; f() { :; }; { f; } >/dev/null)" bye
check $LINENO "$(trap "echo bye" EXIT; f() { :; }; (f) >/dev/null)" bye
check $LINENO "$(trap "echo bye" EXIT; f() { :; }; (f >/dev/null))" bye
check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; f >/dev/null')" bye
check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; { f; } >/dev/null')" bye
check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; (f) >/dev/null')" bye
check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; (f >/dev/null)')" bye
exit $((failures > 0))