summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog1
-rw-r--r--src/eval.c4
-rw-r--r--src/redir.c45
-rw-r--r--src/redir.h3
4 files changed, 40 insertions, 13 deletions
diff --git a/ChangeLog b/ChangeLog
index 1fd184b..650899a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,7 @@
 	* Fix poplocalvar on abnormal exit from function.
 	* Do not poplocalvars prematurely on regular utilities.
 	* Move null redirect checks into caller.
+	* Fix popredir on abnormal exit from built-in.
 
 2010-05-26  Herbert Xu <herbert@gondor.apana.org.au>
 
diff --git a/src/eval.c b/src/eval.c
index 59bded9..d5c1e6c 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -219,6 +219,7 @@ evaltree(union node *n, int flags)
 		goto setstatus;
 	case NREDIR:
 		expredir(n->nredir.redirect);
+		pushredir(n->nredir.redirect);
 		status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
 		if (!status) {
 			evaltree(n->nredir.n, flags & EV_TESTED);
@@ -683,6 +684,7 @@ evalcommand(union node *cmd, int flags)
 #endif
 {
 	struct localvar_list *localvar_stop;
+	struct redirtab *redir_stop;
 	struct stackmark smark;
 	union node *argp;
 	struct arglist arglist;
@@ -740,6 +742,7 @@ evalcommand(union node *cmd, int flags)
 
 	preverrout.fd = 2;
 	expredir(cmd->ncmd.redirect);
+	redir_stop = pushredir(cmd->ncmd.redirect);
 	status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
 
 	path = vpath.text;
@@ -882,6 +885,7 @@ raise:
 out:
 	if (cmd->ncmd.redirect)
 		popredir(execcmd);
+	unwindredir(redir_stop);
 	unwindlocalvars(localvar_stop);
 	if (lastarg)
 		/* dsl: I think this is intended to be used to support
diff --git a/src/redir.c b/src/redir.c
index 16decfc..b4e49c0 100644
--- a/src/redir.c
+++ b/src/redir.c
@@ -111,20 +111,12 @@ redirect(union node *redir, int flags)
 		memory[i] = 0;
 	memory[1] = flags & REDIR_BACKQ;
 #endif
-	if (!redir) {
+	if (!redir)
 		return;
-	}
 	sv = NULL;
 	INTOFF;
-	if (likely(flags & REDIR_PUSH)) {
-		struct redirtab *q;
-		q = ckmalloc(sizeof (struct redirtab));
-		q->next = redirlist;
-		redirlist = q;
-		for (i = 0 ; i < 10 ; i++)
-			q->renamed[i] = EMPTY;
-		sv = q;
-	}
+	if (likely(flags & REDIR_PUSH))
+		sv = redirlist;
 	n = redir;
 	do {
 		newfd = openredirect(n);
@@ -373,8 +365,7 @@ RESET {
 	/*
 	 * Discard all saved file descriptors.
 	 */
-	while (redirlist)
-		popredir(0);
+	unwindredir(0);
 }
 
 #endif
@@ -485,3 +476,31 @@ redirectsafe(union node *redir, int flags)
 	RESTOREINT(saveint);
 	return err;
 }
+
+
+void unwindredir(struct redirtab *stop)
+{
+	while (redirlist != stop)
+		popredir(0);
+}
+
+
+struct redirtab *pushredir(union node *redir)
+{
+	struct redirtab *sv;
+	struct redirtab *q;
+	int i;
+
+	q = redirlist;
+	if (!redir)
+		goto out;
+
+	sv = ckmalloc(sizeof (struct redirtab));
+	sv->next = q;
+	redirlist = sv;
+	for (i = 0; i < 10; i++)
+		sv->renamed[i] = EMPTY;
+
+out:
+	return q;
+}
diff --git a/src/redir.h b/src/redir.h
index d1d160e..8e56995 100644
--- a/src/redir.h
+++ b/src/redir.h
@@ -41,10 +41,13 @@
 #endif
 #define REDIR_SAVEFD2 03	/* set preverrout */
 
+struct redirtab;
 union node;
 void redirect(union node *, int);
 void popredir(int);
 void clearredir(void);
 int savefd(int, int);
 int redirectsafe(union node *, int);
+void unwindredir(struct redirtab *stop);
+struct redirtab *pushredir(union node *redir);