From 0dec6de38b721b4faae5567f8b4643df1055a175 Mon Sep 17 00:00:00 2001
From: Denis Vlasenko <vda.linux@googlemail.com>
Date: Fri, 23 Feb 2007 21:10:47 +0000
Subject: ash: cleanup part 2.7

---
 shell/ash.c | 1632 +++++++++++++++++++++++++++++------------------------------
 1 file changed, 806 insertions(+), 826 deletions(-)

diff --git a/shell/ash.c b/shell/ash.c
index 5c9060cad..1de4cb50b 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -1593,6 +1593,21 @@ struct shparam {
 
 static struct shparam shellparam;      /* $@ current positional parameters */
 
+/*
+ * Free the list of positional parameters.
+ */
+static void
+freeparam(volatile struct shparam *param)
+{
+	char **ap;
+
+	if (param->malloc) {
+		for (ap = param->p; *ap; ap++)
+			free(*ap);
+		free(param->p);
+	}
+}
+
 #if ENABLE_ASH_GETOPTS
 static void
 getoptsreset(const char *value)
@@ -2871,29 +2886,6 @@ static const char syntax_index_table[258] = {
 #endif  /* USE_SIT_FUNCTION */
 
 
-/*      options.h */
-
-static void optschanged(void);
-static void setparam(char **);
-static void freeparam(volatile struct shparam *);
-static int shiftcmd(int, char **);
-static int setcmd(int, char **);
-static int nextopt(const char *);
-
-
-/*      redir.h      */
-
-/* flags passed to redirect */
-#define REDIR_PUSH 01           /* save previous values of file descriptors */
-#define REDIR_SAVEFD2 03       /* set preverrout */
-
-static void redirect(union node *, int);
-static void popredir(int);
-static void clearredir(int);
-static int copyfd(int, int);
-static int redirectsafe(union node *, int);
-
-
 /* ============ Alias handling */
 
 #if ENABLE_ASH_ALIAS
@@ -4622,393 +4614,744 @@ stoppedjobs(void)
 }
 
 
-/* ============ Routines to expand arguments to commands
+/* ============ redir.c
  *
- * We have to deal with backquotes, shell variables, and file metacharacters.
+ * Code for dealing with input/output redirection.
  */
 
-/*
- * expandarg flags
- */
-#define EXP_FULL        0x1     /* perform word splitting & file globbing */
-#define EXP_TILDE       0x2     /* do normal tilde expansion */
-#define EXP_VARTILDE    0x4     /* expand tildes in an assignment */
-#define EXP_REDIR       0x8     /* file glob for a redirection (1 match only) */
-#define EXP_CASE        0x10    /* keeps quotes around for CASE pattern */
-#define EXP_RECORD      0x20    /* need to record arguments for ifs breakup */
-#define EXP_VARTILDE2   0x40    /* expand tildes after colons only */
-#define EXP_WORD        0x80    /* expand word in parameter expansion */
-#define EXP_QWORD       0x100   /* expand word in quoted parameter expansion */
-/*
- * _rmescape() flags
- */
-#define RMESCAPE_ALLOC  0x1     /* Allocate a new string */
-#define RMESCAPE_GLOB   0x2     /* Add backslashes for glob */
-#define RMESCAPE_QUOTED 0x4     /* Remove CTLESC unless in quotes */
-#define RMESCAPE_GROW   0x8     /* Grow strings instead of stalloc */
-#define RMESCAPE_HEAP   0x10    /* Malloc strings instead of stalloc */
+#define EMPTY -2                /* marks an unused slot in redirtab */
+#ifndef PIPE_BUF
+# define PIPESIZE 4096          /* amount of buffering in a pipe */
+#else
+# define PIPESIZE PIPE_BUF
+#endif
 
 /*
- * Structure specifying which parts of the string should be searched
- * for IFS characters.
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
  */
-struct ifsregion {
-	struct ifsregion *next; /* next region in list */
-	int begoff;             /* offset of start of region */
-	int endoff;             /* offset of end of region */
-	int nulonly;            /* search for nul bytes only */
-};
+static int
+noclobberopen(const char *fname)
+{
+	int r, fd;
+	struct stat finfo, finfo2;
 
-struct arglist {
-	struct strlist *list;
-	struct strlist **lastp;
-};
+	/*
+	 * If the file exists and is a regular file, return an error
+	 * immediately.
+	 */
+	r = stat(fname, &finfo);
+	if (r == 0 && S_ISREG(finfo.st_mode)) {
+		errno = EEXIST;
+		return -1;
+	}
 
-/* output of current string */
-static char *expdest;
-/* list of back quote expressions */
-static struct nodelist *argbackq;
-/* first struct in list of ifs regions */
-static struct ifsregion ifsfirst;
-/* last struct in list */
-static struct ifsregion *ifslastp;
-/* holds expanded arg list */
-static struct arglist exparg;
+	/*
+	 * If the file was not present (r != 0), make sure we open it
+	 * exclusively so that if it is created before we open it, our open
+	 * will fail.  Make sure that we do not truncate an existing file.
+	 * Note that we don't turn on O_EXCL unless the stat failed -- if the
+	 * file was not a regular file, we leave O_EXCL off.
+	 */
+	if (r != 0)
+		return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
+	fd = open(fname, O_WRONLY|O_CREAT, 0666);
+
+	/* If the open failed, return the file descriptor right away. */
+	if (fd < 0)
+		return fd;
+
+	/*
+	 * OK, the open succeeded, but the file may have been changed from a
+	 * non-regular file to a regular file between the stat and the open.
+	 * We are assuming that the O_EXCL open handles the case where FILENAME
+	 * did not exist and is symlinked to an existing file between the stat
+	 * and open.
+	 */
+
+	/*
+	 * If we can open it and fstat the file descriptor, and neither check
+	 * revealed that it was a regular file, and the file has not been
+	 * replaced, return the file descriptor.
+	 */
+	if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
+	 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
+		return fd;
+
+	/* The file has been replaced.  badness. */
+	close(fd);
+	errno = EEXIST;
+	return -1;
+}
 
 /*
- * Our own itoa().
+ * Handle here documents.  Normally we fork off a process to write the
+ * data to a pipe.  If the document is short, we can stuff the data in
+ * the pipe without forking.
  */
+/* openhere needs this forward reference */
+static void expandhere(union node *arg, int fd);
 static int
-cvtnum(arith_t num)
+openhere(union node *redir)
 {
-	int len;
+	int pip[2];
+	size_t len = 0;
 
-	expdest = makestrspace(32, expdest);
-#if ENABLE_ASH_MATH_SUPPORT_64
-	len = fmtstr(expdest, 32, "%lld", (long long) num);
-#else
-	len = fmtstr(expdest, 32, "%ld", num);
+	if (pipe(pip) < 0)
+		ash_msg_and_raise_error("Pipe call failed");
+	if (redir->type == NHERE) {
+		len = strlen(redir->nhere.doc->narg.text);
+		if (len <= PIPESIZE) {
+			full_write(pip[1], redir->nhere.doc->narg.text, len);
+			goto out;
+		}
+	}
+	if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+		close(pip[0]);
+		signal(SIGINT, SIG_IGN);
+		signal(SIGQUIT, SIG_IGN);
+		signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+		signal(SIGTSTP, SIG_IGN);
 #endif
-	STADJUST(len, expdest);
-	return len;
+		signal(SIGPIPE, SIG_DFL);
+		if (redir->type == NHERE)
+			full_write(pip[1], redir->nhere.doc->narg.text, len);
+		else
+			expandhere(redir->nhere.doc, pip[1]);
+		_exit(0);
+	}
+ out:
+	close(pip[1]);
+	return pip[0];
 }
 
-static size_t
-esclen(const char *start, const char *p)
+static int
+openredirect(union node *redir)
 {
-	size_t esc = 0;
+	char *fname;
+	int f;
 
-	while (p > start && *--p == CTLESC) {
-		esc++;
+	switch (redir->nfile.type) {
+	case NFROM:
+		fname = redir->nfile.expfname;
+		f = open(fname, O_RDONLY);
+		if (f < 0)
+			goto eopen;
+		break;
+	case NFROMTO:
+		fname = redir->nfile.expfname;
+		f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
+		if (f < 0)
+			goto ecreate;
+		break;
+	case NTO:
+		/* Take care of noclobber mode. */
+		if (Cflag) {
+			fname = redir->nfile.expfname;
+			f = noclobberopen(fname);
+			if (f < 0)
+				goto ecreate;
+			break;
+		}
+		/* FALLTHROUGH */
+	case NCLOBBER:
+		fname = redir->nfile.expfname;
+		f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+		if (f < 0)
+			goto ecreate;
+		break;
+	case NAPPEND:
+		fname = redir->nfile.expfname;
+		f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
+		if (f < 0)
+			goto ecreate;
+		break;
+	default:
+#if DEBUG
+		abort();
+#endif
+		/* Fall through to eliminate warning. */
+	case NTOFD:
+	case NFROMFD:
+		f = -1;
+		break;
+	case NHERE:
+	case NXHERE:
+		f = openhere(redir);
+		break;
 	}
-	return esc;
+
+	return f;
+ ecreate:
+	ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "Directory nonexistent"));
+ eopen:
+	ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file"));
 }
 
 /*
- * Remove any CTLESC characters from a string.
+ * Copy a file descriptor to be >= to.  Returns -1
+ * if the source file descriptor is closed, EMPTY if there are no unused
+ * file descriptors left.
  */
-static char *
-_rmescapes(char *str, int flag)
+static int
+copyfd(int from, int to)
 {
-	char *p, *q, *r;
-	static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
-	unsigned inquotes;
-	int notescaped;
-	int globbing;
+	int newfd;
 
-	p = strpbrk(str, qchars);
-	if (!p) {
-		return str;
-	}
-	q = p;
-	r = str;
-	if (flag & RMESCAPE_ALLOC) {
-		size_t len = p - str;
-		size_t fulllen = len + strlen(p) + 1;
-
-		if (flag & RMESCAPE_GROW) {
-			r = makestrspace(fulllen, expdest);
-		} else if (flag & RMESCAPE_HEAP) {
-			r = ckmalloc(fulllen);
-		} else {
-			r = stalloc(fulllen);
-		}
-		q = r;
-		if (len > 0) {
-			q = memcpy(q, str, len) + len;
-		}
+	newfd = fcntl(from, F_DUPFD, to);
+	if (newfd < 0) {
+		if (errno == EMFILE)
+			return EMPTY;
+		ash_msg_and_raise_error("%d: %m", from);
 	}
-	inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
-	globbing = flag & RMESCAPE_GLOB;
-	notescaped = globbing;
-	while (*p) {
-		if (*p == CTLQUOTEMARK) {
-			inquotes = ~inquotes;
-			p++;
-			notescaped = globbing;
-			continue;
-		}
-		if (*p == '\\') {
-			/* naked back slash */
-			notescaped = 0;
-			goto copy;
-		}
-		if (*p == CTLESC) {
-			p++;
-			if (notescaped && inquotes && *p != '/') {
-				*q++ = '\\';
-			}
+	return newfd;
+}
+
+static void
+dupredirect(union node *redir, int f)
+{
+	int fd = redir->nfile.fd;
+
+	if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
+		if (redir->ndup.dupfd >= 0) {   /* if not ">&-" */
+			copyfd(redir->ndup.dupfd, fd);
 		}
-		notescaped = globbing;
- copy:
-		*q++ = *p++;
+		return;
 	}
-	*q = '\0';
-	if (flag & RMESCAPE_GROW) {
-		expdest = r;
-		STADJUST(q - r + 1, expdest);
+
+	if (f != fd) {
+		copyfd(f, fd);
+		close(f);
 	}
-	return r;
 }
-#define rmescapes(p) _rmescapes((p), 0)
-
-#define pmatch(a, b) !fnmatch((a), (b), 0)
 
 /*
- * Prepare a pattern for a expmeta (internal glob(3)) call.
- *
- * Returns an stalloced string.
+ * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
+ * old file descriptors are stashed away so that the redirection can be
+ * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
+ * standard output, and the standard error if it becomes a duplicate of
+ * stdout, is saved in memory.
  */
-static char *
-preglob(const char *pattern, int quoted, int flag)
+/* flags passed to redirect */
+#define REDIR_PUSH    01        /* save previous values of file descriptors */
+#define REDIR_SAVEFD2 03        /* set preverrout */
+static void
+redirect(union node *redir, int flags)
 {
-	flag |= RMESCAPE_GLOB;
-	if (quoted) {
-		flag |= RMESCAPE_QUOTED;
+	union node *n;
+	struct redirtab *sv;
+	int i;
+	int fd;
+	int newfd;
+	int *p;
+	nullredirs++;
+	if (!redir) {
+		return;
 	}
-	return _rmescapes((char *)pattern, flag);
+	sv = NULL;
+	INT_OFF;
+	if (flags & REDIR_PUSH) {
+		struct redirtab *q;
+		q = ckmalloc(sizeof(struct redirtab));
+		q->next = redirlist;
+		redirlist = q;
+		q->nullredirs = nullredirs - 1;
+		for (i = 0; i < 10; i++)
+			q->renamed[i] = EMPTY;
+		nullredirs = 0;
+		sv = q;
+	}
+	n = redir;
+	do {
+		fd = n->nfile.fd;
+		if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
+		 && n->ndup.dupfd == fd)
+			continue; /* redirect from/to same file descriptor */
+
+		newfd = openredirect(n);
+		if (fd == newfd)
+			continue;
+		if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
+			i = fcntl(fd, F_DUPFD, 10);
+
+			if (i == -1) {
+				i = errno;
+				if (i != EBADF) {
+					close(newfd);
+					errno = i;
+					ash_msg_and_raise_error("%d: %m", fd);
+					/* NOTREACHED */
+				}
+			} else {
+				*p = i;
+				close(fd);
+			}
+		} else {
+			close(fd);
+		}
+		dupredirect(n, newfd);
+	} while ((n = n->nfile.next));
+	INT_ON;
+	if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
+		preverrout_fd = sv->renamed[2];
 }
 
 /*
- * Put a string on the stack.
+ * Undo the effects of the last redirection.
  */
 static void
-memtodest(const char *p, size_t len, int syntax, int quotes)
+popredir(int drop)
 {
-	char *q = expdest;
-
-	q = makestrspace(len * 2, q);
+	struct redirtab *rp;
+	int i;
 
-	while (len--) {
-		int c = signed_char2int(*p++);
-		if (!c)
-			continue;
-		if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
-			USTPUTC(CTLESC, q);
-		USTPUTC(c, q);
+	if (--nullredirs >= 0)
+		return;
+	INT_OFF;
+	rp = redirlist;
+	for (i = 0; i < 10; i++) {
+		if (rp->renamed[i] != EMPTY) {
+			if (!drop) {
+				close(i);
+				copyfd(rp->renamed[i], i);
+			}
+			close(rp->renamed[i]);
+		}
 	}
-
-	expdest = q;
+	redirlist = rp->next;
+	nullredirs = rp->nullredirs;
+	free(rp);
+	INT_ON;
 }
 
-static void
-strtodest(const char *p, int syntax, int quotes)
-{
-	memtodest(p, strlen(p), syntax, quotes);
-}
+/*
+ * Undo all redirections.  Called on error or interrupt.
+ */
 
 /*
- * Record the fact that we have to scan this region of the
- * string for IFS characters.
+ * Discard all saved file descriptors.
  */
 static void
-recordregion(int start, int end, int nulonly)
+clearredir(int drop)
 {
-	struct ifsregion *ifsp;
-
-	if (ifslastp == NULL) {
-		ifsp = &ifsfirst;
-	} else {
-		INT_OFF;
-		ifsp = ckmalloc(sizeof(*ifsp));
-		ifsp->next = NULL;
-		ifslastp->next = ifsp;
-		INT_ON;
+	for (;;) {
+		nullredirs = 0;
+		if (!redirlist)
+			break;
+		popredir(drop);
 	}
-	ifslastp = ifsp;
-	ifslastp->begoff = start;
-	ifslastp->endoff = end;
-	ifslastp->nulonly = nulonly;
 }
 
-static void
-removerecordregions(int endoff)
+static int
+redirectsafe(union node *redir, int flags)
 {
-	if (ifslastp == NULL)
-		return;
-
-	if (ifsfirst.endoff > endoff) {
-		while (ifsfirst.next != NULL) {
-			struct ifsregion *ifsp;
-			INT_OFF;
-			ifsp = ifsfirst.next->next;
-			free(ifsfirst.next);
-			ifsfirst.next = ifsp;
-			INT_ON;
-		}
-		if (ifsfirst.begoff > endoff)
-			ifslastp = NULL;
-		else {
-			ifslastp = &ifsfirst;
-			ifsfirst.endoff = endoff;
-		}
-		return;
-	}
+	int err;
+	volatile int saveint;
+	struct jmploc *volatile savehandler = exception_handler;
+	struct jmploc jmploc;
 
-	ifslastp = &ifsfirst;
-	while (ifslastp->next && ifslastp->next->begoff < endoff)
-		ifslastp=ifslastp->next;
-	while (ifslastp->next != NULL) {
-		struct ifsregion *ifsp;
-		INT_OFF;
-		ifsp = ifslastp->next->next;
-		free(ifslastp->next);
-		ifslastp->next = ifsp;
-		INT_ON;
+	SAVE_INT(saveint);
+	err = setjmp(jmploc.loc) * 2;
+	if (!err) {
+		exception_handler = &jmploc;
+		redirect(redir, flags);
 	}
-	if (ifslastp->endoff > endoff)
-		ifslastp->endoff = endoff;
+	exception_handler = savehandler;
+	if (err && exception != EXERROR)
+		longjmp(exception_handler->loc, 1);
+	RESTORE_INT(saveint);
+	return err;
 }
 
-static char *
-exptilde(char *startp, char *p, int flag)
-{
-	char c;
-	char *name;
-	struct passwd *pw;
-	const char *home;
-	int quotes = flag & (EXP_FULL | EXP_CASE);
-	int startloc;
 
-	name = p + 1;
+/* ============ Routines to expand arguments to commands
+ *
+ * We have to deal with backquotes, shell variables, and file metacharacters.
+ */
 
-	while ((c = *++p) != '\0') {
-		switch (c) {
-		case CTLESC:
-			return startp;
-		case CTLQUOTEMARK:
-			return startp;
-		case ':':
-			if (flag & EXP_VARTILDE)
-				goto done;
-			break;
-		case '/':
-		case CTLENDVAR:
-			goto done;
-		}
-	}
- done:
-	*p = '\0';
-	if (*name == '\0') {
-		home = lookupvar(homestr);
-	} else {
-		pw = getpwnam(name);
-		if (pw == NULL)
-			goto lose;
-		home = pw->pw_dir;
-	}
-	if (!home || !*home)
-		goto lose;
-	*p = c;
-	startloc = expdest - (char *)stackblock();
-	strtodest(home, SQSYNTAX, quotes);
-	recordregion(startloc, expdest - (char *)stackblock(), 0);
-	return p;
- lose:
-	*p = c;
-	return startp;
-}
+/*
+ * expandarg flags
+ */
+#define EXP_FULL        0x1     /* perform word splitting & file globbing */
+#define EXP_TILDE       0x2     /* do normal tilde expansion */
+#define EXP_VARTILDE    0x4     /* expand tildes in an assignment */
+#define EXP_REDIR       0x8     /* file glob for a redirection (1 match only) */
+#define EXP_CASE        0x10    /* keeps quotes around for CASE pattern */
+#define EXP_RECORD      0x20    /* need to record arguments for ifs breakup */
+#define EXP_VARTILDE2   0x40    /* expand tildes after colons only */
+#define EXP_WORD        0x80    /* expand word in parameter expansion */
+#define EXP_QWORD       0x100   /* expand word in quoted parameter expansion */
+/*
+ * _rmescape() flags
+ */
+#define RMESCAPE_ALLOC  0x1     /* Allocate a new string */
+#define RMESCAPE_GLOB   0x2     /* Add backslashes for glob */
+#define RMESCAPE_QUOTED 0x4     /* Remove CTLESC unless in quotes */
+#define RMESCAPE_GROW   0x8     /* Grow strings instead of stalloc */
+#define RMESCAPE_HEAP   0x10    /* Malloc strings instead of stalloc */
 
 /*
- * Execute a command inside back quotes.  If it's a builtin command, we
- * want to save its output in a block obtained from malloc.  Otherwise
- * we fork off a subprocess and get the output of the command via a pipe.
- * Should be called with interrupts off.
+ * Structure specifying which parts of the string should be searched
+ * for IFS characters.
  */
-struct backcmd {                /* result of evalbackcmd */
-	int fd;                 /* file descriptor to read from */
-	char *buf;              /* buffer */
-	int nleft;              /* number of chars in buffer */
-	struct job *jp;         /* job structure for command */
+struct ifsregion {
+	struct ifsregion *next; /* next region in list */
+	int begoff;             /* offset of start of region */
+	int endoff;             /* offset of end of region */
+	int nulonly;            /* search for nul bytes only */
 };
 
-/* These forward decls are needed to use "eval" code for backticks handling: */
-static int back_exitstatus; /* exit status of backquoted command */
-#define EV_EXIT 01              /* exit after evaluating tree */
-static void evaltree(union node *, int);
+struct arglist {
+	struct strlist *list;
+	struct strlist **lastp;
+};
 
-static void
-evalbackcmd(union node *n, struct backcmd *result)
+/* output of current string */
+static char *expdest;
+/* list of back quote expressions */
+static struct nodelist *argbackq;
+/* first struct in list of ifs regions */
+static struct ifsregion ifsfirst;
+/* last struct in list */
+static struct ifsregion *ifslastp;
+/* holds expanded arg list */
+static struct arglist exparg;
+
+/*
+ * Our own itoa().
+ */
+static int
+cvtnum(arith_t num)
 {
-	int saveherefd;
+	int len;
 
-	result->fd = -1;
-	result->buf = NULL;
-	result->nleft = 0;
-	result->jp = NULL;
-	if (n == NULL) {
-		goto out;
+	expdest = makestrspace(32, expdest);
+#if ENABLE_ASH_MATH_SUPPORT_64
+	len = fmtstr(expdest, 32, "%lld", (long long) num);
+#else
+	len = fmtstr(expdest, 32, "%ld", num);
+#endif
+	STADJUST(len, expdest);
+	return len;
+}
+
+static size_t
+esclen(const char *start, const char *p)
+{
+	size_t esc = 0;
+
+	while (p > start && *--p == CTLESC) {
+		esc++;
 	}
+	return esc;
+}
 
-	saveherefd = herefd;
-	herefd = -1;
+/*
+ * Remove any CTLESC characters from a string.
+ */
+static char *
+_rmescapes(char *str, int flag)
+{
+	char *p, *q, *r;
+	static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
+	unsigned inquotes;
+	int notescaped;
+	int globbing;
 
-	{
-		int pip[2];
-		struct job *jp;
+	p = strpbrk(str, qchars);
+	if (!p) {
+		return str;
+	}
+	q = p;
+	r = str;
+	if (flag & RMESCAPE_ALLOC) {
+		size_t len = p - str;
+		size_t fulllen = len + strlen(p) + 1;
 
-		if (pipe(pip) < 0)
-			ash_msg_and_raise_error("Pipe call failed");
-		jp = makejob(n, 1);
-		if (forkshell(jp, n, FORK_NOJOB) == 0) {
-			FORCE_INT_ON;
-			close(pip[0]);
-			if (pip[1] != 1) {
-				close(1);
-				copyfd(pip[1], 1);
-				close(pip[1]);
+		if (flag & RMESCAPE_GROW) {
+			r = makestrspace(fulllen, expdest);
+		} else if (flag & RMESCAPE_HEAP) {
+			r = ckmalloc(fulllen);
+		} else {
+			r = stalloc(fulllen);
+		}
+		q = r;
+		if (len > 0) {
+			q = memcpy(q, str, len) + len;
+		}
+	}
+	inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
+	globbing = flag & RMESCAPE_GLOB;
+	notescaped = globbing;
+	while (*p) {
+		if (*p == CTLQUOTEMARK) {
+			inquotes = ~inquotes;
+			p++;
+			notescaped = globbing;
+			continue;
+		}
+		if (*p == '\\') {
+			/* naked back slash */
+			notescaped = 0;
+			goto copy;
+		}
+		if (*p == CTLESC) {
+			p++;
+			if (notescaped && inquotes && *p != '/') {
+				*q++ = '\\';
 			}
-			eflag = 0;
-			evaltree(n, EV_EXIT); /* actually evaltreenr... */
-			/* NOTREACHED */
 		}
-		close(pip[1]);
-		result->fd = pip[0];
-		result->jp = jp;
+		notescaped = globbing;
+ copy:
+		*q++ = *p++;
 	}
-	herefd = saveherefd;
- out:
-	TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
-		result->fd, result->buf, result->nleft, result->jp));
+	*q = '\0';
+	if (flag & RMESCAPE_GROW) {
+		expdest = r;
+		STADJUST(q - r + 1, expdest);
+	}
+	return r;
+}
+#define rmescapes(p) _rmescapes((p), 0)
+
+#define pmatch(a, b) !fnmatch((a), (b), 0)
+
+/*
+ * Prepare a pattern for a expmeta (internal glob(3)) call.
+ *
+ * Returns an stalloced string.
+ */
+static char *
+preglob(const char *pattern, int quoted, int flag)
+{
+	flag |= RMESCAPE_GLOB;
+	if (quoted) {
+		flag |= RMESCAPE_QUOTED;
+	}
+	return _rmescapes((char *)pattern, flag);
 }
 
 /*
- * Expand stuff in backwards quotes.
+ * Put a string on the stack.
  */
 static void
-expbackq(union node *cmd, int quoted, int quotes)
+memtodest(const char *p, size_t len, int syntax, int quotes)
 {
-	struct backcmd in;
-	int i;
-	char buf[128];
-	char *p;
-	char *dest;
-	int startloc;
-	int syntax = quoted? DQSYNTAX : BASESYNTAX;
-	struct stackmark smark;
+	char *q = expdest;
 
-	INT_OFF;
-	setstackmark(&smark);
+	q = makestrspace(len * 2, q);
+
+	while (len--) {
+		int c = signed_char2int(*p++);
+		if (!c)
+			continue;
+		if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
+			USTPUTC(CTLESC, q);
+		USTPUTC(c, q);
+	}
+
+	expdest = q;
+}
+
+static void
+strtodest(const char *p, int syntax, int quotes)
+{
+	memtodest(p, strlen(p), syntax, quotes);
+}
+
+/*
+ * Record the fact that we have to scan this region of the
+ * string for IFS characters.
+ */
+static void
+recordregion(int start, int end, int nulonly)
+{
+	struct ifsregion *ifsp;
+
+	if (ifslastp == NULL) {
+		ifsp = &ifsfirst;
+	} else {
+		INT_OFF;
+		ifsp = ckmalloc(sizeof(*ifsp));
+		ifsp->next = NULL;
+		ifslastp->next = ifsp;
+		INT_ON;
+	}
+	ifslastp = ifsp;
+	ifslastp->begoff = start;
+	ifslastp->endoff = end;
+	ifslastp->nulonly = nulonly;
+}
+
+static void
+removerecordregions(int endoff)
+{
+	if (ifslastp == NULL)
+		return;
+
+	if (ifsfirst.endoff > endoff) {
+		while (ifsfirst.next != NULL) {
+			struct ifsregion *ifsp;
+			INT_OFF;
+			ifsp = ifsfirst.next->next;
+			free(ifsfirst.next);
+			ifsfirst.next = ifsp;
+			INT_ON;
+		}
+		if (ifsfirst.begoff > endoff)
+			ifslastp = NULL;
+		else {
+			ifslastp = &ifsfirst;
+			ifsfirst.endoff = endoff;
+		}
+		return;
+	}
+
+	ifslastp = &ifsfirst;
+	while (ifslastp->next && ifslastp->next->begoff < endoff)
+		ifslastp=ifslastp->next;
+	while (ifslastp->next != NULL) {
+		struct ifsregion *ifsp;
+		INT_OFF;
+		ifsp = ifslastp->next->next;
+		free(ifslastp->next);
+		ifslastp->next = ifsp;
+		INT_ON;
+	}
+	if (ifslastp->endoff > endoff)
+		ifslastp->endoff = endoff;
+}
+
+static char *
+exptilde(char *startp, char *p, int flag)
+{
+	char c;
+	char *name;
+	struct passwd *pw;
+	const char *home;
+	int quotes = flag & (EXP_FULL | EXP_CASE);
+	int startloc;
+
+	name = p + 1;
+
+	while ((c = *++p) != '\0') {
+		switch (c) {
+		case CTLESC:
+			return startp;
+		case CTLQUOTEMARK:
+			return startp;
+		case ':':
+			if (flag & EXP_VARTILDE)
+				goto done;
+			break;
+		case '/':
+		case CTLENDVAR:
+			goto done;
+		}
+	}
+ done:
+	*p = '\0';
+	if (*name == '\0') {
+		home = lookupvar(homestr);
+	} else {
+		pw = getpwnam(name);
+		if (pw == NULL)
+			goto lose;
+		home = pw->pw_dir;
+	}
+	if (!home || !*home)
+		goto lose;
+	*p = c;
+	startloc = expdest - (char *)stackblock();
+	strtodest(home, SQSYNTAX, quotes);
+	recordregion(startloc, expdest - (char *)stackblock(), 0);
+	return p;
+ lose:
+	*p = c;
+	return startp;
+}
+
+/*
+ * Execute a command inside back quotes.  If it's a builtin command, we
+ * want to save its output in a block obtained from malloc.  Otherwise
+ * we fork off a subprocess and get the output of the command via a pipe.
+ * Should be called with interrupts off.
+ */
+struct backcmd {                /* result of evalbackcmd */
+	int fd;                 /* file descriptor to read from */
+	char *buf;              /* buffer */
+	int nleft;              /* number of chars in buffer */
+	struct job *jp;         /* job structure for command */
+};
+
+/* These forward decls are needed to use "eval" code for backticks handling: */
+static int back_exitstatus; /* exit status of backquoted command */
+#define EV_EXIT 01              /* exit after evaluating tree */
+static void evaltree(union node *, int);
+
+static void
+evalbackcmd(union node *n, struct backcmd *result)
+{
+	int saveherefd;
+
+	result->fd = -1;
+	result->buf = NULL;
+	result->nleft = 0;
+	result->jp = NULL;
+	if (n == NULL) {
+		goto out;
+	}
+
+	saveherefd = herefd;
+	herefd = -1;
+
+	{
+		int pip[2];
+		struct job *jp;
+
+		if (pipe(pip) < 0)
+			ash_msg_and_raise_error("Pipe call failed");
+		jp = makejob(n, 1);
+		if (forkshell(jp, n, FORK_NOJOB) == 0) {
+			FORCE_INT_ON;
+			close(pip[0]);
+			if (pip[1] != 1) {
+				close(1);
+				copyfd(pip[1], 1);
+				close(pip[1]);
+			}
+			eflag = 0;
+			evaltree(n, EV_EXIT); /* actually evaltreenr... */
+			/* NOTREACHED */
+		}
+		close(pip[1]);
+		result->fd = pip[0];
+		result->jp = jp;
+	}
+	herefd = saveherefd;
+ out:
+	TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+		result->fd, result->buf, result->nleft, result->jp));
+}
+
+/*
+ * Expand stuff in backwards quotes.
+ */
+static void
+expbackq(union node *cmd, int quoted, int quotes)
+{
+	struct backcmd in;
+	int i;
+	char buf[128];
+	char *p;
+	char *dest;
+	int startloc;
+	int syntax = quoted? DQSYNTAX : BASESYNTAX;
+	struct stackmark smark;
+
+	INT_OFF;
+	setstackmark(&smark);
 	dest = expdest;
 	startloc = dest - (char *)stackblock();
 	grabstackstr(dest);
@@ -7475,43 +7818,95 @@ evalpipe(union node *n, int flags)
 	INT_ON;
 }
 
-static struct localvar *localvars;
-
 /*
- * Called after a function returns.
- * Interrupts must be off.
+ * Controls whether the shell is interactive or not.
  */
 static void
-poplocalvars(void)
+setinteractive(int on)
 {
-	struct localvar *lvp;
-	struct var *vp;
+	static int is_interactive;
 
-	while ((lvp = localvars) != NULL) {
-		localvars = lvp->next;
-		vp = lvp->vp;
-		TRACE(("poplocalvar %s", vp ? vp->text : "-"));
-		if (vp == NULL) {       /* $- saved */
-			memcpy(optlist, lvp->text, sizeof(optlist));
-			free((char*)lvp->text);
-			optschanged();
-		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
-			unsetvar(vp->text);
-		} else {
-			if (vp->func)
-				(*vp->func)(strchrnul(lvp->text, '=') + 1);
-			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
-				free((char*)vp->text);
-			vp->flags = lvp->flags;
-			vp->text = lvp->text;
-		}
-		free(lvp);
-	}
-}
+	if (++on == is_interactive)
+		return;
+	is_interactive = on;
+	setsignal(SIGINT);
+	setsignal(SIGQUIT);
+	setsignal(SIGTERM);
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+	if (is_interactive > 1) {
+		/* Looks like they want an interactive shell */
+		static smallint do_banner;
 
-static int
-evalfun(struct funcnode *func, int argc, char **argv, int flags)
-{
+		if (!do_banner) {
+			out1fmt(
+				"\n\n"
+				"%s Built-in shell (ash)\n"
+				"Enter 'help' for a list of built-in commands."
+				"\n\n",
+				BB_BANNER);
+			do_banner = 1;
+		}
+	}
+#endif
+}
+
+#if ENABLE_FEATURE_EDITING_VI
+#define setvimode(on) do { \
+	if (on) line_input_state->flags |= VI_MODE; \
+	else line_input_state->flags &= ~VI_MODE; \
+} while (0)
+#else
+#define setvimode(on) viflag = 0   /* forcibly keep the option off */
+#endif
+
+static void
+optschanged(void)
+{
+#if DEBUG
+	opentrace();
+#endif
+	setinteractive(iflag);
+	setjobctl(mflag);
+	setvimode(viflag);
+}
+
+static struct localvar *localvars;
+
+/*
+ * Called after a function returns.
+ * Interrupts must be off.
+ */
+static void
+poplocalvars(void)
+{
+	struct localvar *lvp;
+	struct var *vp;
+
+	while ((lvp = localvars) != NULL) {
+		localvars = lvp->next;
+		vp = lvp->vp;
+		TRACE(("poplocalvar %s", vp ? vp->text : "-"));
+		if (vp == NULL) {       /* $- saved */
+			memcpy(optlist, lvp->text, sizeof(optlist));
+			free((char*)lvp->text);
+			optschanged();
+		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
+			unsetvar(vp->text);
+		} else {
+			if (vp->func)
+				(*vp->func)(strchrnul(lvp->text, '=') + 1);
+			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+				free((char*)vp->text);
+			vp->flags = lvp->flags;
+			vp->text = lvp->text;
+		}
+		free(lvp);
+	}
+}
+
+static int
+evalfun(struct funcnode *func, int argc, char **argv, int flags)
+{
 	volatile struct shparam saveparam;
 	struct localvar *volatile savelocalvars;
 	struct jmploc *volatile savehandler;
@@ -7894,7 +8289,7 @@ evalcommand(union node *cmd, int flags)
 
 	preverrout_fd = 2;
 	expredir(cmd->ncmd.redirect);
-	status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
+	status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
 
 	path = vpath.text;
 	for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
@@ -8678,88 +9073,35 @@ changemail(const char *val)
 /* ============ ??? */
 
 /*
- * Take commands from a file.  To be compatible we should do a path
- * search for the file, which is necessary to find sub-commands.
- */
-static char *
-find_dot_file(char *name)
-{
-	char *fullname;
-	const char *path = pathval();
-	struct stat statb;
-
-	/* don't try this for absolute or relative paths */
-	if (strchr(name, '/'))
-		return name;
-
-	while ((fullname = padvance(&path, name)) != NULL) {
-		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
-			/*
-			 * Don't bother freeing here, since it will
-			 * be freed by the caller.
-			 */
-			return fullname;
-		}
-		stunalloc(fullname);
-	}
-
-	/* not found in the PATH */
-	ash_msg_and_raise_error("%s: not found", name);
-	/* NOTREACHED */
-}
-
-/*
- * Controls whether the shell is interactive or not.
+ * Set the shell parameters.
  */
 static void
-setinteractive(int on)
+setparam(char **argv)
 {
-	static int is_interactive;
-
-	if (++on == is_interactive)
-		return;
-	is_interactive = on;
-	setsignal(SIGINT);
-	setsignal(SIGQUIT);
-	setsignal(SIGTERM);
-#if !ENABLE_FEATURE_SH_EXTRA_QUIET
-	if (is_interactive > 1) {
-		/* Looks like they want an interactive shell */
-		static smallint do_banner;
+	char **newparam;
+	char **ap;
+	int nparam;
 
-		if (!do_banner) {
-			out1fmt(
-				"\n\n"
-				"%s Built-in shell (ash)\n"
-				"Enter 'help' for a list of built-in commands."
-				"\n\n",
-				BB_BANNER);
-			do_banner = 1;
-		}
+	for (nparam = 0; argv[nparam]; nparam++);
+	ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
+	while (*argv) {
+		*ap++ = ckstrdup(*argv++);
 	}
+	*ap = NULL;
+	freeparam(&shellparam);
+	shellparam.malloc = 1;
+	shellparam.nparam = nparam;
+	shellparam.p = newparam;
+#if ENABLE_ASH_GETOPTS
+	shellparam.optind = 1;
+	shellparam.optoff = -1;
 #endif
 }
 
-#if ENABLE_FEATURE_EDITING_VI
-#define setvimode(on) do { \
-	if (on) line_input_state->flags |= VI_MODE; \
-	else line_input_state->flags &= ~VI_MODE; \
-} while (0)
-#else
-#define setvimode(on) viflag = 0   /* forcibly keep the option off */
-#endif
-
-static void
-optschanged(void)
-{
-#if DEBUG
-	opentrace();
-#endif
-	setinteractive(iflag);
-	setjobctl(mflag);
-	setvimode(viflag);
-}
-
+/*
+ * Process shell options.  The global variable argptr contains a pointer
+ * to the argument list; we advance it past the options.
+ */
 static void
 minus_o(char *name, int val)
 {
@@ -8779,7 +9121,6 @@ minus_o(char *name, int val)
 		out1fmt("%-16s%s\n", optnames(i),
 				optlist[i] ? "on" : "off");
 }
-
 static void
 setoption(int flag, int val)
 {
@@ -8794,11 +9135,6 @@ setoption(int flag, int val)
 	ash_msg_and_raise_error("Illegal option -%c", flag);
 	/* NOTREACHED */
 }
-
-/*
- * Process shell options.  The global variable argptr contains a pointer
- * to the argument list; we advance it past the options.
- */
 static void
 options(int cmdline)
 {
@@ -8848,47 +9184,6 @@ options(int cmdline)
 	}
 }
 
-/*
- * Set the shell parameters.
- */
-static void
-setparam(char **argv)
-{
-	char **newparam;
-	char **ap;
-	int nparam;
-
-	for (nparam = 0; argv[nparam]; nparam++);
-	ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
-	while (*argv) {
-		*ap++ = ckstrdup(*argv++);
-	}
-	*ap = NULL;
-	freeparam(&shellparam);
-	shellparam.malloc = 1;
-	shellparam.nparam = nparam;
-	shellparam.p = newparam;
-#if ENABLE_ASH_GETOPTS
-	shellparam.optind = 1;
-	shellparam.optoff = -1;
-#endif
-}
-
-/*
- * Free the list of positional parameters.
- */
-static void
-freeparam(volatile struct shparam *param)
-{
-	char **ap;
-
-	if (param->malloc) {
-		for (ap = param->p; *ap; ap++)
-			free(*ap);
-		free(param->p);
-	}
-}
-
 /*
  * The shift builtin command.
  */
@@ -10720,6 +11015,37 @@ cmdloop(int top)
 	return 0;
 }
 
+/*
+ * Take commands from a file.  To be compatible we should do a path
+ * search for the file, which is necessary to find sub-commands.
+ */
+static char *
+find_dot_file(char *name)
+{
+	char *fullname;
+	const char *path = pathval();
+	struct stat statb;
+
+	/* don't try this for absolute or relative paths */
+	if (strchr(name, '/'))
+		return name;
+
+	while ((fullname = padvance(&path, name)) != NULL) {
+		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
+			/*
+			 * Don't bother freeing here, since it will
+			 * be freed by the caller.
+			 */
+			return fullname;
+		}
+		stunalloc(fullname);
+	}
+
+	/* not found in the PATH */
+	ash_msg_and_raise_error("%s: not found", name);
+	/* NOTREACHED */
+}
+
 static int
 dotcmd(int argc, char **argv)
 {
@@ -10982,352 +11308,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
 }
 
 
-/* ============ redir.c
- *
- * Code for dealing with input/output redirection.
- */
-
-#define EMPTY -2                /* marks an unused slot in redirtab */
-#ifndef PIPE_BUF
-# define PIPESIZE 4096          /* amount of buffering in a pipe */
-#else
-# define PIPESIZE PIPE_BUF
-#endif
-
-/*
- * Open a file in noclobber mode.
- * The code was copied from bash.
- */
-static int
-noclobberopen(const char *fname)
-{
-	int r, fd;
-	struct stat finfo, finfo2;
-
-	/*
-	 * If the file exists and is a regular file, return an error
-	 * immediately.
-	 */
-	r = stat(fname, &finfo);
-	if (r == 0 && S_ISREG(finfo.st_mode)) {
-		errno = EEXIST;
-		return -1;
-	}
-
-	/*
-	 * If the file was not present (r != 0), make sure we open it
-	 * exclusively so that if it is created before we open it, our open
-	 * will fail.  Make sure that we do not truncate an existing file.
-	 * Note that we don't turn on O_EXCL unless the stat failed -- if the
-	 * file was not a regular file, we leave O_EXCL off.
-	 */
-	if (r != 0)
-		return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
-	fd = open(fname, O_WRONLY|O_CREAT, 0666);
-
-	/* If the open failed, return the file descriptor right away. */
-	if (fd < 0)
-		return fd;
-
-	/*
-	 * OK, the open succeeded, but the file may have been changed from a
-	 * non-regular file to a regular file between the stat and the open.
-	 * We are assuming that the O_EXCL open handles the case where FILENAME
-	 * did not exist and is symlinked to an existing file between the stat
-	 * and open.
-	 */
-
-	/*
-	 * If we can open it and fstat the file descriptor, and neither check
-	 * revealed that it was a regular file, and the file has not been
-	 * replaced, return the file descriptor.
-	 */
-	if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
-	 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
-		return fd;
-
-	/* The file has been replaced.  badness. */
-	close(fd);
-	errno = EEXIST;
-	return -1;
-}
-
-/*
- * Handle here documents.  Normally we fork off a process to write the
- * data to a pipe.  If the document is short, we can stuff the data in
- * the pipe without forking.
- */
-static int
-openhere(union node *redir)
-{
-	int pip[2];
-	size_t len = 0;
-
-	if (pipe(pip) < 0)
-		ash_msg_and_raise_error("Pipe call failed");
-	if (redir->type == NHERE) {
-		len = strlen(redir->nhere.doc->narg.text);
-		if (len <= PIPESIZE) {
-			full_write(pip[1], redir->nhere.doc->narg.text, len);
-			goto out;
-		}
-	}
-	if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
-		close(pip[0]);
-		signal(SIGINT, SIG_IGN);
-		signal(SIGQUIT, SIG_IGN);
-		signal(SIGHUP, SIG_IGN);
-#ifdef SIGTSTP
-		signal(SIGTSTP, SIG_IGN);
-#endif
-		signal(SIGPIPE, SIG_DFL);
-		if (redir->type == NHERE)
-			full_write(pip[1], redir->nhere.doc->narg.text, len);
-		else
-			expandhere(redir->nhere.doc, pip[1]);
-		_exit(0);
-	}
- out:
-	close(pip[1]);
-	return pip[0];
-}
-
-static int
-openredirect(union node *redir)
-{
-	char *fname;
-	int f;
-
-	switch (redir->nfile.type) {
-	case NFROM:
-		fname = redir->nfile.expfname;
-		f = open(fname, O_RDONLY);
-		if (f < 0)
-			goto eopen;
-		break;
-	case NFROMTO:
-		fname = redir->nfile.expfname;
-		f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
-		if (f < 0)
-			goto ecreate;
-		break;
-	case NTO:
-		/* Take care of noclobber mode. */
-		if (Cflag) {
-			fname = redir->nfile.expfname;
-			f = noclobberopen(fname);
-			if (f < 0)
-				goto ecreate;
-			break;
-		}
-		/* FALLTHROUGH */
-	case NCLOBBER:
-		fname = redir->nfile.expfname;
-		f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
-		if (f < 0)
-			goto ecreate;
-		break;
-	case NAPPEND:
-		fname = redir->nfile.expfname;
-		f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
-		if (f < 0)
-			goto ecreate;
-		break;
-	default:
-#if DEBUG
-		abort();
-#endif
-		/* Fall through to eliminate warning. */
-	case NTOFD:
-	case NFROMFD:
-		f = -1;
-		break;
-	case NHERE:
-	case NXHERE:
-		f = openhere(redir);
-		break;
-	}
-
-	return f;
- ecreate:
-	ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "Directory nonexistent"));
- eopen:
-	ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file"));
-}
-
-static void
-dupredirect(union node *redir, int f)
-{
-	int fd = redir->nfile.fd;
-
-	if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
-		if (redir->ndup.dupfd >= 0) {   /* if not ">&-" */
-			copyfd(redir->ndup.dupfd, fd);
-		}
-		return;
-	}
-
-	if (f != fd) {
-		copyfd(f, fd);
-		close(f);
-	}
-}
-
-/*
- * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
- * old file descriptors are stashed away so that the redirection can be
- * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
- * standard output, and the standard error if it becomes a duplicate of
- * stdout, is saved in memory.
- */
-static void
-redirect(union node *redir, int flags)
-{
-	union node *n;
-	struct redirtab *sv;
-	int i;
-	int fd;
-	int newfd;
-	int *p;
-	nullredirs++;
-	if (!redir) {
-		return;
-	}
-	sv = NULL;
-	INT_OFF;
-	if (flags & REDIR_PUSH) {
-		struct redirtab *q;
-		q = ckmalloc(sizeof(struct redirtab));
-		q->next = redirlist;
-		redirlist = q;
-		q->nullredirs = nullredirs - 1;
-		for (i = 0; i < 10; i++)
-			q->renamed[i] = EMPTY;
-		nullredirs = 0;
-		sv = q;
-	}
-	n = redir;
-	do {
-		fd = n->nfile.fd;
-		if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
-		 && n->ndup.dupfd == fd)
-			continue; /* redirect from/to same file descriptor */
-
-		newfd = openredirect(n);
-		if (fd == newfd)
-			continue;
-		if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
-			i = fcntl(fd, F_DUPFD, 10);
-
-			if (i == -1) {
-				i = errno;
-				if (i != EBADF) {
-					close(newfd);
-					errno = i;
-					ash_msg_and_raise_error("%d: %m", fd);
-					/* NOTREACHED */
-				}
-			} else {
-				*p = i;
-				close(fd);
-			}
-		} else {
-			close(fd);
-		}
-		dupredirect(n, newfd);
-	} while ((n = n->nfile.next));
-	INT_ON;
-	if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
-		preverrout_fd = sv->renamed[2];
-}
-
-/*
- * Undo the effects of the last redirection.
- */
-static void
-popredir(int drop)
-{
-	struct redirtab *rp;
-	int i;
-
-	if (--nullredirs >= 0)
-		return;
-	INT_OFF;
-	rp = redirlist;
-	for (i = 0; i < 10; i++) {
-		if (rp->renamed[i] != EMPTY) {
-			if (!drop) {
-				close(i);
-				copyfd(rp->renamed[i], i);
-			}
-			close(rp->renamed[i]);
-		}
-	}
-	redirlist = rp->next;
-	nullredirs = rp->nullredirs;
-	free(rp);
-	INT_ON;
-}
-
-/*
- * Undo all redirections.  Called on error or interrupt.
- */
-
-/*
- * Discard all saved file descriptors.
- */
-static void
-clearredir(int drop)
-{
-	for (;;) {
-		nullredirs = 0;
-		if (!redirlist)
-			break;
-		popredir(drop);
-	}
-}
-
-/*
- * Copy a file descriptor to be >= to.  Returns -1
- * if the source file descriptor is closed, EMPTY if there are no unused
- * file descriptors left.
- */
-static int
-copyfd(int from, int to)
-{
-	int newfd;
-
-	newfd = fcntl(from, F_DUPFD, to);
-	if (newfd < 0) {
-		if (errno == EMFILE)
-			return EMPTY;
-		ash_msg_and_raise_error("%d: %m", from);
-	}
-	return newfd;
-}
-
-static int
-redirectsafe(union node *redir, int flags)
-{
-	int err;
-	volatile int saveint;
-	struct jmploc *volatile savehandler = exception_handler;
-	struct jmploc jmploc;
-
-	SAVE_INT(saveint);
-	err = setjmp(jmploc.loc) * 2;
-	if (!err) {
-		exception_handler = &jmploc;
-		redirect(redir, flags);
-	}
-	exception_handler = savehandler;
-	if (err && exception != EXERROR)
-		longjmp(exception_handler->loc, 1);
-	RESTORE_INT(saveint);
-	return err;
-}
-
-
 /* ============ trap.c */
 
 /*
-- 
cgit v1.2.3