sh: Allow EV_EXIT through function calls.
authorPeter Avalos <pavalos@dragonflybsd.org>
Sat, 2 Jul 2011 23:38:15 +0000 (13:38 -1000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sun, 21 Aug 2011 19:10:23 +0000 (12:10 -0700)
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.

Obtained-from:   FreeBSD SVN rev 220978

bin/sh/error.h
bin/sh/eval.c
bin/sh/main.c
tools/regression/bin/sh/execution/fork3.0 [new file with mode: 0644]
tools/regression/bin/sh/execution/redir6.0 [new file with mode: 0644]
tools/regression/bin/sh/execution/redir7.0 [new file with mode: 0644]

index 03badcf..8f0cd6e 100644 (file)
@@ -34,7 +34,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)error.h     8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/error.h,v 1.21 2011/02/04 22:47:55 jilles Exp $
+ * $FreeBSD: src/bin/sh/error.h,v 1.22 2011/04/23 22:28:56 jilles Exp $
  */
 
 /*
@@ -61,6 +61,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) */
 
 
 /*
index 703d4c2..0d4964d 100644 (file)
@@ -34,7 +34,7 @@
  * SUCH DAMAGE.
  *
  * @(#)eval.c  8.9 (Berkeley) 6/8/95
- * $FreeBSD: src/bin/sh/eval.c,v 1.101 2011/02/05 14:08:51 jilles Exp $
+ * $FreeBSD: src/bin/sh/eval.c,v 1.102 2011/04/23 22:28:56 jilles Exp $
  */
 
 #include <sys/time.h>
@@ -180,7 +180,7 @@ evalstring(char *s, int flags)
        if (!any)
                exitstatus = 0;
        if (flags_exit)
-               exitshell(exitstatus);
+               exraise(EXEXIT);
 }
 
 
@@ -286,8 +286,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);
 }
 
 
@@ -441,8 +443,8 @@ evalredir(union node *n, int flags)
 
                handler = savehandler;
                e = exception;
+               popredir();
                if (e == EXERROR || e == EXEXEC) {
-                       popredir();
                        if (in_redirect) {
                                exitstatus = 2;
                                return;
@@ -930,8 +932,7 @@ evalcommand(union node *cmd, int flgs, 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;
@@ -946,10 +947,8 @@ evalcommand(union node *cmd, int flgs, 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();
@@ -985,7 +984,10 @@ evalcommand(union node *cmd, int flgs, 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;
@@ -1021,8 +1023,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)
index 5fc7873..688384e 100644 (file)
@@ -35,7 +35,7 @@
  *
  * @(#) Copyright (c) 1991, 1993 The Regents of the University of California.  All rights reserved.
  * @(#)main.c  8.6 (Berkeley) 5/28/95
- * $FreeBSD: src/bin/sh/main.c,v 1.44 2011/02/04 22:47:55 jilles Exp $
+ * $FreeBSD: src/bin/sh/main.c,v 1.45 2011/04/23 22:28:56 jilles Exp $
  */
 
 #include <stdio.h>
@@ -105,7 +105,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)
diff --git a/tools/regression/bin/sh/execution/fork3.0 b/tools/regression/bin/sh/execution/fork3.0
new file mode 100644 (file)
index 0000000..db9a523
--- /dev/null
@@ -0,0 +1,4 @@
+# $FreeBSD: src/tools/regression/bin/sh/execution/fork3.0,v 1.1 2011/04/23 22:28:56 jilles Exp $
+
+result=$(${SH} -c 'f() { ps -p $$ -o comm=; }; f')
+test "$result" = "ps"
diff --git a/tools/regression/bin/sh/execution/redir6.0 b/tools/regression/bin/sh/execution/redir6.0
new file mode 100644 (file)
index 0000000..b50159a
--- /dev/null
@@ -0,0 +1,21 @@
+# $FreeBSD: src/tools/regression/bin/sh/execution/redir6.0,v 1.1 2011/04/23 22:28:56 jilles Exp $
+
+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))
diff --git a/tools/regression/bin/sh/execution/redir7.0 b/tools/regression/bin/sh/execution/redir7.0
new file mode 100644 (file)
index 0000000..e99a356
--- /dev/null
@@ -0,0 +1,21 @@
+# $FreeBSD: src/tools/regression/bin/sh/execution/redir7.0,v 1.1 2011/04/23 22:28:56 jilles Exp $
+
+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))