aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2020-02-21 13:27:25 +0000
committerRon Yorston <rmy@pobox.com>2020-02-21 13:27:25 +0000
commit35da75bde8885d1922701a36baa61bc8b01f14d4 (patch)
tree87f6cbac5912e70a9c69b541f39e3b1d02f4394f
parent63c8e755021cf35c8a94ea1291c1e123164a7917 (diff)
parent3ced804e3118d138781c3e4baa6bf1589b9f2dfd (diff)
downloadbusybox-w32-35da75bde8885d1922701a36baa61bc8b01f14d4.tar.gz
busybox-w32-35da75bde8885d1922701a36baa61bc8b01f14d4.tar.bz2
busybox-w32-35da75bde8885d1922701a36baa61bc8b01f14d4.zip
Merge branch 'busybox' into merge
-rw-r--r--shell/ash.c162
-rw-r--r--shell/ash_test/ash-misc/exitcode_trap3.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode_trap3.tests9
-rw-r--r--shell/ash_test/ash-misc/exitcode_trap4.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode_trap4.tests8
-rw-r--r--shell/ash_test/ash-misc/exitcode_trap5.right3
-rwxr-xr-xshell/ash_test/ash-misc/exitcode_trap5.tests10
-rw-r--r--shell/ash_test/ash-misc/exitcode_trap6.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode_trap6.tests11
-rw-r--r--shell/hush.c47
-rw-r--r--shell/hush_test/hush-misc/exitcode_trap3.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode_trap3.tests9
-rw-r--r--shell/hush_test/hush-misc/exitcode_trap4.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode_trap4.tests8
-rw-r--r--shell/hush_test/hush-misc/exitcode_trap5.right3
-rwxr-xr-xshell/hush_test/hush-misc/exitcode_trap5.tests10
-rw-r--r--shell/hush_test/hush-misc/exitcode_trap6.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode_trap6.tests11
18 files changed, 226 insertions, 77 deletions
diff --git a/shell/ash.c b/shell/ash.c
index f33f72b80..252061dae 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -532,11 +532,11 @@ struct globals_misc {
532 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ 532 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
533 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ 533 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
534#endif 534#endif
535 smallint exception_type; /* kind of exception (0..5) */ 535 smallint exception_type; /* kind of exception: */
536 /* exceptions */
537#define EXINT 0 /* SIGINT received */ 536#define EXINT 0 /* SIGINT received */
538#define EXERROR 1 /* a generic error */ 537#define EXERROR 1 /* a generic error */
539#define EXEXIT 4 /* exit the shell */ 538#define EXEND 3 /* exit the shell */
539#define EXEXIT 4 /* exit the shell via exitcmd */
540 540
541 char nullstr[1]; /* zero length string */ 541 char nullstr[1]; /* zero length string */
542 542
@@ -6561,6 +6561,8 @@ static int substr_atoi(const char *s)
6561#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */ 6561#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */
6562#define EXP_WORD 0x40 /* expand word in parameter expansion */ 6562#define EXP_WORD 0x40 /* expand word in parameter expansion */
6563#define EXP_QUOTED 0x100 /* expand word in double quotes */ 6563#define EXP_QUOTED 0x100 /* expand word in double quotes */
6564#define EXP_KEEPNUL 0x200 /* do not skip NUL characters */
6565
6564/* 6566/*
6565 * rmescape() flags 6567 * rmescape() flags
6566 */ 6568 */
@@ -6571,8 +6573,6 @@ static int substr_atoi(const char *s)
6571 6573
6572/* Add CTLESC when necessary. */ 6574/* Add CTLESC when necessary. */
6573#define QUOTES_ESC (EXP_FULL | EXP_CASE) 6575#define QUOTES_ESC (EXP_FULL | EXP_CASE)
6574/* Do not skip NUL characters. */
6575#define QUOTES_KEEPNUL EXP_TILDE
6576 6576
6577/* 6577/*
6578 * Structure specifying which parts of the string should be searched 6578 * Structure specifying which parts of the string should be searched
@@ -6879,27 +6879,28 @@ preglob(const char *pattern, int flag)
6879 * Put a string on the stack. 6879 * Put a string on the stack.
6880 */ 6880 */
6881static void 6881static void
6882memtodest(const char *p, size_t len, int syntax, int quotes) 6882memtodest(const char *p, size_t len, int flags)
6883{ 6883{
6884 int syntax = flags & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
6884 char *q; 6885 char *q;
6885 6886
6886 if (!len) 6887 if (!len)
6887 return; 6888 return;
6888 6889
6889 q = makestrspace((quotes & QUOTES_ESC) ? len * 2 : len, expdest); 6890 q = makestrspace(len * 2, expdest);
6890 6891
6891 do { 6892 do {
6892 unsigned char c = *p++; 6893 unsigned char c = *p++;
6893 if (c) { 6894 if (c) {
6894 if (quotes & QUOTES_ESC) { 6895 if (flags & QUOTES_ESC) {
6895 int n = SIT(c, syntax); 6896 int n = SIT(c, syntax);
6896 if (n == CCTL 6897 if (n == CCTL
6897 || (syntax != BASESYNTAX && n == CBACK) 6898 || ((flags & EXP_QUOTED) && n == CBACK)
6898 ) { 6899 ) {
6899 USTPUTC(CTLESC, q); 6900 USTPUTC(CTLESC, q);
6900 } 6901 }
6901 } 6902 }
6902 } else if (!(quotes & QUOTES_KEEPNUL)) 6903 } else if (!(flags & EXP_KEEPNUL))
6903 continue; 6904 continue;
6904 USTPUTC(c, q); 6905 USTPUTC(c, q);
6905 } while (--len); 6906 } while (--len);
@@ -6908,10 +6909,10 @@ memtodest(const char *p, size_t len, int syntax, int quotes)
6908} 6909}
6909 6910
6910static size_t 6911static size_t
6911strtodest(const char *p, int syntax, int quotes) 6912strtodest(const char *p, int flags)
6912{ 6913{
6913 size_t len = strlen(p); 6914 size_t len = strlen(p);
6914 memtodest(p, len, syntax, quotes); 6915 memtodest(p, len, flags);
6915 return len; 6916 return len;
6916} 6917}
6917 6918
@@ -6979,13 +6980,12 @@ removerecordregions(int endoff)
6979} 6980}
6980 6981
6981static char * 6982static char *
6982exptilde(char *startp, char *p, int flags) 6983exptilde(char *startp, char *p, int flag)
6983{ 6984{
6984 unsigned char c; 6985 unsigned char c;
6985 char *name; 6986 char *name;
6986 struct passwd *pw; 6987 struct passwd *pw;
6987 const char *home; 6988 const char *home;
6988 int quotes = flags & QUOTES_ESC;
6989 6989
6990 name = p + 1; 6990 name = p + 1;
6991 6991
@@ -6996,7 +6996,7 @@ exptilde(char *startp, char *p, int flags)
6996 case CTLQUOTEMARK: 6996 case CTLQUOTEMARK:
6997 return startp; 6997 return startp;
6998 case ':': 6998 case ':':
6999 if (flags & EXP_VARTILDE) 6999 if (flag & EXP_VARTILDE)
7000 goto done; 7000 goto done;
7001 break; 7001 break;
7002 case '/': 7002 case '/':
@@ -7017,7 +7017,7 @@ exptilde(char *startp, char *p, int flags)
7017 if (!home) 7017 if (!home)
7018 goto lose; 7018 goto lose;
7019 *p = c; 7019 *p = c;
7020 strtodest(home, SQSYNTAX, quotes); 7020 strtodest(home, flag | EXP_QUOTED);
7021 return p; 7021 return p;
7022 lose: 7022 lose:
7023 *p = c; 7023 *p = c;
@@ -7127,7 +7127,6 @@ expbackq(union node *cmd, int flag)
7127 char *p; 7127 char *p;
7128 char *dest; 7128 char *dest;
7129 int startloc; 7129 int startloc;
7130 int syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
7131 struct stackmark smark; 7130 struct stackmark smark;
7132 7131
7133 INT_OFF; 7132 INT_OFF;
@@ -7141,7 +7140,7 @@ expbackq(union node *cmd, int flag)
7141 if (i == 0) 7140 if (i == 0)
7142 goto read; 7141 goto read;
7143 for (;;) { 7142 for (;;) {
7144 memtodest(p, i, syntax, flag & QUOTES_ESC); 7143 memtodest(p, i, flag);
7145 read: 7144 read:
7146 if (in.fd < 0) 7145 if (in.fd < 0)
7147 break; 7146 break;
@@ -7851,11 +7850,10 @@ varvalue(char *name, int varflags, int flags, int quoted)
7851 int sep; 7850 int sep;
7852 int subtype = varflags & VSTYPE; 7851 int subtype = varflags & VSTYPE;
7853 int discard = subtype == VSPLUS || subtype == VSLENGTH; 7852 int discard = subtype == VSPLUS || subtype == VSLENGTH;
7854 int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL;
7855 int syntax;
7856 7853
7854 flags |= EXP_KEEPNUL;
7855 flags &= discard ? ~QUOTES_ESC : ~0;
7857 sep = (flags & EXP_FULL) << CHAR_BIT; 7856 sep = (flags & EXP_FULL) << CHAR_BIT;
7858 syntax = quoted ? DQSYNTAX : BASESYNTAX;
7859 7857
7860 switch (*name) { 7858 switch (*name) {
7861 case '$': 7859 case '$':
@@ -7921,11 +7919,11 @@ varvalue(char *name, int varflags, int flags, int quoted)
7921 if (!ap) 7919 if (!ap)
7922 return -1; 7920 return -1;
7923 while ((p = *ap++) != NULL) { 7921 while ((p = *ap++) != NULL) {
7924 len += strtodest(p, syntax, quotes); 7922 len += strtodest(p, flags);
7925 7923
7926 if (*ap && sep) { 7924 if (*ap && sep) {
7927 len++; 7925 len++;
7928 memtodest(&sepc, 1, syntax, quotes); 7926 memtodest(&sepc, 1, flags);
7929 } 7927 }
7930 } 7928 }
7931 break; 7929 break;
@@ -7952,7 +7950,7 @@ varvalue(char *name, int varflags, int flags, int quoted)
7952 if (!p) 7950 if (!p)
7953 return -1; 7951 return -1;
7954 7952
7955 len = strtodest(p, syntax, quotes); 7953 len = strtodest(p, flags);
7956#if ENABLE_UNICODE_SUPPORT 7954#if ENABLE_UNICODE_SUPPORT
7957 if (subtype == VSLENGTH && len > 0) { 7955 if (subtype == VSLENGTH && len > 0) {
7958 reinit_unicode_for_ash(); 7956 reinit_unicode_for_ash();
@@ -8795,7 +8793,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8795 exitstatus = exerrno; 8793 exitstatus = exerrno;
8796 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", 8794 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
8797 prog, e, suppress_int)); 8795 prog, e, suppress_int));
8798 ash_msg_and_raise(EXEXIT, "%s: %s", prog, errmsg(e, "not found")); 8796 ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found"));
8799 /* NOTREACHED */ 8797 /* NOTREACHED */
8800} 8798}
8801 8799
@@ -9742,6 +9740,7 @@ defun(union node *func)
9742#define SKIPBREAK (1 << 0) 9740#define SKIPBREAK (1 << 0)
9743#define SKIPCONT (1 << 1) 9741#define SKIPCONT (1 << 1)
9744#define SKIPFUNC (1 << 2) 9742#define SKIPFUNC (1 << 2)
9743#define SKIPFUNCDEF (1 << 3)
9745static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 9744static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
9746static int skipcount; /* number of levels to skip */ 9745static int skipcount; /* number of levels to skip */
9747#if ENABLE_PLATFORM_POSIX 9746#if ENABLE_PLATFORM_POSIX
@@ -9802,7 +9801,8 @@ dotrap(void)
9802 if (!p) 9801 if (!p)
9803 continue; 9802 continue;
9804 evalstring(p, 0); 9803 evalstring(p, 0);
9805 exitstatus = status; 9804 if (evalskip != SKIPFUNC)
9805 exitstatus = status;
9806 } 9806 }
9807 9807
9808 savestatus = last_status; 9808 savestatus = last_status;
@@ -9946,9 +9946,9 @@ evaltree(union node *n, int flags)
9946 dotrap(); 9946 dotrap();
9947 9947
9948 if (checkexit & status) 9948 if (checkexit & status)
9949 raise_exception(EXEXIT); 9949 raise_exception(EXEND);
9950 if (flags & EV_EXIT) 9950 if (flags & EV_EXIT)
9951 raise_exception(EXEXIT); 9951 raise_exception(EXEND);
9952 9952
9953 popstackmark(&smark); 9953 popstackmark(&smark);
9954 TRACE(("leaving evaltree (no interrupts)\n")); 9954 TRACE(("leaving evaltree (no interrupts)\n"));
@@ -10462,7 +10462,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
10462 shellparam = saveparam; 10462 shellparam = saveparam;
10463 exception_handler = savehandler; 10463 exception_handler = savehandler;
10464 INT_ON; 10464 INT_ON;
10465 evalskip &= ~SKIPFUNC; 10465 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
10466 return e; 10466 return e;
10467} 10467}
10468 10468
@@ -10607,12 +10607,23 @@ execcmd(int argc UNUSED_PARAM, char **argv)
10607static int FAST_FUNC 10607static int FAST_FUNC
10608returncmd(int argc UNUSED_PARAM, char **argv) 10608returncmd(int argc UNUSED_PARAM, char **argv)
10609{ 10609{
10610 int skip;
10611 int status;
10612
10610 /* 10613 /*
10611 * If called outside a function, do what ksh does; 10614 * If called outside a function, do what ksh does;
10612 * skip the rest of the file. 10615 * skip the rest of the file.
10613 */ 10616 */
10614 evalskip = SKIPFUNC; 10617 if (argv[1]) {
10615 return argv[1] ? number(argv[1]) : exitstatus; 10618 skip = SKIPFUNC;
10619 status = number(argv[1]);
10620 } else {
10621 skip = SKIPFUNCDEF;
10622 status = exitstatus;
10623 }
10624 evalskip = skip;
10625
10626 return status;
10616} 10627}
10617 10628
10618/* Forward declarations for builtintab[] */ 10629/* Forward declarations for builtintab[] */
@@ -14100,7 +14111,7 @@ cmdloop(int top)
14100 skip = evalskip; 14111 skip = evalskip;
14101 14112
14102 if (skip) { 14113 if (skip) {
14103 evalskip &= ~SKIPFUNC; 14114 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
14104 break; 14115 break;
14105 } 14116 }
14106 } 14117 }
@@ -14889,6 +14900,47 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv)
14889/* ============ main() and helpers */ 14900/* ============ main() and helpers */
14890 14901
14891/* 14902/*
14903 * This routine is called when an error or an interrupt occurs in an
14904 * interactive shell and control is returned to the main command loop
14905 * but prior to exitshell.
14906 */
14907static void
14908exitreset(void)
14909{
14910 /* from eval.c: */
14911 if (savestatus >= 0) {
14912 if (exception_type == EXEXIT || evalskip == SKIPFUNCDEF)
14913 exitstatus = savestatus;
14914 savestatus = -1;
14915 }
14916 evalskip = 0;
14917 loopnest = 0;
14918
14919 /* from expand.c: */
14920 ifsfree();
14921
14922 /* from redir.c: */
14923 unwindredir(NULL);
14924}
14925
14926/*
14927 * This routine is called when an error or an interrupt occurs in an
14928 * interactive shell and control is returned to the main command loop.
14929 * (In dash, this function is auto-generated by build machinery).
14930 */
14931static void
14932reset(void)
14933{
14934 /* from input.c: */
14935 g_parsefile->left_in_buffer = 0;
14936 g_parsefile->left_in_line = 0; /* clear input buffer */
14937 popallfiles();
14938
14939 /* from var.c: */
14940 unwindlocalvars(NULL);
14941}
14942
14943/*
14892 * Called to exit the shell. 14944 * Called to exit the shell.
14893 */ 14945 */
14894static void 14946static void
@@ -14911,15 +14963,17 @@ exitshell(void)
14911 trap[0] = NULL; 14963 trap[0] = NULL;
14912 evalskip = 0; 14964 evalskip = 0;
14913 evalstring(p, 0); 14965 evalstring(p, 0);
14966 evalskip = SKIPFUNCDEF;
14914 /*free(p); - we'll exit soon */ 14967 /*free(p); - we'll exit soon */
14915 } 14968 }
14916 out: 14969 out:
14970 exitreset();
14917 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}". 14971 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14918 * our setjobctl(0) does not panic if tcsetpgrp fails inside it. 14972 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
14919 */ 14973 */
14920 setjobctl(0); 14974 setjobctl(0);
14921 flush_stdout_stderr(); 14975 flush_stdout_stderr();
14922 _exit(savestatus); 14976 _exit(exitstatus);
14923 /* NOTREACHED */ 14977 /* NOTREACHED */
14924} 14978}
14925 14979
@@ -15174,46 +15228,6 @@ read_profile(const char *name)
15174 popfile(); 15228 popfile();
15175} 15229}
15176 15230
15177/*
15178 * This routine is called when an error or an interrupt occurs in an
15179 * interactive shell and control is returned to the main command loop
15180 * but prior to exitshell.
15181 */
15182static void
15183exitreset(void)
15184{
15185 /* from eval.c: */
15186 evalskip = 0;
15187 loopnest = 0;
15188 if (savestatus >= 0) {
15189 exitstatus = savestatus;
15190 savestatus = -1;
15191 }
15192
15193 /* from expand.c: */
15194 ifsfree();
15195
15196 /* from redir.c: */
15197 unwindredir(NULL);
15198}
15199
15200/*
15201 * This routine is called when an error or an interrupt occurs in an
15202 * interactive shell and control is returned to the main command loop.
15203 * (In dash, this function is auto-generated by build machinery).
15204 */
15205static void
15206reset(void)
15207{
15208 /* from input.c: */
15209 g_parsefile->left_in_buffer = 0;
15210 g_parsefile->left_in_line = 0; /* clear input buffer */
15211 popallfiles();
15212
15213 /* from var.c: */
15214 unwindlocalvars(NULL);
15215}
15216
15217#if PROFILE 15231#if PROFILE
15218static short profile_buf[16384]; 15232static short profile_buf[16384];
15219extern int etext(); 15233extern int etext();
@@ -15264,7 +15278,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
15264 15278
15265 e = exception_type; 15279 e = exception_type;
15266 s = state; 15280 s = state;
15267 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) { 15281 if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
15268 exitshell(); 15282 exitshell();
15269 } 15283 }
15270 15284
diff --git a/shell/ash_test/ash-misc/exitcode_trap3.right b/shell/ash_test/ash-misc/exitcode_trap3.right
new file mode 100644
index 000000000..f275cdc45
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_trap3.right
@@ -0,0 +1,2 @@
1TERM
242:42
diff --git a/shell/ash_test/ash-misc/exitcode_trap3.tests b/shell/ash_test/ash-misc/exitcode_trap3.tests
new file mode 100755
index 000000000..98745e463
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_trap3.tests
@@ -0,0 +1,9 @@
1# "return" in trap should not use last command's exitcode,
2# but exitcode on entering the trap.
3trap "echo TERM;return" term
4f() {
5 (sleep 1; kill $$) &
6 until (exit 42) do (exit 42); done
7}
8f
9echo 42:$?
diff --git a/shell/ash_test/ash-misc/exitcode_trap4.right b/shell/ash_test/ash-misc/exitcode_trap4.right
new file mode 100644
index 000000000..ed6989593
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_trap4.right
@@ -0,0 +1,2 @@
1TERM
211:11
diff --git a/shell/ash_test/ash-misc/exitcode_trap4.tests b/shell/ash_test/ash-misc/exitcode_trap4.tests
new file mode 100755
index 000000000..36dba90bd
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_trap4.tests
@@ -0,0 +1,8 @@
1# "return" in trap sets $? after trap
2trap "echo TERM;return 11" term
3f() {
4 (sleep 1; kill $$) &
5 until (exit 42) do (exit 42); done
6}
7f
8echo 11:$?
diff --git a/shell/ash_test/ash-misc/exitcode_trap5.right b/shell/ash_test/ash-misc/exitcode_trap5.right
new file mode 100644
index 000000000..1ad4443b4
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_trap5.right
@@ -0,0 +1,3 @@
1TERM
2Nested
3Zero:0
diff --git a/shell/ash_test/ash-misc/exitcode_trap5.tests b/shell/ash_test/ash-misc/exitcode_trap5.tests
new file mode 100755
index 000000000..332a06463
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_trap5.tests
@@ -0,0 +1,10 @@
1# "return" in trap sets $? after trap...
2# ...but not a nested one!
3g() { echo Nested; return 22; }
4trap "echo TERM;false;g" term
5f() {
6 (kill $$) &
7 sleep 1
8}
9f
10echo Zero:$?
diff --git a/shell/ash_test/ash-misc/exitcode_trap6.right b/shell/ash_test/ash-misc/exitcode_trap6.right
new file mode 100644
index 000000000..b76c1908e
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_trap6.right
@@ -0,0 +1,2 @@
1INT
242:42
diff --git a/shell/ash_test/ash-misc/exitcode_trap6.tests b/shell/ash_test/ash-misc/exitcode_trap6.tests
new file mode 100755
index 000000000..15fb99d2d
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode_trap6.tests
@@ -0,0 +1,11 @@
1# "exit" in trap should not use last command's exitcode,
2# but exitcode on entering the trap.
3# Nested trap should not interfere with this.
4$THIS_SH -c '
5 trap "echo INT" int
6 trap "kill -int $$;exit" term
7 kill $$ &
8 (exit 42)
9 wait
10'
11echo 42:$?
diff --git a/shell/hush.c b/shell/hush.c
index bced388bf..357a354e2 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -988,6 +988,10 @@ struct globals {
988# define G_fatal_sig_mask 0 988# define G_fatal_sig_mask 0
989#endif 989#endif
990#if ENABLE_HUSH_TRAP 990#if ENABLE_HUSH_TRAP
991 int pre_trap_exitcode;
992# if ENABLE_HUSH_FUNCTIONS
993 int return_exitcode;
994# endif
991 char **traps; /* char *traps[NSIG] */ 995 char **traps; /* char *traps[NSIG] */
992# define G_traps G.traps 996# define G_traps G.traps
993#else 997#else
@@ -2097,25 +2101,35 @@ static int check_and_run_traps(void)
2097 } while (sig < NSIG); 2101 } while (sig < NSIG);
2098 break; 2102 break;
2099 got_sig: 2103 got_sig:
2104#if ENABLE_HUSH_TRAP
2100 if (G_traps && G_traps[sig]) { 2105 if (G_traps && G_traps[sig]) {
2101 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]); 2106 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]);
2102 if (G_traps[sig][0]) { 2107 if (G_traps[sig][0]) {
2103 /* We have user-defined handler */ 2108 /* We have user-defined handler */
2104 smalluint save_rcode; 2109 smalluint save_rcode;
2110 int save_pre;
2105 char *argv[3]; 2111 char *argv[3];
2106 /* argv[0] is unused */ 2112 /* argv[0] is unused */
2107 argv[1] = xstrdup(G_traps[sig]); 2113 argv[1] = xstrdup(G_traps[sig]);
2108 /* why strdup? trap can modify itself: trap 'trap "echo oops" INT' INT */ 2114 /* why strdup? trap can modify itself: trap 'trap "echo oops" INT' INT */
2109 argv[2] = NULL; 2115 argv[2] = NULL;
2110 save_rcode = G.last_exitcode; 2116 save_pre = G.pre_trap_exitcode;
2117 G.pre_trap_exitcode = save_rcode = G.last_exitcode;
2111 builtin_eval(argv); 2118 builtin_eval(argv);
2112 free(argv[1]); 2119 free(argv[1]);
2113//FIXME: shouldn't it be set to 128 + sig instead? 2120 G.pre_trap_exitcode = save_pre;
2114 G.last_exitcode = save_rcode; 2121 G.last_exitcode = save_rcode;
2122# if ENABLE_HUSH_FUNCTIONS
2123 if (G.return_exitcode >= 0) {
2124 debug_printf_exec("trap exitcode:%d\n", G.return_exitcode);
2125 G.last_exitcode = G.return_exitcode;
2126 }
2127# endif
2115 last_sig = sig; 2128 last_sig = sig;
2116 } /* else: "" trap, ignoring signal */ 2129 } /* else: "" trap, ignoring signal */
2117 continue; 2130 continue;
2118 } 2131 }
2132#endif
2119 /* not a trap: special action */ 2133 /* not a trap: special action */
2120 switch (sig) { 2134 switch (sig) {
2121 case SIGINT: 2135 case SIGINT:
@@ -8127,6 +8141,10 @@ static int run_function(const struct function *funcp, char **argv)
8127 IF_HUSH_LOCAL(leave_var_nest_level();) 8141 IF_HUSH_LOCAL(leave_var_nest_level();)
8128 8142
8129 G_flag_return_in_progress = sv_flg; 8143 G_flag_return_in_progress = sv_flg;
8144# if ENABLE_HUSH_TRAP
8145 debug_printf_exec("G.return_exitcode=-1\n");
8146 G.return_exitcode = -1; /* invalidate stashed return value */
8147# endif
8130 8148
8131 restore_G_args(&sv, argv); 8149 restore_G_args(&sv, argv);
8132 8150
@@ -9628,6 +9646,9 @@ static int run_list(struct pipe *pi)
9628 debug_printf_exec(": builtin/func exitcode %d\n", rcode); 9646 debug_printf_exec(": builtin/func exitcode %d\n", rcode);
9629 G.last_exitcode = rcode; 9647 G.last_exitcode = rcode;
9630 check_and_run_traps(); 9648 check_and_run_traps();
9649#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
9650 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
9651#endif
9631#if ENABLE_HUSH_LOOPS 9652#if ENABLE_HUSH_LOOPS
9632 /* Was it "break" or "continue"? */ 9653 /* Was it "break" or "continue"? */
9633 if (G.flag_break_continue) { 9654 if (G.flag_break_continue) {
@@ -9684,6 +9705,9 @@ static int run_list(struct pipe *pi)
9684 check_traps: 9705 check_traps:
9685 G.last_exitcode = rcode; 9706 G.last_exitcode = rcode;
9686 check_and_run_traps(); 9707 check_and_run_traps();
9708#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
9709 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
9710#endif
9687 } 9711 }
9688 9712
9689 /* Handle "set -e" */ 9713 /* Handle "set -e" */
@@ -9907,6 +9931,12 @@ int hush_main(int argc, char **argv)
9907 INIT_G(); 9931 INIT_G();
9908 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ 9932 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
9909 G.last_exitcode = EXIT_SUCCESS; 9933 G.last_exitcode = EXIT_SUCCESS;
9934#if ENABLE_HUSH_TRAP
9935# if ENABLE_HUSH_FUNCTIONS
9936 G.return_exitcode = -1;
9937# endif
9938 G.pre_trap_exitcode = -1;
9939#endif
9910 9940
9911#if ENABLE_HUSH_FAST 9941#if ENABLE_HUSH_FAST
9912 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ 9942 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
@@ -10552,8 +10582,13 @@ static int FAST_FUNC builtin_exit(char **argv)
10552 10582
10553 /* note: EXIT trap is run by hush_exit */ 10583 /* note: EXIT trap is run by hush_exit */
10554 argv = skip_dash_dash(argv); 10584 argv = skip_dash_dash(argv);
10555 if (argv[0] == NULL) 10585 if (argv[0] == NULL) {
10586#if ENABLE_HUSH_TRAP
10587 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
10588 hush_exit(G.pre_trap_exitcode);
10589#endif
10556 hush_exit(G.last_exitcode); 10590 hush_exit(G.last_exitcode);
10591 }
10557 /* mimic bash: exit 123abc == exit 255 + error msg */ 10592 /* mimic bash: exit 123abc == exit 255 + error msg */
10558 xfunc_error_retval = 255; 10593 xfunc_error_retval = 255;
10559 /* bash: exit -2 == exit 254, no error msg */ 10594 /* bash: exit -2 == exit 254, no error msg */
@@ -11745,6 +11780,12 @@ static int FAST_FUNC builtin_return(char **argv)
11745 * 255 <== we also do this 11780 * 255 <== we also do this
11746 */ 11781 */
11747 rc = parse_numeric_argv1(argv, G.last_exitcode, 0); 11782 rc = parse_numeric_argv1(argv, G.last_exitcode, 0);
11783# if ENABLE_HUSH_TRAP
11784 if (argv[1]) { /* "return ARG" inside a running trap sets $? */
11785 debug_printf_exec("G.return_exitcode=%d\n", rc);
11786 G.return_exitcode = rc;
11787 }
11788# endif
11748 return rc; 11789 return rc;
11749} 11790}
11750#endif 11791#endif
diff --git a/shell/hush_test/hush-misc/exitcode_trap3.right b/shell/hush_test/hush-misc/exitcode_trap3.right
new file mode 100644
index 000000000..f275cdc45
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_trap3.right
@@ -0,0 +1,2 @@
1TERM
242:42
diff --git a/shell/hush_test/hush-misc/exitcode_trap3.tests b/shell/hush_test/hush-misc/exitcode_trap3.tests
new file mode 100755
index 000000000..98745e463
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_trap3.tests
@@ -0,0 +1,9 @@
1# "return" in trap should not use last command's exitcode,
2# but exitcode on entering the trap.
3trap "echo TERM;return" term
4f() {
5 (sleep 1; kill $$) &
6 until (exit 42) do (exit 42); done
7}
8f
9echo 42:$?
diff --git a/shell/hush_test/hush-misc/exitcode_trap4.right b/shell/hush_test/hush-misc/exitcode_trap4.right
new file mode 100644
index 000000000..ed6989593
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_trap4.right
@@ -0,0 +1,2 @@
1TERM
211:11
diff --git a/shell/hush_test/hush-misc/exitcode_trap4.tests b/shell/hush_test/hush-misc/exitcode_trap4.tests
new file mode 100755
index 000000000..36dba90bd
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_trap4.tests
@@ -0,0 +1,8 @@
1# "return" in trap sets $? after trap
2trap "echo TERM;return 11" term
3f() {
4 (sleep 1; kill $$) &
5 until (exit 42) do (exit 42); done
6}
7f
8echo 11:$?
diff --git a/shell/hush_test/hush-misc/exitcode_trap5.right b/shell/hush_test/hush-misc/exitcode_trap5.right
new file mode 100644
index 000000000..1ad4443b4
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_trap5.right
@@ -0,0 +1,3 @@
1TERM
2Nested
3Zero:0
diff --git a/shell/hush_test/hush-misc/exitcode_trap5.tests b/shell/hush_test/hush-misc/exitcode_trap5.tests
new file mode 100755
index 000000000..332a06463
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_trap5.tests
@@ -0,0 +1,10 @@
1# "return" in trap sets $? after trap...
2# ...but not a nested one!
3g() { echo Nested; return 22; }
4trap "echo TERM;false;g" term
5f() {
6 (kill $$) &
7 sleep 1
8}
9f
10echo Zero:$?
diff --git a/shell/hush_test/hush-misc/exitcode_trap6.right b/shell/hush_test/hush-misc/exitcode_trap6.right
new file mode 100644
index 000000000..b76c1908e
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_trap6.right
@@ -0,0 +1,2 @@
1INT
242:42
diff --git a/shell/hush_test/hush-misc/exitcode_trap6.tests b/shell/hush_test/hush-misc/exitcode_trap6.tests
new file mode 100755
index 000000000..15fb99d2d
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode_trap6.tests
@@ -0,0 +1,11 @@
1# "exit" in trap should not use last command's exitcode,
2# but exitcode on entering the trap.
3# Nested trap should not interfere with this.
4$THIS_SH -c '
5 trap "echo INT" int
6 trap "kill -int $$;exit" term
7 kill $$ &
8 (exit 42)
9 wait
10'
11echo 42:$?