Merge commit 'maks/maks'
diff --git a/usr/dash/README.klibc b/usr/dash/README.klibc
index 0394fb0..1e9f005 100644
--- a/usr/dash/README.klibc
+++ b/usr/dash/README.klibc
@@ -2,6 +2,6 @@
 
 http://gondor.apana.org.au/~herbert/dash/dash.git/
 
-It corresponds to changeset 3c98399cdf8d376b2c1ebd9cd32ca5d8c84f3ac9.
+It corresponds to changeset e592f4dfe80dcac85764ac9aaad3132e5ba28663.
 
 The only changes made are the addition of config.h and a new Makefile.
diff --git a/usr/dash/bltin/printf.c b/usr/dash/bltin/printf.c
index 75ba40d..8efcbe3 100644
--- a/usr/dash/bltin/printf.c
+++ b/usr/dash/bltin/printf.c
@@ -40,7 +40,7 @@
 #include <string.h>
 #include <unistd.h>
 
-static char	*conv_escape_str(char *);
+static int	 conv_escape_str(char *);
 static char	*conv_escape(char *, int *);
 static int	 getchr(void);
 static intmax_t	 getintmax(void);
@@ -157,11 +157,12 @@
 			switch (ch) {
 
 			case 'b': {
-				char *p = conv_escape_str(getstr());
+				int done = conv_escape_str(getstr());
+				char *p = stackblock();
 				*fmt = 's';
 				PF(start, p);
 				/* escape if a \c was encountered */
-				if (rval & 0x100)
+				if (done)
 					goto out;
 				*fmt = 'b';
 				break;
@@ -212,7 +213,7 @@
 	} while (gargv != argv && *gargv);
 
 out:
-	return (rval & ~0x100);
+	return rval;
 err:
 	return 1;
 }
@@ -222,7 +223,7 @@
  * Print SysV echo(1) style escape string
  *	Halts processing string if a \c escape is encountered.
  */
-static char *
+static int
 conv_escape_str(char *str)
 {
 	int ch;
@@ -241,8 +242,7 @@
 		ch = *str++;
 		if (ch == 'c') {
 			/* \c as in SYSV echo - abort all processing.... */
-			rval |= 0x100;
-			ch = 0;
+			ch = 0x100;
 			continue;
 		}
 
@@ -269,9 +269,9 @@
 		/* Finally test for sequences valid in the format string */
 		str = conv_escape(str - 1, &c);
 		ch = c;
-	} while (STPUTC(ch, cp), ch);
+	} while (STPUTC(ch, cp), (char)ch);
 
-	return stackblock();
+	return ch;
 }
 
 /*
@@ -451,16 +451,11 @@
 	do {
 		char c;
 
-		c = *(*argv)++;
-		if (!c)
-			goto next;
-		if (c != '\\')
-			goto print;
-
-		outstr(conv_escape_str(*argv - 1), outs);
-		if (rval & 0x100)
+		nonl += conv_escape_str(*argv);
+		outstr(stackblock(), outs);
+		if (nonl > 0)
 			break;
-next:
+
 		c = ' ';
 		if (!*++argv) {
 end:
@@ -469,7 +464,6 @@
 			}
 			c = '\n';
 		}
-print:
 		outc(c, outs);
 	} while (*argv);
 	return 0;
diff --git a/usr/dash/bltin/test.c b/usr/dash/bltin/test.c
index 77949de..8f9e085 100644
--- a/usr/dash/bltin/test.c
+++ b/usr/dash/bltin/test.c
@@ -489,7 +489,8 @@
 
 	ngroups = getgroups(0, NULL);
 	group_array = stalloc(ngroups * sizeof(gid_t));
-	getgroups(ngroups, group_array);
+	if ((getgroups(ngroups, group_array)) != ngroups)
+		return (0);
 
 	/* Search through the list looking for GID. */
 	for (i = 0; i < ngroups; i++)
diff --git a/usr/dash/config.h b/usr/dash/config.h
index 1b587cc..a557b46 100644
--- a/usr/dash/config.h
+++ b/usr/dash/config.h
@@ -53,16 +53,16 @@
 #define PACKAGE_NAME "dash"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "dash 0.5.2"
+#define PACKAGE_STRING "dash 0.5.4"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "dash"
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "0.5.2"
+#define PACKAGE_VERSION "0.5.4"
 
 /* Version number of package */
-#define VERSION "0.5.2"
+#define VERSION "0.5.4"
 
 /* Enable GNU extensions on systems that have them.  */
 #ifndef _GNU_SOURCE
diff --git a/usr/dash/eval.c b/usr/dash/eval.c
index 44ba850..a9a73db 100644
--- a/usr/dash/eval.c
+++ b/usr/dash/eval.c
@@ -150,10 +150,9 @@
                         STPUTC('\0', concat);
                         p = grabstackstr(concat);
                 }
-                evalstring(p, ~SKIPEVAL);
-
+                return evalstring(p, ~SKIPEVAL);
         }
-        return exitstatus;
+        return 0;
 }
 
 
@@ -166,24 +165,23 @@
 {
 	union node *n;
 	struct stackmark smark;
-	int skip;
+	int status;
 
 	setinputstring(s);
 	setstackmark(&smark);
 
-	skip = 0;
+	status = 0;
 	while ((n = parsecmd(0)) != NEOF) {
 		evaltree(n, 0);
+		status = exitstatus;
 		popstackmark(&smark);
-		skip = evalskip;
-		if (skip)
+		if (evalskip)
 			break;
 	}
 	popfile();
 
-	skip &= mask;
-	evalskip = skip;
-	return skip;
+	evalskip &= mask;
+	return status;
 }
 
 
@@ -627,8 +625,7 @@
 			FORCEINTON;
 			close(pip[0]);
 			if (pip[1] != 1) {
-				close(1);
-				copyfd(pip[1], 1);
+				dup2(pip[1], 1);
 				close(pip[1]);
 			}
 			eflag = 0;
diff --git a/usr/dash/exec.c b/usr/dash/exec.c
index 417ba8a..8a1f722 100644
--- a/usr/dash/exec.c
+++ b/usr/dash/exec.c
@@ -110,7 +110,6 @@
 	char **envp;
 	int exerrno;
 
-	clearredir(1);
 	envp = environment();
 	if (strchr(argv[0], '/') != NULL) {
 		tryexec(argv[0], argv, envp);
@@ -846,6 +845,7 @@
 	int argc;
 	char **argv;
 {
+	char *cmd;
 	int c;
 	enum {
 		VERIFY_BRIEF = 1,
@@ -862,8 +862,9 @@
 			abort();
 #endif
 
-	if (verify)
-		return describe_command(out1, *argptr, verify - VERIFY_BRIEF);
+	cmd = *argptr;
+	if (verify && cmd)
+		return describe_command(out1, cmd, verify - VERIFY_BRIEF);
 
 	return 0;
 }
diff --git a/usr/dash/expand.c b/usr/dash/expand.c
index 2eb726e..d5d1b60 100644
--- a/usr/dash/expand.c
+++ b/usr/dash/expand.c
@@ -1581,7 +1581,7 @@
 				if (c == '[') {
 					const char *r;
 
-					found |= ccmatch(p, chr, &r);
+					found |= !!ccmatch(p, chr, &r);
 					if (r) {
 						p = r;
 						continue;
diff --git a/usr/dash/input.c b/usr/dash/input.c
index 057da71..11f7a3f 100644
--- a/usr/dash/input.c
+++ b/usr/dash/input.c
@@ -428,7 +428,6 @@
 setinputfile(const char *fname, int flags)
 {
 	int fd;
-	int fd2;
 
 	INTOFF;
 	if ((fd = open(fname, O_RDONLY)) < 0) {
@@ -436,13 +435,8 @@
 			goto out;
 		sh_error("Can't open %s", fname);
 	}
-	if (fd < 10) {
-		fd2 = copyfd(fd, 10);
-		close(fd);
-		if (fd2 < 0)
-			sh_error("Out of file descriptors");
-		fd = fd2;
-	}
+	if (fd < 10)
+		fd = savefd(fd);
 	setinputfd(fd, flags & INPUT_PUSH_FILE);
 out:
 	INTON;
@@ -458,7 +452,6 @@
 void
 setinputfd(int fd, int push)
 {
-	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
 	if (push) {
 		pushfile();
 		parsefile->buf = 0;
diff --git a/usr/dash/jobs.c b/usr/dash/jobs.c
index 77ed779..7e38048 100644
--- a/usr/dash/jobs.c
+++ b/usr/dash/jobs.c
@@ -188,18 +188,14 @@
 	if (on == jobctl || rootshell == 0)
 		return;
 	if (on) {
-		int ofd;
-		ofd = fd = open(_PATH_TTY, O_RDWR);
+		fd = open(_PATH_TTY, O_RDWR);
 		if (fd < 0) {
 			fd += 3;
-			while (!isatty(fd) && --fd >= 0)
-				;
+			while (!isatty(fd))
+				if (--fd < 0)
+					goto out;
 		}
-		fd = fcntl(fd, F_DUPFD, 10);
-		close(ofd);
-		if (fd < 0)
-			goto out;
-		fcntl(fd, F_SETFD, FD_CLOEXEC);
+		fd = savefd(fd);
 		do { /* while we are in the background */
 			if ((pgrp = tcgetpgrp(fd)) < 0) {
 out:
diff --git a/usr/dash/memalloc.h b/usr/dash/memalloc.h
index 1691d13..8a41e64 100644
--- a/usr/dash/memalloc.h
+++ b/usr/dash/memalloc.h
@@ -64,7 +64,7 @@
 char *stputs(const char *, char *);
 
 
-static inline char *_STPUTC(char c, char *p) {
+static inline char *_STPUTC(int c, char *p) {
 	if (p == sstrend)
 		p = growstackstr();
 	*p++ = c;
diff --git a/usr/dash/mkbuiltins b/usr/dash/mkbuiltins
index 3833052..dc0212b 100644
--- a/usr/dash/mkbuiltins
+++ b/usr/dash/mkbuiltins
@@ -65,7 +65,7 @@
 		if ($i ~ /^-/)
 			line = $(++i) "\t" line
 		print line
-	}}' $temp | sort -k 1,1 | tee $temp2 | awk '{
+	}}' $temp | LC_COLLATE=C sort -k 1,1 | tee $temp2 | awk '{
 		opt = ""
 		if (NF > 2) {
 			opt = substr($2, 2)
diff --git a/usr/dash/mkinit.c b/usr/dash/mkinit.c
index e803751..9714bee 100644
--- a/usr/dash/mkinit.c
+++ b/usr/dash/mkinit.c
@@ -427,9 +427,12 @@
 	struct block *bp;
 
 	if (text->start != NULL) {
-		for (bp = text->start ; bp != text->last ; bp = bp->next)
-			fwrite(bp->text, sizeof (char), BLOCKSIZE, fp);
-		fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp);
+		for (bp = text->start ; bp != text->last ; bp = bp->next) {
+			if ((fwrite(bp->text, sizeof (char), BLOCKSIZE, fp)) != BLOCKSIZE)
+				error("Can't write data\n");
+		}
+		if ((fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp)) != (BLOCKSIZE - text->nleft))
+			error("Can't write data\n");
 	}
 }
 
diff --git a/usr/dash/parser.c b/usr/dash/parser.c
index 91f019e..c83b14d 100644
--- a/usr/dash/parser.c
+++ b/usr/dash/parser.c
@@ -39,7 +39,6 @@
 #include "parser.h"
 #include "nodes.h"
 #include "expand.h"	/* defines rmescapes() */
-#include "redir.h"	/* defines copyfd() */
 #include "exec.h"	/* defines find_builtin() */
 #include "syntax.h"
 #include "options.h"
diff --git a/usr/dash/parser.h b/usr/dash/parser.h
index d0cf440..76ec839 100644
--- a/usr/dash/parser.h
+++ b/usr/dash/parser.h
@@ -41,7 +41,7 @@
 #define CTLENDVAR -125
 #define CTLBACKQ -124
 #define CTLQUOTE 01		/* ored with CTLBACKQ code if in quotes */
-/*	CTLBACKQ | CTLQUOTE == 133 */
+/*	CTLBACKQ | CTLQUOTE == -123 */
 #define	CTLARI -122		/* arithmetic expression */
 #define	CTLENDARI -121
 #define	CTLQUOTEMARK -120
diff --git a/usr/dash/redir.c b/usr/dash/redir.c
index 2bd8e9f..33dbc88 100644
--- a/usr/dash/redir.c
+++ b/usr/dash/redir.c
@@ -57,7 +57,10 @@
 #include "error.h"
 
 
+#define REALLY_CLOSED -3	/* fd that was closed and still is */
 #define EMPTY -2		/* marks an unused slot in redirtab */
+#define CLOSED -1		/* fd opened for redir needs to be closed */
+
 #ifndef PIPE_BUF
 # define PIPESIZE 4096		/* amount of buffering in a pipe */
 #else
@@ -116,7 +119,7 @@
 	}
 	sv = NULL;
 	INTOFF;
-	if (flags & REDIR_PUSH) {
+	if (likely(flags & REDIR_PUSH)) {
 		struct redirtab *q;
 		q = ckmalloc(sizeof (struct redirtab));
 		q->next = redirlist;
@@ -129,31 +132,34 @@
 	}
 	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 (newfd < -1)
+			continue;
+
+		fd = n->nfile.fd;
+
+		if (sv) {
+			p = &sv->renamed[fd];
+			i = *p;
+
+			if (likely(i == EMPTY)) {
+				i = CLOSED;
+				if (fd != newfd) {
+					i = savefd(fd);
+					fd = -1;
+				}
+			}
+
+			if (i == newfd)
+				/* Can only happen if i == newfd == CLOSED */
+				i = REALLY_CLOSED;
+
+			*p = i;
+		}
+
 		if (fd == newfd)
 			continue;
-		if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
-			int i = fcntl(fd, F_DUPFD, 10);
-			if (i == -1) {
-				i = errno;
-				if (i != EBADF) {
-					const char *m = strerror(i);
-					close(newfd);
-					sh_error("%d: %s", fd, m);
-					/* NOTREACHED */
-				}
-			} else {
-				*p = i;
-				close(fd);
-			}
-		} else {
-			close(fd);
-		}
+
 #ifdef notyet
 		dupredirect(n, newfd, memory);
 #else
@@ -167,7 +173,7 @@
 	if (memory[2])
 		out2 = &memout;
 #endif
-	if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
+	if (flags & REDIR_SAVEFD2 && sv->renamed[2] >= 0)
 		preverrout.fd = sv->renamed[2];
 }
 
@@ -208,15 +214,17 @@
 		if ((f = open64(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
 			goto ecreate;
 		break;
+	case NTOFD:
+	case NFROMFD:
+		f = redir->ndup.dupfd;
+		if (f == redir->nfile.fd)
+			f = -2;
+		break;
 	default:
 #ifdef DEBUG
 		abort();
 #endif
 		/* Fall through to eliminate warning. */
-	case NTOFD:
-	case NFROMFD:
-		f = -1;
-		break;
 	case NHERE:
 	case NXHERE:
 		f = openhere(redir);
@@ -244,27 +252,37 @@
 #endif
 	{
 	int fd = redir->nfile.fd;
+	int err = 0;
 
 #ifdef notyet
 	memory[fd] = 0;
 #endif
 	if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
-		if (redir->ndup.dupfd >= 0) {	/* if not ">&-" */
+		/* if not ">&-" */
+		if (f >= 0) {
 #ifdef notyet
-			if (memory[redir->ndup.dupfd])
+			if (memory[f])
 				memory[fd] = 1;
 			else
 #endif
-				copyfd(redir->ndup.dupfd, fd);
+				if (dup2(f, fd) < 0) {
+					err = errno;
+					goto err;
+				}
+			return;
 		}
-		return;
-	}
+		f = fd;
+	} else if (dup2(f, fd) < 0)
+		err = errno;
 
-	if (f != fd) {
-		copyfd(f, fd);
-		close(f);
-	}
+	close(f);
+	if (err < 0)
+		goto err;
+
 	return;
+
+err:
+	sh_error("%d: %s", f, strerror(err));
 }
 
 
@@ -326,12 +344,19 @@
 	INTOFF;
 	rp = redirlist;
 	for (i = 0 ; i < 10 ; i++) {
-		if (rp->renamed[i] != EMPTY) {
-			if (!drop) {
+		switch (rp->renamed[i]) {
+		case CLOSED:
+			if (!drop)
 				close(i);
-				copyfd(rp->renamed[i], i);
-			}
+			break;
+		case EMPTY:
+		case REALLY_CLOSED:
+			break;
+		default:
+			if (!drop)
+				dup2(rp->renamed[i], i);
 			close(rp->renamed[i]);
+			break;
 		}
 	}
 	redirlist = rp->next;
@@ -349,47 +374,42 @@
 INCLUDE "redir.h"
 
 RESET {
-	clearredir(0);
-}
-
-#endif
-
-/*
- * Discard all saved file descriptors.
- */
-
-void
-clearredir(int drop)
-{
+	/*
+	 * Discard all saved file descriptors.
+	 */
 	for (;;) {
 		nullredirs = 0;
 		if (!redirlist)
 			break;
-		popredir(drop);
+		popredir(0);
 	}
 }
 
+#endif
+
 
 
 /*
- * 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.
+ * Move a file descriptor to > 10.  Invokes sh_error on error unless
+ * the original file dscriptor is not open.
  */
 
 int
-copyfd(int from, int to)
+savefd(int from)
 {
 	int newfd;
+	int err;
 
-	newfd = fcntl(from, F_DUPFD, to);
-	if (newfd < 0) {
-		int errno2 = errno;
-		if (errno2 == EMFILE)
-			return EMPTY;
+	newfd = fcntl(from, F_DUPFD, 10);
+	err = newfd < 0 ? errno : 0;
+	if (err != EBADF) {
+		close(from);
+		if (err)
+			sh_error("%d: %s", from, strerror(err));
 		else
-			sh_error("%d: %s", from, strerror(errno2));
+			fcntl(newfd, F_SETFD, FD_CLOEXEC);
 	}
+
 	return newfd;
 }
 
diff --git a/usr/dash/redir.h b/usr/dash/redir.h
index 3297f6a..74c1a7e 100644
--- a/usr/dash/redir.h
+++ b/usr/dash/redir.h
@@ -44,6 +44,6 @@
 union node;
 void redirect(union node *, int);
 void popredir(int);
-void clearredir(int);
-int copyfd(int, int);
+void clearredir(void);
+int savefd(int);
 int redirectsafe(union node *, int);
diff --git a/usr/dash/trap.c b/usr/dash/trap.c
index 51e1d56..dc27224 100644
--- a/usr/dash/trap.c
+++ b/usr/dash/trap.c
@@ -294,7 +294,6 @@
 	char *q;
 	int i;
 	int savestatus;
-	int skip = 0;
 
 	savestatus = exitstatus;
 	pendingsigs = 0;
@@ -308,13 +307,13 @@
 		p = trap[i + 1];
 		if (!p)
 			continue;
-		skip = evalstring(p, SKIPEVAL);
+		evalstring(p, SKIPEVAL);
 		exitstatus = savestatus;
-		if (skip)
-			break;
+		if (evalskip)
+			return evalskip;
 	}
 
-	return skip;
+	return 0;
 }