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.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
diff --git a/src/eval.c b/src/eval.c
index 30c05f9..7498f9d 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -81,16 +81,16 @@
STATIC
#endif
void evaltreenr(union node *, int) __attribute__ ((__noreturn__));
-STATIC void evalloop(union node *, int);
-STATIC void evalfor(union node *, int);
-STATIC void evalcase(union node *, int);
-STATIC void evalsubshell(union node *, int);
+STATIC int evalloop(union node *, int);
+STATIC int evalfor(union node *, int);
+STATIC int evalcase(union node *, int);
+STATIC int evalsubshell(union node *, int);
STATIC void expredir(union node *);
-STATIC void evalpipe(union node *, int);
+STATIC int evalpipe(union node *, int);
#ifdef notyet
-STATIC void evalcommand(union node *, int, struct backcmd *);
+STATIC int evalcommand(union node *, int, struct backcmd *);
#else
-STATIC void evalcommand(union node *, int);
+STATIC int evalcommand(union node *, int);
#endif
STATIC int evalbltin(const struct builtincmd *, int, char **, int);
STATIC int evalfun(struct funcnode *, int, char **, int);
@@ -170,11 +170,13 @@
setstackmark(&smark);
status = 0;
- while ((n = parsecmd(0)) != NEOF) {
- evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
+ for (; (n = parsecmd(0)) != NEOF; popstackmark(&smark)) {
+ int i;
+
+ i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
if (n)
- status = exitstatus;
- popstackmark(&smark);
+ status = i;
+
if (evalskip)
break;
}
@@ -192,13 +194,13 @@
* exitstatus.
*/
-void
+int
evaltree(union node *n, int flags)
{
int checkexit = 0;
- void (*evalfn)(union node *, int);
+ int (*evalfn)(union node *, int);
unsigned isor;
- int status;
+ int status = 0;
if (n == NULL) {
TRACE(("evaltree(NULL) called\n"));
goto out;
@@ -221,8 +223,7 @@
break;
#endif
case NNOT:
- evaltree(n->nnot.com, EV_TESTED);
- status = !exitstatus;
+ status = !evaltree(n->nnot.com, EV_TESTED);
goto setstatus;
case NREDIR:
errlinno = lineno = n->nredir.linno;
@@ -230,11 +231,8 @@
lineno -= funcline - 1;
expredir(n->nredir.redirect);
pushredir(n->nredir.redirect);
- status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
- if (!status) {
- evaltree(n->nredir.n, flags & EV_TESTED);
- status = exitstatus;
- }
+ status = redirectsafe(n->nredir.redirect, REDIR_PUSH) ?:
+ evaltree(n->nredir.n, flags & EV_TESTED);
if (n->nredir.redirect)
popredir(0);
goto setstatus;
@@ -242,8 +240,8 @@
#ifdef notyet
if (eflag && !(flags & EV_TESTED))
checkexit = ~0;
- evalcommand(n, flags, (struct backcmd *)NULL);
- break;
+ status = evalcommand(n, flags, (struct backcmd *)NULL);
+ goto setstatus;
#else
evalfn = evalcommand;
checkexit:
@@ -278,43 +276,37 @@
#error NOR + 1 != NSEMI
#endif
isor = n->type - NAND;
- evaltree(
- n->nbinary.ch1,
- (flags | ((isor >> 1) - 1)) & EV_TESTED
- );
- if (!exitstatus == isor)
+ status = evaltree(n->nbinary.ch1,
+ (flags | ((isor >> 1) - 1)) & EV_TESTED);
+ if (!status == isor || evalskip)
break;
- if (!evalskip) {
- n = n->nbinary.ch2;
+ n = n->nbinary.ch2;
evaln:
- evalfn = evaltree;
+ evalfn = evaltree;
calleval:
- evalfn(n, flags);
- break;
- }
- break;
+ status = evalfn(n, flags);
+ goto setstatus;
case NIF:
- evaltree(n->nif.test, EV_TESTED);
+ status = evaltree(n->nif.test, EV_TESTED);
if (evalskip)
break;
- if (exitstatus == 0) {
+ if (!status) {
n = n->nif.ifpart;
goto evaln;
} else if (n->nif.elsepart) {
n = n->nif.elsepart;
goto evaln;
}
- goto success;
+ status = 0;
+ goto setstatus;
case NDEFUN:
defun(n);
-success:
- status = 0;
setstatus:
exitstatus = status;
break;
}
out:
- if (checkexit & exitstatus)
+ if (checkexit & status)
goto exexit;
dotrap();
@@ -323,6 +315,8 @@
exexit:
exraise(EXEXIT);
}
+
+ return exitstatus;
}
@@ -363,7 +357,7 @@
}
-STATIC void
+STATIC int
evalloop(union node *n, int flags)
{
int skip;
@@ -375,33 +369,34 @@
do {
int i;
- evaltree(n->nbinary.ch1, EV_TESTED);
+ i = evaltree(n->nbinary.ch1, EV_TESTED);
skip = skiploop();
+ if (skip == SKIPFUNC)
+ status = i;
if (skip)
continue;
- i = exitstatus;
if (n->type != NWHILE)
i = !i;
if (i != 0)
break;
- evaltree(n->nbinary.ch2, flags);
- status = exitstatus;
+ status = evaltree(n->nbinary.ch2, flags);
skip = skiploop();
} while (!(skip & ~SKIPCONT));
- if (skip != SKIPFUNC)
- exitstatus = status;
loopnest--;
+
+ return status;
}
-STATIC void
+STATIC int
evalfor(union node *n, int flags)
{
struct arglist arglist;
union node *argp;
struct strlist *sp;
struct stackmark smark;
+ int status;
errlinno = lineno = n->nfor.linno;
if (funcline)
@@ -414,28 +409,31 @@
}
*arglist.lastp = NULL;
- exitstatus = 0;
+ status = 0;
loopnest++;
flags &= EV_TESTED;
for (sp = arglist.list ; sp ; sp = sp->next) {
setvar(n->nfor.var, sp->text, 0);
- evaltree(n->nfor.body, flags);
+ status = evaltree(n->nfor.body, flags);
if (skiploop() & ~SKIPCONT)
break;
}
loopnest--;
popstackmark(&smark);
+
+ return status;
}
-STATIC void
+STATIC int
evalcase(union node *n, int flags)
{
union node *cp;
union node *patp;
struct arglist arglist;
struct stackmark smark;
+ int status = 0;
errlinno = lineno = n->ncase.linno;
if (funcline)
@@ -444,12 +442,16 @@
setstackmark(&smark);
arglist.lastp = &arglist.list;
expandarg(n->ncase.expr, &arglist, EXP_TILDE);
- exitstatus = 0;
for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
if (casematch(patp, arglist.list->text)) {
- if (evalskip == 0) {
- evaltree(cp->nclist.body, flags);
+ /* Ensure body is non-empty as otherwise
+ * EV_EXIT may prevent us from setting the
+ * exit status.
+ */
+ if (evalskip == 0 && cp->nclist.body) {
+ status = evaltree(cp->nclist.body,
+ flags);
}
goto out;
}
@@ -457,6 +459,8 @@
}
out:
popstackmark(&smark);
+
+ return status;
}
@@ -465,7 +469,7 @@
* Kick off a subshell to evaluate a tree.
*/
-STATIC void
+STATIC int
evalsubshell(union node *n, int flags)
{
struct job *jp;
@@ -494,8 +498,8 @@
status = 0;
if (! backgnd)
status = waitforjob(jp);
- exitstatus = status;
INTON;
+ return status;
}
@@ -541,7 +545,7 @@
* of all the rest.)
*/
-STATIC void
+STATIC int
evalpipe(union node *n, int flags)
{
struct job *jp;
@@ -549,6 +553,7 @@
int pipelen;
int prevfd;
int pip[2];
+ int status = 0;
TRACE(("evalpipe(0x%lx) called\n", (long)n));
pipelen = 0;
@@ -589,10 +594,12 @@
close(pip[1]);
}
if (n->npipe.backgnd == 0) {
- exitstatus = waitforjob(jp);
- TRACE(("evalpipe: job done exit status %d\n", exitstatus));
+ status = waitforjob(jp);
+ TRACE(("evalpipe: job done exit status %d\n", status));
}
INTON;
+
+ return status;
}
@@ -679,7 +686,7 @@
* Execute a simple command.
*/
-STATIC void
+STATIC int
#ifdef notyet
evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
#else
@@ -849,7 +856,7 @@
INTOFF;
jp = makejob(cmd, 1);
if (forkshell(jp, cmd, FORK_FG) != 0) {
- exitstatus = waitforjob(jp);
+ status = waitforjob(jp);
INTON;
break;
}
@@ -868,17 +875,19 @@
if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) {
if (exception == EXERROR && spclbltin <= 0) {
FORCEINTON;
- break;
+ goto readstatus;
}
raise:
longjmp(handler->loc, 1);
}
- break;
+ goto readstatus;
case CMDFUNCTION:
poplocalvars(1);
if (evalfun(cmdentry.u.func, argc, argv, flags))
goto raise;
+readstatus:
+ status = exitstatus;
break;
}
@@ -894,6 +903,8 @@
*/
setvar("_", lastarg, 0);
popstackmark(&smark);
+
+ return status;
}
STATIC int
diff --git a/src/eval.h b/src/eval.h
index 6e8acda..63e7d86 100644
--- a/src/eval.h
+++ b/src/eval.h
@@ -53,7 +53,7 @@
int evalstring(char *, int);
union node; /* BLETCH for ansi C */
-void evaltree(union node *, int);
+int evaltree(union node *, int);
void evalbackcmd(union node *, struct backcmd *);
extern int evalskip;
diff --git a/src/main.c b/src/main.c
index 497ac16..fcd3e7d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -225,11 +225,13 @@
}
numeof++;
} else if (nflag == 0) {
+ int i;
+
job_warning = (job_warning == 2) ? 1 : 0;
numeof = 0;
- evaltree(n, 0);
+ i = evaltree(n, 0);
if (n)
- status = exitstatus;
+ status = i;
}
popstackmark(&smark);