ash: eval: Only restore exit status on exit/return
[oweals/busybox.git] / shell / ash.c
index bea24601cbb85e05d37452927114f2aea53e396e..fd2fc9f2381047d52328f71f7c2324c4e76a6e24 100644 (file)
@@ -404,11 +404,11 @@ struct globals_misc {
        volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
        volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
        volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
-       smallint exception_type; /* kind of exception (0..5) */
-       /* exceptions */
+       smallint exception_type; /* kind of exception: */
 #define EXINT 0         /* SIGINT received */
 #define EXERROR 1       /* a generic error */
-#define EXEXIT 4        /* exit the shell */
+#define EXEND 3         /* exit the shell */
+#define EXEXIT 4        /* exit the shell via exitcmd */
 
        char nullstr[1];        /* zero length string */
 
@@ -8237,7 +8237,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
        exitstatus = exerrno;
        TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
                prog, e, suppress_int));
-       ash_msg_and_raise(EXEXIT, "%s: %s", prog, errmsg(e, "not found"));
+       ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found"));
        /* NOTREACHED */
 }
 
@@ -9291,9 +9291,9 @@ evaltree(union node *n, int flags)
        dotrap();
 
        if (checkexit & status)
-               raise_exception(EXEXIT);
+               raise_exception(EXEND);
        if (flags & EV_EXIT)
-               raise_exception(EXEXIT);
+               raise_exception(EXEND);
 
        popstackmark(&smark);
        TRACE(("leaving evaltree (no interrupts)\n"));
@@ -14146,6 +14146,47 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv)
 
 /* ============ main() and helpers */
 
+/*
+ * This routine is called when an error or an interrupt occurs in an
+ * interactive shell and control is returned to the main command loop
+ * but prior to exitshell.
+ */
+static void
+exitreset(void)
+{
+       /* from eval.c: */
+       if (savestatus >= 0) {
+               if (exception_type == EXEXIT || evalskip == SKIPFUNCDEF)
+                       exitstatus = savestatus;
+               savestatus = -1;
+       }
+       evalskip = 0;
+       loopnest = 0;
+
+       /* from expand.c: */
+       ifsfree();
+
+       /* from redir.c: */
+       unwindredir(NULL);
+}
+
+/*
+ * This routine is called when an error or an interrupt occurs in an
+ * interactive shell and control is returned to the main command loop.
+ * (In dash, this function is auto-generated by build machinery).
+ */
+static void
+reset(void)
+{
+       /* from input.c: */
+       g_parsefile->left_in_buffer = 0;
+       g_parsefile->left_in_line = 0;      /* clear input buffer */
+       popallfiles();
+
+       /* from var.c: */
+       unwindlocalvars(NULL);
+}
+
 /*
  * Called to exit the shell.
  */
@@ -14169,15 +14210,17 @@ exitshell(void)
                trap[0] = NULL;
                evalskip = 0;
                evalstring(p, 0);
+               evalskip = SKIPFUNCDEF;
                /*free(p); - we'll exit soon */
        }
  out:
+       exitreset();
        /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
         * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
         */
        setjobctl(0);
        flush_stdout_stderr();
-       _exit(savestatus);
+       _exit(exitstatus);
        /* NOTREACHED */
 }
 
@@ -14337,46 +14380,6 @@ read_profile(const char *name)
        popfile();
 }
 
-/*
- * This routine is called when an error or an interrupt occurs in an
- * interactive shell and control is returned to the main command loop
- * but prior to exitshell.
- */
-static void
-exitreset(void)
-{
-       /* from eval.c: */
-       evalskip = 0;
-       loopnest = 0;
-       if (savestatus >= 0) {
-               exitstatus = savestatus;
-               savestatus = -1;
-       }
-
-       /* from expand.c: */
-       ifsfree();
-
-       /* from redir.c: */
-       unwindredir(NULL);
-}
-
-/*
- * This routine is called when an error or an interrupt occurs in an
- * interactive shell and control is returned to the main command loop.
- * (In dash, this function is auto-generated by build machinery).
- */
-static void
-reset(void)
-{
-       /* from input.c: */
-       g_parsefile->left_in_buffer = 0;
-       g_parsefile->left_in_line = 0;      /* clear input buffer */
-       popallfiles();
-
-       /* from var.c: */
-       unwindlocalvars(NULL);
-}
-
 #if PROFILE
 static short profile_buf[16384];
 extern int etext();
@@ -14424,7 +14427,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
 
                e = exception_type;
                s = state;
-               if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
+               if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
                        exitshell();
                }