aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2016-09-28 19:41:57 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2016-09-28 19:41:57 +0200
commiteb17b6f6c99df4a132742facd43a9485bb7ac5a0 (patch)
treee433e1a453bcf0728e56e114783718071de82af6
parent1c32e49bdf74dfe47cae108d06702996ead052f0 (diff)
downloadbusybox-w32-eb17b6f6c99df4a132742facd43a9485bb7ac5a0.tar.gz
busybox-w32-eb17b6f6c99df4a132742facd43a9485bb7ac5a0.tar.bz2
busybox-w32-eb17b6f6c99df4a132742facd43a9485bb7ac5a0.zip
ash: eval: Return status in eval functions
Backported from dash: eval: Return status in eval functions The exit status is currently clobbered too early for case statements and loops. This patch fixes it by making the eval functions return the current exit status and setting them in one place -- evaltree. Harald van Dijk pointed out a number of bugs in the original patch. function old new delta evalcommand 1226 1242 +16 cmdloop 383 398 +15 evalfor 223 227 +4 evalcase 271 275 +4 localcmd 348 350 +2 evaltreenr 927 928 +1 evaltree 927 928 +1 evalsubshell 150 151 +1 evalpipe 356 357 +1 parse_command 1585 1584 -1 evalloop 177 164 -13 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 9/2 up/down: 45/-14) Total: 31 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash.c136
-rw-r--r--shell/ash_test/ash-misc/exitcode1.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode1.tests2
-rw-r--r--shell/hush_test/hush-misc/exitcode1.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode1.tests2
5 files changed, 89 insertions, 55 deletions
diff --git a/shell/ash.c b/shell/ash.c
index a113ff155..ec006af7d 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -5864,7 +5864,7 @@ struct backcmd { /* result of evalbackcmd */
5864/* These forward decls are needed to use "eval" code for backticks handling: */ 5864/* These forward decls are needed to use "eval" code for backticks handling: */
5865static uint8_t back_exitstatus; /* exit status of backquoted command */ 5865static uint8_t back_exitstatus; /* exit status of backquoted command */
5866#define EV_EXIT 01 /* exit after evaluating tree */ 5866#define EV_EXIT 01 /* exit after evaluating tree */
5867static void evaltree(union node *, int); 5867static int evaltree(union node *, int);
5868 5868
5869static void FAST_FUNC 5869static void FAST_FUNC
5870evalbackcmd(union node *n, struct backcmd *result) 5870evalbackcmd(union node *n, struct backcmd *result)
@@ -8412,13 +8412,13 @@ dotrap(void)
8412} 8412}
8413 8413
8414/* forward declarations - evaluation is fairly recursive business... */ 8414/* forward declarations - evaluation is fairly recursive business... */
8415static void evalloop(union node *, int); 8415static int evalloop(union node *, int);
8416static void evalfor(union node *, int); 8416static int evalfor(union node *, int);
8417static void evalcase(union node *, int); 8417static int evalcase(union node *, int);
8418static void evalsubshell(union node *, int); 8418static int evalsubshell(union node *, int);
8419static void expredir(union node *); 8419static void expredir(union node *);
8420static void evalpipe(union node *, int); 8420static int evalpipe(union node *, int);
8421static void evalcommand(union node *, int); 8421static int evalcommand(union node *, int);
8422static int evalbltin(const struct builtincmd *, int, char **); 8422static int evalbltin(const struct builtincmd *, int, char **);
8423static void prehash(union node *); 8423static void prehash(union node *);
8424 8424
@@ -8426,14 +8426,14 @@ static void prehash(union node *);
8426 * Evaluate a parse tree. The value is left in the global variable 8426 * Evaluate a parse tree. The value is left in the global variable
8427 * exitstatus. 8427 * exitstatus.
8428 */ 8428 */
8429static void 8429static int
8430evaltree(union node *n, int flags) 8430evaltree(union node *n, int flags)
8431{ 8431{
8432 struct jmploc *volatile savehandler = exception_handler; 8432 struct jmploc *volatile savehandler = exception_handler;
8433 struct jmploc jmploc; 8433 struct jmploc jmploc;
8434 int checkexit = 0; 8434 int checkexit = 0;
8435 void (*evalfn)(union node *, int); 8435 int (*evalfn)(union node *, int);
8436 int status; 8436 int status = 0;
8437 int int_level; 8437 int int_level;
8438 8438
8439 SAVE_INT(int_level); 8439 SAVE_INT(int_level);
@@ -8470,15 +8470,13 @@ evaltree(union node *n, int flags)
8470 break; 8470 break;
8471#endif 8471#endif
8472 case NNOT: 8472 case NNOT:
8473 evaltree(n->nnot.com, EV_TESTED); 8473 status = !evaltree(n->nnot.com, EV_TESTED);
8474 status = !exitstatus;
8475 goto setstatus; 8474 goto setstatus;
8476 case NREDIR: 8475 case NREDIR:
8477 expredir(n->nredir.redirect); 8476 expredir(n->nredir.redirect);
8478 status = redirectsafe(n->nredir.redirect, REDIR_PUSH); 8477 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8479 if (!status) { 8478 if (!status) {
8480 evaltree(n->nredir.n, flags & EV_TESTED); 8479 status = evaltree(n->nredir.n, flags & EV_TESTED);
8481 status = exitstatus;
8482 } 8480 }
8483 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); 8481 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
8484 goto setstatus; 8482 goto setstatus;
@@ -8518,27 +8516,24 @@ evaltree(union node *n, int flags)
8518#error NOR + 1 != NSEMI 8516#error NOR + 1 != NSEMI
8519#endif 8517#endif
8520 unsigned is_or = n->type - NAND; 8518 unsigned is_or = n->type - NAND;
8521 evaltree( 8519 status = evaltree(
8522 n->nbinary.ch1, 8520 n->nbinary.ch1,
8523 (flags | ((is_or >> 1) - 1)) & EV_TESTED 8521 (flags | ((is_or >> 1) - 1)) & EV_TESTED
8524 ); 8522 );
8525 if ((!exitstatus) == is_or) 8523 if (!status == is_or || evalskip)
8526 break; 8524 break;
8527 if (!evalskip) { 8525 n = n->nbinary.ch2;
8528 n = n->nbinary.ch2;
8529 evaln: 8526 evaln:
8530 evalfn = evaltree; 8527 evalfn = evaltree;
8531 calleval: 8528 calleval:
8532 evalfn(n, flags); 8529 status = evalfn(n, flags);
8533 break; 8530 goto setstatus;
8534 }
8535 break;
8536 } 8531 }
8537 case NIF: 8532 case NIF:
8538 evaltree(n->nif.test, EV_TESTED); 8533 status = evaltree(n->nif.test, EV_TESTED);
8539 if (evalskip) 8534 if (evalskip)
8540 break; 8535 break;
8541 if (exitstatus == 0) { 8536 if (!status) {
8542 n = n->nif.ifpart; 8537 n = n->nif.ifpart;
8543 goto evaln; 8538 goto evaln;
8544 } 8539 }
@@ -8546,11 +8541,14 @@ evaltree(union node *n, int flags)
8546 n = n->nif.elsepart; 8541 n = n->nif.elsepart;
8547 goto evaln; 8542 goto evaln;
8548 } 8543 }
8549 goto success; 8544 status = 0;
8545 goto setstatus;
8550 case NDEFUN: 8546 case NDEFUN:
8551 defun(n->narg.text, n->narg.next); 8547 defun(n->narg.text, n->narg.next);
8552 success: 8548 /* Not necessary. To test it:
8553 status = 0; 8549 * "false; f() { qwerty; }; echo $?" should print 0.
8550 */
8551 /* status = 0; */
8554 setstatus: 8552 setstatus:
8555 exitstatus = status; 8553 exitstatus = status;
8556 break; 8554 break;
@@ -8565,7 +8563,7 @@ evaltree(union node *n, int flags)
8565 */ 8563 */
8566 if (pending_sig && dotrap()) 8564 if (pending_sig && dotrap())
8567 goto exexit; 8565 goto exexit;
8568 if (checkexit & exitstatus) 8566 if (checkexit & status)
8569 evalskip |= SKIPEVAL; 8567 evalskip |= SKIPEVAL;
8570 8568
8571 if (flags & EV_EXIT) { 8569 if (flags & EV_EXIT) {
@@ -8575,14 +8573,16 @@ evaltree(union node *n, int flags)
8575 8573
8576 RESTORE_INT(int_level); 8574 RESTORE_INT(int_level);
8577 TRACE(("leaving evaltree (no interrupts)\n")); 8575 TRACE(("leaving evaltree (no interrupts)\n"));
8576
8577 return exitstatus;
8578} 8578}
8579 8579
8580#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) 8580#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8581static 8581static
8582#endif 8582#endif
8583void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); 8583int evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8584 8584
8585static void 8585static int
8586evalloop(union node *n, int flags) 8586evalloop(union node *n, int flags)
8587{ 8587{
8588 int status; 8588 int status;
@@ -8593,7 +8593,7 @@ evalloop(union node *n, int flags)
8593 for (;;) { 8593 for (;;) {
8594 int i; 8594 int i;
8595 8595
8596 evaltree(n->nbinary.ch1, EV_TESTED); 8596 i = evaltree(n->nbinary.ch1, EV_TESTED);
8597 if (evalskip) { 8597 if (evalskip) {
8598 skipping: 8598 skipping:
8599 if (evalskip == SKIPCONT && --skipcount <= 0) { 8599 if (evalskip == SKIPCONT && --skipcount <= 0) {
@@ -8604,27 +8604,28 @@ evalloop(union node *n, int flags)
8604 evalskip = 0; 8604 evalskip = 0;
8605 break; 8605 break;
8606 } 8606 }
8607 i = exitstatus;
8608 if (n->type != NWHILE) 8607 if (n->type != NWHILE)
8609 i = !i; 8608 i = !i;
8610 if (i != 0) 8609 if (i != 0)
8611 break; 8610 break;
8612 evaltree(n->nbinary.ch2, flags); 8611 status = evaltree(n->nbinary.ch2, flags);
8613 status = exitstatus;
8614 if (evalskip) 8612 if (evalskip)
8615 goto skipping; 8613 goto skipping;
8616 } 8614 }
8617 loopnest--;
8618 exitstatus = status; 8615 exitstatus = status;
8616 loopnest--;
8617
8618 return status;
8619} 8619}
8620 8620
8621static void 8621static int
8622evalfor(union node *n, int flags) 8622evalfor(union node *n, int flags)
8623{ 8623{
8624 struct arglist arglist; 8624 struct arglist arglist;
8625 union node *argp; 8625 union node *argp;
8626 struct strlist *sp; 8626 struct strlist *sp;
8627 struct stackmark smark; 8627 struct stackmark smark;
8628 int status = 0;
8628 8629
8629 setstackmark(&smark); 8630 setstackmark(&smark);
8630 arglist.list = NULL; 8631 arglist.list = NULL;
@@ -8637,12 +8638,11 @@ evalfor(union node *n, int flags)
8637 } 8638 }
8638 *arglist.lastp = NULL; 8639 *arglist.lastp = NULL;
8639 8640
8640 exitstatus = 0;
8641 loopnest++; 8641 loopnest++;
8642 flags &= EV_TESTED; 8642 flags &= EV_TESTED;
8643 for (sp = arglist.list; sp; sp = sp->next) { 8643 for (sp = arglist.list; sp; sp = sp->next) {
8644 setvar0(n->nfor.var, sp->text); 8644 setvar0(n->nfor.var, sp->text);
8645 evaltree(n->nfor.body, flags); 8645 status = evaltree(n->nfor.body, flags);
8646 if (evalskip) { 8646 if (evalskip) {
8647 if (evalskip == SKIPCONT && --skipcount <= 0) { 8647 if (evalskip == SKIPCONT && --skipcount <= 0) {
8648 evalskip = 0; 8648 evalskip = 0;
@@ -8656,26 +8656,32 @@ evalfor(union node *n, int flags)
8656 loopnest--; 8656 loopnest--;
8657 out: 8657 out:
8658 popstackmark(&smark); 8658 popstackmark(&smark);
8659
8660 return status;
8659} 8661}
8660 8662
8661static void 8663static int
8662evalcase(union node *n, int flags) 8664evalcase(union node *n, int flags)
8663{ 8665{
8664 union node *cp; 8666 union node *cp;
8665 union node *patp; 8667 union node *patp;
8666 struct arglist arglist; 8668 struct arglist arglist;
8667 struct stackmark smark; 8669 struct stackmark smark;
8670 int status = 0;
8668 8671
8669 setstackmark(&smark); 8672 setstackmark(&smark);
8670 arglist.list = NULL; 8673 arglist.list = NULL;
8671 arglist.lastp = &arglist.list; 8674 arglist.lastp = &arglist.list;
8672 expandarg(n->ncase.expr, &arglist, EXP_TILDE); 8675 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
8673 exitstatus = 0;
8674 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { 8676 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8675 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { 8677 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
8676 if (casematch(patp, arglist.list->text)) { 8678 if (casematch(patp, arglist.list->text)) {
8677 if (evalskip == 0) { 8679 /* Ensure body is non-empty as otherwise
8678 evaltree(cp->nclist.body, flags); 8680 * EV_EXIT may prevent us from setting the
8681 * exit status.
8682 */
8683 if (evalskip == 0 && cp->nclist.body) {
8684 status = evaltree(cp->nclist.body, flags);
8679 } 8685 }
8680 goto out; 8686 goto out;
8681 } 8687 }
@@ -8683,12 +8689,14 @@ evalcase(union node *n, int flags)
8683 } 8689 }
8684 out: 8690 out:
8685 popstackmark(&smark); 8691 popstackmark(&smark);
8692
8693 return status;
8686} 8694}
8687 8695
8688/* 8696/*
8689 * Kick off a subshell to evaluate a tree. 8697 * Kick off a subshell to evaluate a tree.
8690 */ 8698 */
8691static void 8699static int
8692evalsubshell(union node *n, int flags) 8700evalsubshell(union node *n, int flags)
8693{ 8701{
8694 struct job *jp; 8702 struct job *jp;
@@ -8714,8 +8722,8 @@ evalsubshell(union node *n, int flags)
8714 status = 0; 8722 status = 0;
8715 if (!backgnd) 8723 if (!backgnd)
8716 status = waitforjob(jp); 8724 status = waitforjob(jp);
8717 exitstatus = status;
8718 INT_ON; 8725 INT_ON;
8726 return status;
8719} 8727}
8720 8728
8721/* 8729/*
@@ -8788,7 +8796,7 @@ expredir(union node *n)
8788 * of the shell, which make the last process in a pipeline the parent 8796 * of the shell, which make the last process in a pipeline the parent
8789 * of all the rest.) 8797 * of all the rest.)
8790 */ 8798 */
8791static void 8799static int
8792evalpipe(union node *n, int flags) 8800evalpipe(union node *n, int flags)
8793{ 8801{
8794 struct job *jp; 8802 struct job *jp;
@@ -8796,6 +8804,7 @@ evalpipe(union node *n, int flags)
8796 int pipelen; 8804 int pipelen;
8797 int prevfd; 8805 int prevfd;
8798 int pip[2]; 8806 int pip[2];
8807 int status = 0;
8799 8808
8800 TRACE(("evalpipe(0x%lx) called\n", (long)n)); 8809 TRACE(("evalpipe(0x%lx) called\n", (long)n));
8801 pipelen = 0; 8810 pipelen = 0;
@@ -8838,10 +8847,12 @@ evalpipe(union node *n, int flags)
8838 close(pip[1]); 8847 close(pip[1]);
8839 } 8848 }
8840 if (n->npipe.pipe_backgnd == 0) { 8849 if (n->npipe.pipe_backgnd == 0) {
8841 exitstatus = waitforjob(jp); 8850 status = waitforjob(jp);
8842 TRACE(("evalpipe: job done exit status %d\n", exitstatus)); 8851 TRACE(("evalpipe: job done exit status %d\n", status));
8843 } 8852 }
8844 INT_ON; 8853 INT_ON;
8854
8855 return status;
8845} 8856}
8846 8857
8847/* 8858/*
@@ -9328,7 +9339,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
9328 * as POSIX mandates */ 9339 * as POSIX mandates */
9329 return back_exitstatus; 9340 return back_exitstatus;
9330} 9341}
9331static void 9342static int
9332evalcommand(union node *cmd, int flags) 9343evalcommand(union node *cmd, int flags)
9333{ 9344{
9334 static const struct builtincmd null_bltin = { 9345 static const struct builtincmd null_bltin = {
@@ -9511,9 +9522,9 @@ evalcommand(union node *cmd, int flags)
9511 jp = makejob(/*cmd,*/ 1); 9522 jp = makejob(/*cmd,*/ 1);
9512 if (forkshell(jp, cmd, FORK_FG) != 0) { 9523 if (forkshell(jp, cmd, FORK_FG) != 0) {
9513 /* parent */ 9524 /* parent */
9514 exitstatus = waitforjob(jp); 9525 status = waitforjob(jp);
9515 INT_ON; 9526 INT_ON;
9516 TRACE(("forked child exited with %d\n", exitstatus)); 9527 TRACE(("forked child exited with %d\n", status));
9517 break; 9528 break;
9518 } 9529 }
9519 /* child */ 9530 /* child */
@@ -9559,7 +9570,7 @@ evalcommand(union node *cmd, int flags)
9559 } 9570 }
9560 FORCE_INT_ON; 9571 FORCE_INT_ON;
9561 } 9572 }
9562 break; 9573 goto readstatus;
9563 9574
9564 case CMDFUNCTION: 9575 case CMDFUNCTION:
9565 listsetvar(varlist.list, 0); 9576 listsetvar(varlist.list, 0);
@@ -9567,6 +9578,8 @@ evalcommand(union node *cmd, int flags)
9567 dowait(DOWAIT_NONBLOCK, NULL); 9578 dowait(DOWAIT_NONBLOCK, NULL);
9568 if (evalfun(cmdentry.u.func, argc, argv, flags)) 9579 if (evalfun(cmdentry.u.func, argc, argv, flags))
9569 goto raise; 9580 goto raise;
9581 readstatus:
9582 status = exitstatus;
9570 break; 9583 break;
9571 } /* switch */ 9584 } /* switch */
9572 9585
@@ -9580,6 +9593,8 @@ evalcommand(union node *cmd, int flags)
9580 setvar0("_", lastarg); 9593 setvar0("_", lastarg);
9581 } 9594 }
9582 popstackmark(&smark); 9595 popstackmark(&smark);
9596
9597 return status;
9583} 9598}
9584 9599
9585static int 9600static int
@@ -12205,13 +12220,18 @@ evalstring(char *s, int mask)
12205 union node *n; 12220 union node *n;
12206 struct stackmark smark; 12221 struct stackmark smark;
12207 int skip; 12222 int skip;
12223// int status;
12208 12224
12209 setinputstring(s); 12225 setinputstring(s);
12210 setstackmark(&smark); 12226 setstackmark(&smark);
12211 12227
12212 skip = 0; 12228 skip = 0;
12213 while ((n = parsecmd(0)) != NODE_EOF) { 12229 while ((n = parsecmd(0)) != NODE_EOF) {
12214 evaltree(n, 0); 12230 int i;
12231
12232 i = evaltree(n, 0);
12233// if (n)
12234// status = i;
12215 popstackmark(&smark); 12235 popstackmark(&smark);
12216 skip = evalskip; 12236 skip = evalskip;
12217 if (skip) 12237 if (skip)
@@ -12222,6 +12242,7 @@ evalstring(char *s, int mask)
12222 skip &= mask; 12242 skip &= mask;
12223 evalskip = skip; 12243 evalskip = skip;
12224 return skip; 12244 return skip;
12245// return status;
12225} 12246}
12226 12247
12227/* 12248/*
@@ -12264,6 +12285,7 @@ cmdloop(int top)
12264 union node *n; 12285 union node *n;
12265 struct stackmark smark; 12286 struct stackmark smark;
12266 int inter; 12287 int inter;
12288 int status = 0;
12267 int numeof = 0; 12289 int numeof = 0;
12268 12290
12269 TRACE(("cmdloop(%d) called\n", top)); 12291 TRACE(("cmdloop(%d) called\n", top));
@@ -12295,10 +12317,14 @@ cmdloop(int top)
12295 } 12317 }
12296 numeof++; 12318 numeof++;
12297 } else if (nflag == 0) { 12319 } else if (nflag == 0) {
12320 int i;
12321
12298 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ 12322 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12299 job_warning >>= 1; 12323 job_warning >>= 1;
12300 numeof = 0; 12324 numeof = 0;
12301 evaltree(n, 0); 12325 i = evaltree(n, 0);
12326 if (n)
12327 status = i;
12302 } 12328 }
12303 popstackmark(&smark); 12329 popstackmark(&smark);
12304 skip = evalskip; 12330 skip = evalskip;
@@ -12308,7 +12334,7 @@ cmdloop(int top)
12308 return skip & SKIPEVAL; 12334 return skip & SKIPEVAL;
12309 } 12335 }
12310 } 12336 }
12311 return 0; 12337 return status;
12312} 12338}
12313 12339
12314/* 12340/*
diff --git a/shell/ash_test/ash-misc/exitcode1.right b/shell/ash_test/ash-misc/exitcode1.right
new file mode 100644
index 000000000..e5fefefda
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode1.right
@@ -0,0 +1,2 @@
1One:1
2Zero:0
diff --git a/shell/ash_test/ash-misc/exitcode1.tests b/shell/ash_test/ash-misc/exitcode1.tests
new file mode 100755
index 000000000..dc8619d8b
--- /dev/null
+++ b/shell/ash_test/ash-misc/exitcode1.tests
@@ -0,0 +1,2 @@
1false || case a in a) echo One:$?;; esac
2echo Zero:$?
diff --git a/shell/hush_test/hush-misc/exitcode1.right b/shell/hush_test/hush-misc/exitcode1.right
new file mode 100644
index 000000000..e5fefefda
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode1.right
@@ -0,0 +1,2 @@
1One:1
2Zero:0
diff --git a/shell/hush_test/hush-misc/exitcode1.tests b/shell/hush_test/hush-misc/exitcode1.tests
new file mode 100755
index 000000000..dc8619d8b
--- /dev/null
+++ b/shell/hush_test/hush-misc/exitcode1.tests
@@ -0,0 +1,2 @@
1false || case a in a) echo One:$?;; esac
2echo Zero:$?