ash: [BUILTIN] Exit without arguments in a trap should use status outside traps
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 14 Feb 2020 16:27:18 +0000 (17:27 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 16 Feb 2020 18:14:45 +0000 (19:14 +0100)
Upstream commit:

    Date:   Mon Oct 6 10:39:47 2014 +0800
    [BUILTIN] Exit without arguments in a trap should use status outside traps

    POSIX now requires that exit without arguments in a trap should
    return the last command status prior to executing traps.  This
    patch implements this behaviour.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c
shell/ash_test/ash-misc/exitcode_trap1.right [new file with mode: 0644]
shell/ash_test/ash-misc/exitcode_trap1.tests [new file with mode: 0755]

index a300061a24288df9a4f15eae9f656df39ceb7089..270a338d92410643df5378303edf304721588fc1 100644 (file)
@@ -384,6 +384,7 @@ struct globals_misc {
        uint8_t exitstatus;     /* exit status of last command */
        uint8_t back_exitstatus;/* exit status of backquoted command */
        smallint job_warning;   /* user was warned about stopped jobs (can be 2, 1 or 0). */
+       int savestatus;         /* exit status of last command outside traps */
        int rootpid;            /* pid of main shell */
        /* shell level: 0 for the main shell, 1 for its children, and so on */
        int shlvl;
@@ -466,6 +467,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
 #define exitstatus        (G_misc.exitstatus )
 #define back_exitstatus   (G_misc.back_exitstatus )
 #define job_warning       (G_misc.job_warning)
+#define savestatus  (G_misc.savestatus )
 #define rootpid     (G_misc.rootpid    )
 #define shlvl       (G_misc.shlvl      )
 #define errlinno    (G_misc.errlinno   )
@@ -491,6 +493,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
 #define INIT_G_misc() do { \
        (*(struct globals_misc**)not_const_pp(&ash_ptr_to_globals_misc)) = xzalloc(sizeof(G_misc)); \
        barrier(); \
+       savestatus = -1; \
        curdir = nullstr; \
        physdir = nullstr; \
        trap_ptr = trap; \
@@ -9055,12 +9058,17 @@ dotrap(void)
 {
        uint8_t *g;
        int sig;
-       uint8_t last_status;
+       int status, last_status;
 
        if (!pending_sig)
                return;
 
-       last_status = exitstatus;
+       status = savestatus;
+       last_status = status;
+       if (status < 0) {
+               status = exitstatus;
+               savestatus = status;
+       }
        pending_sig = 0;
        barrier();
 
@@ -9087,8 +9095,10 @@ dotrap(void)
                if (!p)
                        continue;
                evalstring(p, 0);
+               exitstatus = status;
        }
-       exitstatus = last_status;
+
+       savestatus = last_status;
        TRACE(("dotrap returns\n"));
 }
 
@@ -13416,8 +13426,15 @@ exitcmd(int argc UNUSED_PARAM, char **argv)
 {
        if (stoppedjobs())
                return 0;
-       if (argv[1])
-               exitstatus = number(argv[1]);
+
+       if (argv[1]) {
+               int status = number(argv[1]);
+
+               exitstatus = status;
+               if (savestatus >= 0)
+                       savestatus = status;
+       }
+
        raise_exception(EXEXIT);
        /* NOTREACHED */
 }
@@ -14077,19 +14094,15 @@ exitshell(void)
 {
        struct jmploc loc;
        char *p;
-       int status;
 
 #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
        if (line_input_state)
                save_history(line_input_state);
 #endif
-       status = exitstatus;
-       TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
-       if (setjmp(loc.loc)) {
-               if (exception_type == EXEXIT)
-                       status = exitstatus;
+       savestatus = exitstatus;
+       TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
+       if (setjmp(loc.loc))
                goto out;
-       }
        exception_handler = &loc;
        p = trap[0];
        if (p) {
@@ -14104,7 +14117,7 @@ exitshell(void)
         */
        setjobctl(0);
        flush_stdout_stderr();
-       _exit(status);
+       _exit(savestatus);
        /* NOTREACHED */
 }
 
@@ -14280,6 +14293,10 @@ reset(void)
        /* from eval.c: */
        evalskip = 0;
        loopnest = 0;
+       if (savestatus >= 0) {
+               exitstatus = savestatus;
+               savestatus = -1;
+       }
 
        /* from expand.c: */
        ifsfree();
diff --git a/shell/ash_test/ash-misc/exitcode_trap1.right b/shell/ash_test/ash-misc/exitcode_trap1.right
new file mode 100644 (file)
index 0000000..5f76f68
--- /dev/null
@@ -0,0 +1,2 @@
+Trapped
+One:1
diff --git a/shell/ash_test/ash-misc/exitcode_trap1.tests b/shell/ash_test/ash-misc/exitcode_trap1.tests
new file mode 100755 (executable)
index 0000000..c35b6b3
--- /dev/null
@@ -0,0 +1,6 @@
+# "exit" in trap should not use last command's exitcode,
+# but exitcode on entering the trap.
+(trap "echo Trapped; exit" EXIT
+ (exit 1)
+)
+echo One:$?