hush: implement "return NUM in trap sets $? after trap"
[oweals/busybox.git] / shell / hush.c
index bced388bff797a5ae132b578736e0b5453b6a08c..6172f228599b81585a860772106e072cf855df6e 100644 (file)
@@ -988,6 +988,9 @@ struct globals {
 # define G_fatal_sig_mask 0
 #endif
 #if ENABLE_HUSH_TRAP
+# if ENABLE_HUSH_FUNCTIONS
+       int return_exitcode;
+# endif
        char **traps; /* char *traps[NSIG] */
 # define G_traps G.traps
 #else
@@ -2097,6 +2100,7 @@ static int check_and_run_traps(void)
                } while (sig < NSIG);
                break;
  got_sig:
+#if ENABLE_HUSH_TRAP
                if (G_traps && G_traps[sig]) {
                        debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]);
                        if (G_traps[sig][0]) {
@@ -2110,12 +2114,18 @@ static int check_and_run_traps(void)
                                save_rcode = G.last_exitcode;
                                builtin_eval(argv);
                                free(argv[1]);
-//FIXME: shouldn't it be set to 128 + sig instead?
                                G.last_exitcode = save_rcode;
+# if ENABLE_HUSH_FUNCTIONS
+                               if (G.return_exitcode >= 0) {
+                                       debug_printf_exec("trap exitcode:%d\n", G.return_exitcode);
+                                       G.last_exitcode = G.return_exitcode;
+                               }
+# endif
                                last_sig = sig;
                        } /* else: "" trap, ignoring signal */
                        continue;
                }
+#endif
                /* not a trap: special action */
                switch (sig) {
                case SIGINT:
@@ -8127,6 +8137,10 @@ static int run_function(const struct function *funcp, char **argv)
        IF_HUSH_LOCAL(leave_var_nest_level();)
 
        G_flag_return_in_progress = sv_flg;
+# if ENABLE_HUSH_TRAP
+       debug_printf_exec("G.return_exitcode=-1\n");
+       G.return_exitcode = -1; /* invalidate stashed return value */
+# endif
 
        restore_G_args(&sv, argv);
 
@@ -9628,6 +9642,9 @@ static int run_list(struct pipe *pi)
                        debug_printf_exec(": builtin/func exitcode %d\n", rcode);
                        G.last_exitcode = rcode;
                        check_and_run_traps();
+#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
+                       rcode = G.last_exitcode; /* "return" in trap can change it, read back */
+#endif
 #if ENABLE_HUSH_LOOPS
                        /* Was it "break" or "continue"? */
                        if (G.flag_break_continue) {
@@ -9684,6 +9701,9 @@ static int run_list(struct pipe *pi)
  check_traps:
                        G.last_exitcode = rcode;
                        check_and_run_traps();
+#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
+                       rcode = G.last_exitcode; /* "return" in trap can change it, read back */
+#endif
                }
 
                /* Handle "set -e" */
@@ -9907,6 +9927,9 @@ int hush_main(int argc, char **argv)
        INIT_G();
        if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
                G.last_exitcode = EXIT_SUCCESS;
+#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
+       G.return_exitcode = -1;
+#endif
 
 #if ENABLE_HUSH_FAST
        G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
@@ -11745,6 +11768,12 @@ static int FAST_FUNC builtin_return(char **argv)
         * 255  <== we also do this
         */
        rc = parse_numeric_argv1(argv, G.last_exitcode, 0);
+# if ENABLE_HUSH_TRAP
+       if (argv[1]) { /* "return ARG" inside a running trap sets $? */
+               debug_printf_exec("G.return_exitcode=%d\n", rc);
+               G.return_exitcode = rc;
+       }
+# endif
        return rc;
 }
 #endif