Merge with master.kernel.org:/pub/scm/linux/kernel/git/herbert/klibc.git
diff --git a/Makefile b/Makefile
index e634aac..c260039 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 VERSION := $(shell cat version)
-SUBDIRS = klibc ash ipconfig nfsmount utils kinit gzip
+SUBDIRS = klibc dash ipconfig nfsmount utils kinit gzip
 SRCROOT = .
 
 all:
diff --git a/ash/Makefile b/ash/Makefile
deleted file mode 100644
index 521bf99..0000000
--- a/ash/Makefile
+++ /dev/null
@@ -1,161 +0,0 @@
-#	Makefile.linux
-
-KLIBC_SH_HISTORY = false
-KLIBC_SH_PRINTF  = false
-KLIBC_SH_ALIAS	 = false
-KLIBC_SH_MAIL	 = false
-KLIBC_SH_JOBS	 = false
-
-PROG=	sh
-SRCS =	arith.c arith_lex.c \
-	builtins.c cd.c echo.c error.c eval.c exec.c expand.c \
-	input.c jobs.c main.c memalloc.c miscbltin.c \
-	mystring.c nodes.c options.c parser.c redir.c \
-	show.c syntax.c trap.c output.c var.c test.c
-
-OBJ1 =	init.o
-OBJ2 =	arith.o arith_lex.o \
-	builtins.o cd.o echo.o error.o eval.o exec.o expand.o \
-	input.o jobs.o main.o memalloc.o miscbltin.o \
-	mystring.o nodes.o options.o parser.o redir.o \
-	show.o syntax.o trap.o output.o var.o test.o
-
-OBJS =	$(OBJ1) $(OBJ2)
-
-SHELL_DEFS = -DSHELL
-
-ifeq ($(strip $(KLIBC_SH_HISTORY)),true)
-SHELL_DEFS += -DKLIBC_SH_HISTORY
-SRCS += histedit.c
-OBJS += histedit.o
-endif
-ifeq ($(strip $(KLIBC_SH_PRINTF)),true)
-SHELL_DEFS += -DKLIBC_SH_PRINTF
-SRCS += printf.c
-OBJS += printf.o
-endif
-ifeq ($(strip $(KLIBC_SH_ALIAS)),true)
-SHELL_DEFS += -DKLIBC_SH_ALIAS
-SRCS += alias.c
-OBJS += alias.o
-endif
-ifeq ($(strip $(KLIBC_SH_MAIL)),true)
-SHELL_DEFS += -DKLIBC_SH_MAIL
-SRCS += mail.c
-OBJS += mail.o
-endif
-ifeq ($(strip $(KLIBC_SH_JOBS)),true)
-SHELL_DEFS += -DJOBS=1
-SRCS += kill.c
-OBJS += kill.o
-else
-SHELL_DEFS += -DJOBS=0
-endif
-
-
-OBJ_NODES = cd.o eval.o exec.o expand.o jobs.o main.o options.o parser.o redir.o show.o trap.o var.o
-OBJ_SYNTAX = eval.o exec.o expand.o input.o jobs.o mystring.o output.o parser.o syntax.o trap.o var.o
-OBJ_BUILDINS = eval.o exec.o
-
-# bison produces substantially smaller output than byacc
-YACC =  bison -y
-LEX  =  lex
-
-SRCROOT  = ..
-include ../MCONFIG
-
-MAKEDEPS = -Wp,-MD,.$(subst /,-,$*).d
-SRC_DEFS = 
-CFLAGS   = $(MAKEDEPS) $(OPTFLAGS) $(REQFLAGS) $(SHELL_DEFS) $(SRC_DEFS) -I.
-LIBS     = $(KLIBC) $(LIBGCC)
-
-HOST_CFLAGS  = -g -O -I. -DSHELL
-
-CLEANFILES =\
-	builtins.c builtins.h init.c \
-	nodes.c nodes.h signames.c token.h \
-	arith.c arith.h arith_lex.c \
-	printf.c test.c echo.c kill.c
-
-all:	$(PROG) $(PROG).shared
-
-$(OBJS): Makefile
-
-$(PROG): $(OBJS) $(LIBS) $(CRT0)
-	$(LD) $(LDFLAGS) -o $@ $(CRT0) $(OBJS) $(LIBS)
-	cp -f $@ $@.g
-	$(STRIPCMD) $@
-
-$(PROG).shared: $(OBJS) $(CRTSHARED) $(LIBSHARED) $(LIBGCC)
-	$(LD) $(LDFLAGS) -o $(PROG).shared $(EMAIN) $(CRTSHARED) $(OBJS) -R $(LIBSHARED) $(LIBGCC)
-	cp -f $@ $@.g
-	$(STRIPCMD) $@
-
-$(CRT0) $(LIBS):
-	@echo '*** error: $@ not up to date' || exit 1
-
-parser.o: token.h
-
-token.h: mktokens
-	sh ./mktokens
-
-$(OBJ_BUILDINS): builtins.h
-builtins.def: Makefile
-builtins.h builtins.c: mkbuiltins shell.h builtins.def
-	sh ./mkbuiltins shell.h builtins.def . "$(CFLAGS)"
-
-init.c: mkinit.sh $(SRCS)
-	sh mkinit.sh $(SRCS)
-	touch init.c
-
-$(OBJ_NODES): nodes.h
-$(OBJ_SYNTAX): syntax.h
-
-nodes.c: mknodes.sh nodetypes nodes.c.pat nodes.h
-nodes.h:
-	sh mknodes.sh nodetypes nodes.c.pat .
-
-signames.c: mksignames
-	./mksignames
-
-echo.c: bltin/echo.c
-	ln -sf $< $@
-
-test.c: bltin/test.c
-	ln -sf $< $@
-
-kill.c: bltin/kill.c
-	ln -sf $< $@
-
-printf.c: bltin/printf.c
-	ln -s $< $@
-
-arith.c: arith.y
-	$(YACC) $<
-	mv y.tab.c $@
-
-arith_lex.o: arith_lex.c arith.h
-
-arith_yylex.o: arith_yylex.c arith.h
-
-arith_lex.c: arith_lex.l
-	$(LEX) $<
-	mv lex.yy.c $@
-
-arith.h: arith.c
-	$(PERL) -ne 'print if ( /^\#\s*define\s+ARITH/ );' < $< > $@
-
-clean:
-	rm -f core $(CLEANFILES) $(OBJS) .*.d *.g
-	rm -f $(PROG) $(PROG).shared
-
-spotless: clean
-	find . \( -name \*~ -o -name \*.orig -o -name \*.rej -o -name \*.o \) -not -type d -print0 | xargs -0rt rm -f
-	rm -f tags
-
-install: all
-	$(INSTALL_EXEC) sh.shared $(INSTALLROOT)$(INSTALLDIR)/$(CROSS)bin/sh
-
-ifneq ($(wildcard .*.d),)
-include $(wildcard .*.d)
-endif
diff --git a/ash/README.klibc b/ash/README.klibc
deleted file mode 100644
index b16f66a..0000000
--- a/ash/README.klibc
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a version from ash pulled from NetBSD in mid-2004 and reduced
-in size somewhat, by removing mainly interactive features like job
-control.
-
diff --git a/ash/alias.c b/ash/alias.c
deleted file mode 100644
index 19e40e9..0000000
--- a/ash/alias.c
+++ /dev/null
@@ -1,278 +0,0 @@
-/*	$NetBSD: alias.c,v 1.12 2003/08/07 09:05:29 agc Exp $	*/
-
-/*-
- * Copyright (c) 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)alias.c	8.3 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: alias.c,v 1.12 2003/08/07 09:05:29 agc Exp $");
-#endif
-#endif /* not lint */
-
-#include <stdlib.h>
-#include "shell.h"
-#include "input.h"
-#include "output.h"
-#include "error.h"
-#include "memalloc.h"
-#include "mystring.h"
-#include "alias.h"
-#include "options.h"	/* XXX for argptr (should remove?) */
-#include "var.h"
-
-#define ATABSIZE 39
-
-struct alias *atab[ATABSIZE];
-
-STATIC void setalias(char *, char *);
-STATIC int unalias(char *);
-STATIC struct alias **hashalias(char *);
-
-STATIC
-void
-setalias(char *name, char *val)
-{
-	struct alias *ap, **app;
-
-	app = hashalias(name);
-	for (ap = *app; ap; ap = ap->next) {
-		if (equal(name, ap->name)) {
-			INTOFF;
-			ckfree(ap->val);
-			ap->val	= savestr(val);
-			INTON;
-			return;
-		}
-	}
-	/* not found */
-	INTOFF;
-	ap = ckmalloc(sizeof (struct alias));
-	ap->name = savestr(name);
-	/*
-	 * XXX - HACK: in order that the parser will not finish reading the
-	 * alias value off the input before processing the next alias, we
-	 * dummy up an extra space at the end of the alias.  This is a crock
-	 * and should be re-thought.  The idea (if you feel inclined to help)
-	 * is to avoid alias recursions.  The mechanism used is: when
-	 * expanding an alias, the value of the alias is pushed back on the
-	 * input as a string and a pointer to the alias is stored with the
-	 * string.  The alias is marked as being in use.  When the input
-	 * routine finishes reading the string, it markes the alias not
-	 * in use.  The problem is synchronization with the parser.  Since
-	 * it reads ahead, the alias is marked not in use before the
-	 * resulting token(s) is next checked for further alias sub.  The
-	 * H A C K is that we add a little fluff after the alias value
-	 * so that the string will not be exhausted.  This is a good
-	 * idea ------- ***NOT***
-	 */
-#ifdef notyet
-	ap->val = savestr(val);
-#else /* hack */
-	{
-	int len = strlen(val);
-	ap->val = ckmalloc(len + 2);
-	memcpy(ap->val, val, len);
-	ap->val[len] = ' ';	/* fluff */
-	ap->val[len+1] = '\0';
-	}
-#endif
-	ap->next = *app;
-	*app = ap;
-	INTON;
-}
-
-STATIC int
-unalias(char *name)
-{
-	struct alias *ap, **app;
-
-	app = hashalias(name);
-
-	for (ap = *app; ap; app = &(ap->next), ap = ap->next) {
-		if (equal(name, ap->name)) {
-			/*
-			 * if the alias is currently in use (i.e. its
-			 * buffer is being used by the input routine) we
-			 * just null out the name instead of freeing it.
-			 * We could clear it out later, but this situation
-			 * is so rare that it hardly seems worth it.
-			 */
-			if (ap->flag & ALIASINUSE)
-				*ap->name = '\0';
-			else {
-				INTOFF;
-				*app = ap->next;
-				ckfree(ap->name);
-				ckfree(ap->val);
-				ckfree(ap);
-				INTON;
-			}
-			return (0);
-		}
-	}
-
-	return (1);
-}
-
-#ifdef mkinit
-MKINIT void rmaliases(void);
-
-SHELLPROC {
-	rmaliases();
-}
-#endif
-
-void
-rmaliases(void)
-{
-	struct alias *ap, *tmp;
-	int i;
-
-	INTOFF;
-	for (i = 0; i < ATABSIZE; i++) {
-		ap = atab[i];
-		atab[i] = NULL;
-		while (ap) {
-			ckfree(ap->name);
-			ckfree(ap->val);
-			tmp = ap;
-			ap = ap->next;
-			ckfree(tmp);
-		}
-	}
-	INTON;
-}
-
-struct alias *
-lookupalias(char *name, int check)
-{
-	struct alias *ap = *hashalias(name);
-
-	for (; ap; ap = ap->next) {
-		if (equal(name, ap->name)) {
-			if (check && (ap->flag & ALIASINUSE))
-				return (NULL);
-			return (ap);
-		}
-	}
-
-	return (NULL);
-}
-
-char *
-get_alias_text(char *name)
-{
-	struct alias *ap;
-
-	ap = lookupalias(name, 0);
-	if (ap == NULL)
-		return NULL;
-	return ap->val;
-}
-
-/*
- * TODO - sort output
- */
-int
-aliascmd(int argc, char **argv)
-{
-	char *n, *v;
-	int ret = 0;
-	struct alias *ap;
-
-	if (argc == 1) {
-		int i;
-
-		for (i = 0; i < ATABSIZE; i++)
-			for (ap = atab[i]; ap; ap = ap->next) {
-				if (*ap->name != '\0') {
-					out1fmt("alias %s=", ap->name);
-					print_quoted(ap->val);
-					out1c('\n');
-				}
-			}
-		return (0);
-	}
-	while ((n = *++argv) != NULL) {
-		if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
-			if ((ap = lookupalias(n, 0)) == NULL) {
-				outfmt(out2, "alias: %s not found\n", n);
-				ret = 1;
-			} else {
-				out1fmt("alias %s=", n);
-				print_quoted(ap->val);
-				out1c('\n');
-			}
-		} else {
-			*v++ = '\0';
-			setalias(n, v);
-		}
-	}
-
-	return (ret);
-}
-
-int
-unaliascmd(int argc, char **argv)
-{
-	int i;
-
-	while ((i = nextopt("a")) != '\0') {
-		if (i == 'a') {
-			rmaliases();
-			return (0);
-		}
-	}
-	for (i = 0; *argptr; argptr++)
-		i = unalias(*argptr);
-
-	return (i);
-}
-
-STATIC struct alias **
-hashalias(char *p)
-{
-	unsigned int hashval;
-
-	hashval = *p << 4;
-	while (*p)
-		hashval+= *p++;
-	return &atab[hashval % ATABSIZE];
-}
diff --git a/ash/b.c b/ash/b.c
deleted file mode 100644
index 9b7f568..0000000
--- a/ash/b.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * This file was generated by the mkbuiltins program.
- */
-
-#ifndef lint
-static char rcsid[] = "b.c,v 1.2 1993/08/02 17:15:45 mycroft Exp";
-#endif /* not lint */
-
-#include "shell.h"
-#include "builtins.h"
-
-int bltincmd();
-int bgcmd();
-int breakcmd();
-int cdcmd();
-int dotcmd();
-int echocmd();
-int evalcmd();
-int execcmd();
-int exitcmd();
-int exportcmd();
-int fgcmd();
-int getoptscmd();
-int hashcmd();
-int jobidcmd();
-int jobscmd();
-int lccmd();
-int localcmd();
-int pwdcmd();
-int readcmd();
-int returncmd();
-int setcmd();
-int setvarcmd();
-int shiftcmd();
-int trapcmd();
-int truecmd();
-int umaskcmd();
-int unsetcmd();
-int waitcmd();
-
-int (*const builtinfunc[])() = {
-	bltincmd,
-	bgcmd,
-	breakcmd,
-	cdcmd,
-	dotcmd,
-	echocmd,
-	evalcmd,
-	execcmd,
-	exitcmd,
-	exportcmd,
-	fgcmd,
-	getoptscmd,
-	hashcmd,
-	jobidcmd,
-	jobscmd,
-	lccmd,
-	localcmd,
-	pwdcmd,
-	readcmd,
-	returncmd,
-	setcmd,
-	setvarcmd,
-	shiftcmd,
-	trapcmd,
-	truecmd,
-	umaskcmd,
-	unsetcmd,
-	waitcmd,
-};
-
-const struct builtincmd builtincmd[] = {
-	"command", 0,
-	"bg", 1,
-	"break", 2,
-	"continue", 2,
-	"cd", 3,
-	"chdir", 3,
-	".", 4,
-	"echo", 5,
-	"eval", 6,
-	"exec", 7,
-	"exit", 8,
-	"export", 9,
-	"readonly", 9,
-	"fg", 10,
-	"getopts", 11,
-	"hash", 12,
-	"jobid", 13,
-	"jobs", 14,
-	"lc", 15,
-	"local", 16,
-	"pwd", 17,
-	"read", 18,
-	"return", 19,
-	"set", 20,
-	"setvar", 21,
-	"shift", 22,
-	"trap", 23,
-	":", 24,
-	"true", 24,
-	"umask", 25,
-	"unset", 26,
-	"wait", 27,
-	NULL, 0
-};
diff --git a/ash/bltin/echo.c b/ash/bltin/echo.c
deleted file mode 100644
index 86db278..0000000
--- a/ash/bltin/echo.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*	$NetBSD: echo.c,v 1.11 2003/08/07 09:05:40 agc Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- *	@(#)echo.c	8.1 (Berkeley) 5/31/93
- */
-
-/*
- * Echo command.
- *
- * echo is steeped in tradition - several of them!
- * netbsd has supported 'echo [-n | -e] args' in spite of -e not being
- * documented anywhere.
- * Posix requires that -n be supported, output from strings containing
- * \ is implementation defined
- * The Single Unix Spec requires that \ escapes be treated as if -e
- * were set, but that -n not be treated as an option.
- * (ksh supports 'echo [-eEn] args', but not -- so that it is actually
- * impossible to actually output '-n')
- *
- * It is suggested that 'printf "%b" "string"' be used to get \ sequences
- * expanded.  printf is now a builtin of netbsd's sh and csh.
- */
-
-#define main echocmd
-
-#include "bltin/bltin.h"
-
-int
-main(int argc, char **argv)
-{
-	register char **ap;
-	register char *p;
-	register char c;
-	int count;
-	int nflag = 0;
-	int eflag = 0;
-
-	ap = argv;
-	if (argc)
-		ap++;
-
-	if ((p = *ap) != NULL) {
-		if (equal(p, "-n")) {
-			nflag = 1;
-			ap++;
-		} else if (equal(p, "-e")) {
-			eflag = 1;
-			ap++;
-		}
-	}
-
-	while ((p = *ap++) != NULL) {
-		while ((c = *p++) != '\0') {
-			if (c == '\\' && eflag) {
-				switch (*p++) {
-				case 'a':  c = '\a';  break;	/* bell */
-				case 'b':  c = '\b';  break;
-				case 'c':  return 0;		/* exit */
-				case 'e':  c =  033;  break;	/* escape */
-				case 'f':  c = '\f';  break;
-				case 'n':  c = '\n';  break;
-				case 'r':  c = '\r';  break;
-				case 't':  c = '\t';  break;
-				case 'v':  c = '\v';  break;
-				case '\\':  break;		/* c = '\\' */
-				case '0':
-					c = 0;
-					count = 3;
-					while (--count >= 0 && (unsigned)(*p - '0') < 8)
-						c = (c << 3) + (*p++ - '0');
-					break;
-				default:
-					/* Output the '/' and char following */
-					p--;
-					break;
-				}
-			}
-			putchar(c);
-		}
-		if (*ap)
-			putchar(' ');
-	}
-	if (! nflag)
-		putchar('\n');
-	return 0;
-}
diff --git a/ash/bltin/kill.c b/ash/bltin/kill.c
deleted file mode 100644
index a0c839e..0000000
--- a/ash/bltin/kill.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/* $NetBSD: kill.c,v 1.22 2003/08/04 22:31:24 jschauma Exp $ */
-
-/*
- * Copyright (c) 1988, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef __COPYRIGHT
-#define __COPYRIGHT(arg)
-#endif
-#if !defined(lint) && !defined(SHELL)
-__COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\n\
-	The Regents of the University of California.  All rights reserved.\n");
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)kill.c	8.4 (Berkeley) 4/28/95";
-#else
-__RCSID("$NetBSD: kill.c,v 1.22 2003/08/04 22:31:24 jschauma Exp $");
-#endif
-#endif /* not lint */
-
-#include <ctype.h>
-#ifndef __KLIBC__
-#include <err.h>
-#endif
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <termios.h>
-#include <unistd.h>
-#ifdef HAVE_LOCALE_H
-#include <locale.h>
-#endif
-#include <sys/ioctl.h>
-
-#ifdef SHELL            /* sh (aka ash) builtin */
-#define main killcmd
-#include "bltin/bltin.h"
-#endif /* SHELL */ 
-
-#ifdef __KLIBC__
-#undef _GNU_SOURCE
-#define sys_signame sys_siglist
-#endif
-
-static void nosig(char *);
-static void printsignals(FILE *);
-static int signame_to_signum(char *);
-static void usage(void);
-int main(int, char *[]);
-
-int
-main(int argc, char *argv[])
-{
-	int errors, numsig, pid;
-	char *ep;
-
-#ifndef SHELL
-	setprogname(argv[0]);
-#endif
-#ifdef HAVE_LOCALE_H
-	setlocale(LC_ALL, "");
-#endif
-	if (argc < 2)
-		usage();
-
-	numsig = SIGTERM;
-
-	argc--, argv++;
-	if (strcmp(*argv, "-l") == 0) {
-		argc--, argv++;
-		if (argc > 1)
-			usage();
-		if (argc == 1) {
-			if (isdigit((unsigned char)**argv) == 0)
-				usage();
-			numsig = strtol(*argv, &ep, 10);
-			if (*ep != '\0') {
-				errx(EXIT_FAILURE, "illegal signal number: %s",
-						*argv);
-				/* NOTREACHED */
-			}
-			if (numsig >= 128)
-				numsig -= 128;
-			if (numsig <= 0 || numsig >= NSIG)
-				nosig(*argv);
-#ifdef _GNU_SOURCE
-			printf("%s\n", strsignal(numsig));
-#else
-			printf("%s\n", sys_signame[numsig]);
-#endif
-			exit(0);
-		}
-		printsignals(stdout);
-		exit(0);
-	}
-
-	if (!strcmp(*argv, "-s")) {
-		argc--, argv++;
-		if (argc < 1) {
-			warnx("option requires an argument -- s");
-			usage();
-		}
-		if (strcmp(*argv, "0")) {
-			if ((numsig = signame_to_signum(*argv)) < 0)
-				nosig(*argv);
-		} else
-			numsig = 0;
-		argc--, argv++;
-	} else if (**argv == '-') {
-		++*argv;
-		if (isalpha((unsigned char)**argv)) {
-			if ((numsig = signame_to_signum(*argv)) < 0)
-				nosig(*argv);
-		} else if (isdigit((unsigned char)**argv)) {
-			numsig = strtol(*argv, &ep, 10);
-			if (!*argv || *ep) {
-				errx(EXIT_FAILURE, "illegal signal number: %s",
-						*argv);
-				/* NOTREACHED */
-			}
-			if (numsig < 0 || numsig >= NSIG)
-				nosig(*argv);
-		} else
-			nosig(*argv);
-		argc--, argv++;
-	}
-
-	if (argc == 0)
-		usage();
-
-	for (errors = 0; argc; argc--, argv++) {
-#ifdef SHELL
-		extern int getjobpgrp(const char *);
-		if (*argv[0] == '%') {
-			pid = getjobpgrp(*argv);
-			if (pid == 0) {
-				warnx("illegal job id: %s", *argv);
-				errors = 1;
-				continue;
-			}
-		} else 
-#endif
-		{
-			pid = strtol(*argv, &ep, 10);
-			if (!**argv || *ep) {
-				warnx("illegal process id: %s", *argv);
-				errors = 1;
-				continue;
-			}
-		}
-		if (kill(pid, numsig) == -1) {
-			warn("%s", *argv);
-			errors = 1;
-		}
-#ifdef SHELL
-		/* Wakeup the process if it was suspended, so it can
-		   exit without an explicit 'fg'. */
-		if (numsig == SIGTERM || numsig == SIGHUP)
-			kill(pid, SIGCONT);
-#endif
-	}
-
-	exit(errors);
-	/* NOTREACHED */
-}
-
-static int
-signame_to_signum(char *sig)
-{
-	int n;
-
-	if (strncasecmp(sig, "sig", 3) == 0)
-		sig += 3;
-	for (n = 1; n < NSIG; n++) {
-#ifdef _GNU_SOURCE
-		if (!strcasecmp(strsignal(n), sig))
-#else
-		if (!strcasecmp(sys_signame[n], sig))
-#endif
-			return (n);
-	}
-	return (-1);
-}
-
-static void
-nosig(char *name)
-{
-
-	warnx("unknown signal %s; valid signals:", name);
-	printsignals(stderr);
-	exit(1);
-	/* NOTREACHED */
-}
-
-static void
-printsignals(FILE *fp)
-{
-	int sig;
-	int len, nl;
-	const char *name;
-	int termwidth = 80;
-
-	if (isatty(fileno(fp))) {
-		struct winsize win;
-		if (ioctl(fileno(fp), TIOCGWINSZ, &win) == 0 && win.ws_col > 0)
-			termwidth = win.ws_col;
-	}
-
-	for (len = 0, sig = 1; sig < NSIG; sig++) {
-#ifdef _GNU_SOURCE
-		name = strsignal(sig);
-#else
-		name = sys_signame[sig];
-#endif
-		nl = 1 + strlen(name);
-
-		if (len + nl >= termwidth) {
-			fprintf(fp, "\n");
-			len = 0;
-		} else
-			if (len != 0)
-				fprintf(fp, " ");
-		len += nl;
-		fprintf(fp, "%s", name);
-	}
-	if (len != 0)
-		fprintf(fp, "\n");
-}
-
-static void
-usage(void)
-{
-
-	fprintf(stderr, "usage: %s [-s signal_name] pid ...\n"
-	    "       %s -l [exit_status]\n"
-	    "       %s -signal_name pid ...\n"
-	    "       %s -signal_number pid ...\n",
-	    getprogname(), getprogname(), getprogname(), getprogname());
-	exit(1);
-	/* NOTREACHED */
-}
diff --git a/ash/bltin/printf.c b/ash/bltin/printf.c
deleted file mode 100644
index ed8d053..0000000
--- a/ash/bltin/printf.c
+++ /dev/null
@@ -1,678 +0,0 @@
-/*	$NetBSD: printf.c,v 1.28 2003/06/25 12:56:59 dsl Exp $	*/
-
-/*
- * Copyright (c) 1989, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if !defined(BUILTIN) && !defined(SHELL)
-__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
-	The Regents of the University of California.  All rights reserved.\n");
-#endif
-#endif
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)printf.c	8.2 (Berkeley) 3/22/95";
-#else
-__RCSID("$NetBSD: printf.c,v 1.28 2003/06/25 12:56:59 dsl Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/types.h>
-
-#include <ctype.h>
-#ifndef __KLIBC__
-#include <err.h>
-#endif
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#ifdef HAVE_LOCALE_H
-#include <locale.h>
-#endif
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#ifdef __linux__
-#include "compat_linux.h"
-#endif
-
-#ifdef __GNUC__
-#define ESCAPE '\e'
-#else
-#define ESCAPE 033
-#endif
-
-static void	 conv_escape_str(char *, void (*)(int));
-static char	*conv_escape(char *, char *);
-static char	*conv_expand(const char *);
-static int	 getchr(void);
-#ifndef __KLIBC__
-static double	 getdouble(void);
-#endif
-static int	 getwidth(void);
-static intmax_t	 getintmax(void);
-static uintmax_t getuintmax(void);
-static char	*getstr(void);
-static char	*mklong(const char *, int);
-static void      check_conversion(const char *, const char *);
-static void	 usage(void); 
-
-static void	b_count(int);
-static void	b_output(int);
-static int	b_length;
-static char	*b_fmt;
-
-static int	rval;
-static char  **gargv;
-
-#ifdef BUILTIN		/* csh builtin */
-#define main progprintf
-#endif
-
-#ifdef SHELL		/* sh (aka ash) builtin */
-#define main printfcmd
-#include "bltin/bltin.h"
-#endif /* SHELL */
-
-#define PF(f, func) { \
-	if (fieldwidth != -1) { \
-		if (precision != -1) \
-			(void)printf(f, fieldwidth, precision, func); \
-		else \
-			(void)printf(f, fieldwidth, func); \
-	} else if (precision != -1) \
-		(void)printf(f, precision, func); \
-	else \
-		(void)printf(f, func); \
-}
-
-#define APF(cpp, f, func) { \
-	if (fieldwidth != -1) { \
-		if (precision != -1) \
-			(void)asprintf(cpp, f, fieldwidth, precision, func); \
-		else \
-			(void)asprintf(cpp, f, fieldwidth, func); \
-	} else if (precision != -1) \
-		(void)asprintf(cpp, f, precision, func); \
-	else \
-		(void)asprintf(cpp, f, func); \
-}
-
-int main(int, char **);
-int main(int argc, char *argv[])
-{
-	char *fmt, *start;
-	int fieldwidth, precision;
-	char nextch;
-	char *format;
-	int ch;
-
-#if !defined(SHELL) && !defined(BUILTIN)
-	(void)setlocale (LC_ALL, "");
-#endif
-
-	while ((ch = getopt(argc, argv, "")) != -1) {
-		switch (ch) {
-		case '?':
-		default:
-			usage();
-			return (1);
-		}
-	}
-	argc -= optind;
-	argv += optind;
-
-	if (argc < 1) {
-		usage();
-		return (1);
-	}
-
-	format = *argv;
-	gargv = ++argv;
-
-#define SKIP1	"#-+ 0"
-#define SKIP2	"*0123456789"
-	do {
-		/*
-		 * Basic algorithm is to scan the format string for conversion
-		 * specifications -- once one is found, find out if the field
-		 * width or precision is a '*'; if it is, gather up value. 
-		 * Note, format strings are reused as necessary to use up the
-		 * provided arguments, arguments of zero/null string are 
-		 * provided to use up the format string.
-		 */
-
-		/* find next format specification */
-		for (fmt = format; (ch = *fmt++) ;) {
-			if (ch == '\\') {
-				char c_ch;
-				fmt = conv_escape(fmt, &c_ch);
-				putchar(c_ch);
-				continue;
-			}
-			if (ch != '%' || (*fmt == '%' && ++fmt)) {
-				(void)putchar(ch);
-				continue;
-			}
-
-			/* Ok - we've found a format specification,
-			   Save its address for a later printf(). */
-			start = fmt - 1;
-
-			/* skip to field width */
-			fmt += strspn(fmt, SKIP1);
-			fieldwidth = *fmt == '*' ? getwidth() : -1;
-
-			/* skip to possible '.', get following precision */
-			fmt += strspn(fmt, SKIP2);
-			if (*fmt == '.')
-				++fmt;
-			precision = *fmt == '*' ? getwidth() : -1;
-
-			fmt += strspn(fmt, SKIP2);
-
-			ch = *fmt;
-			if (!ch) {
-				warnx("missing format character");
-				return (1);
-			}
-			/* null terminate format string to we can use it
-			   as an argument to printf. */
-			nextch = fmt[1];
-			fmt[1] = 0;
-			switch (ch) {
-
-			case 'B': {
-				const char *p = conv_expand(getstr());
-				*fmt = 's';
-				PF(start, p);
-				break;
-			}
-			case 'b': {
-				/* There has to be a better way to do this,
-				 * but the string we generate might have
-				 * embedded nulls. */
-				static char *a, *t;
-				char *cp = getstr();
-				/* Free on entry in case shell longjumped out */
-				if (a != NULL)
-					free(a);
-				a = NULL;
-				if (t != NULL)
-					free(t);
-				t = NULL;
-				/* Count number of bytes we want to output */
-				b_length = 0;
-				conv_escape_str(cp, b_count);
-				t = malloc(b_length + 1);
-				if (t == NULL)
-					break;
-				memset(t, 'x', b_length);
-				t[b_length] = 0;
-				/* Get printf to calculate the lengths */
-				*fmt = 's';
-				APF(&a, start, t);
-				b_fmt = a;
-				/* Output leading spaces and data bytes */
-				conv_escape_str(cp, b_output);
-				/* Add any trailing spaces */
-				printf("%s", b_fmt);
-				break;
-			}
-			case 'c': {
-				char p = getchr();
-				PF(start, p);
-				break;
-			}
-			case 's': {
-				char *p = getstr();
-				PF(start, p);
-				break;
-			}
-			case 'd':
-			case 'i': {
-				intmax_t p = getintmax();
-				char *f = mklong(start, ch);
-				PF(f, p);
-				break;
-			}
-			case 'o':
-			case 'u':
-			case 'x':
-			case 'X': {
-				uintmax_t p = getuintmax();
-				char *f = mklong(start, ch);
-				PF(f, p);
-				break;
-			}
-#ifndef __KLIBC__
-			case 'e':
-			case 'E':
-			case 'f':
-			case 'g':
-			case 'G': {
-				double p = getdouble();
-				PF(start, p);
-				break;
-			}
-#endif
-			default:
-				warnx("%s: invalid directive", start);
-				return (1);
-			}
-			*fmt++ = ch;
-			*fmt = nextch;
-			/* escape if a \c was encountered */
-			if (rval & 0x100)
-				return (rval & ~0x100);
-		}
-	} while (gargv != argv && *gargv);
-
-	return (rval);
-}
-
-/* helper functions for conv_escape_str */
-
-static void
-b_count(int ch)
-{
-	b_length++;
-}
-
-/* Output one converted character for every 'x' in the 'format' */
-
-static void
-b_output(int ch)
-{
-	for (;;) {
-		switch (*b_fmt++) {
-		case 0:
-			b_fmt--;
-			return;
-		case ' ':
-			putchar(' ');
-			break;
-		default:
-			putchar(ch);
-			return;
-		}
-	}
-}
-
-
-/*
- * Print SysV echo(1) style escape string 
- *	Halts processing string if a \c escape is encountered.
- */
-static void
-conv_escape_str(char *str, void (*do_putchar)(int))
-{
-	int value;
-	int ch;
-	char c;
-
-	while ((ch = *str++)) {
-		if (ch != '\\') {
-			do_putchar(ch);
-			continue;
-		}
-
-		ch = *str++;
-		if (ch == 'c') {
-			/* \c as in SYSV echo - abort all processing.... */
-			rval |= 0x100;
-			break;
-		}
-
-		/* 
-		 * %b string octal constants are not like those in C.
-		 * They start with a \0, and are followed by 0, 1, 2, 
-		 * or 3 octal digits. 
-		 */
-		if (ch == '0') {
-			char octnum[4], *oct_end;
-			octnum[0] = str[0];
-			octnum[1] = str[1];
-			octnum[2] = str[2];
-			octnum[3] = 0;
-			do_putchar(strtoul(octnum, &oct_end, 8));
-			str += oct_end - octnum;
-			continue;
-		}
-
-		/* \[M][^|-]C as defined by vis(3) */
-		if (ch == 'M' && *str == '-') {
-			do_putchar(0200 | str[1]);
-			str += 2;
-			continue;
-		}
-		if (ch == 'M' && *str == '^') {
-			str++;
-			value = 0200;
-			ch = '^';
-		} else
-			value = 0;
-		if (ch == '^') {
-			ch = *str++;
-			if (ch == '?')
-				value |= 0177;
-			else
-				value |= ch & 037;
-			do_putchar(value);
-			continue;
-		}
-
-		/* Finally test for sequences valid in the format string */
-		str = conv_escape(str - 1, &c);
-		do_putchar(c);
-	}
-}
-
-/*
- * Print "standard" escape characters 
- */
-static char *
-conv_escape(char *str, char *conv_ch)
-{
-	int value;
-	int ch;
-	char num_buf[4], *num_end;
-
-	ch = *str++;
-
-	switch (ch) {
-	case '0': case '1': case '2': case '3':
-	case '4': case '5': case '6': case '7':
-		num_buf[0] = ch;
-		ch = str[0];
-		num_buf[1] = ch;
-		num_buf[2] = ch ? str[1] : 0;
-		num_buf[3] = 0;
-		value = strtoul(num_buf, &num_end, 8);
-		str += num_end  - (num_buf + 1);
-		break;
-
-	case 'x':
-		/* Hexadecimal character constants are not required to be
-		   supported (by SuS v1) because there is no consistent
-		   way to detect the end of the constant.
-		   Supporting 2 byte constants is a compromise. */
-		ch = str[0];
-		num_buf[0] = ch;
-		num_buf[1] = ch ? str[1] : 0;
-		num_buf[2] = 0;
-		value = strtoul(num_buf, &num_end, 16);
-		str += num_end - num_buf;
-		break;
-
-	case '\\':	value = '\\';	break;	/* backslash */
-	case '\'':	value = '\'';	break;	/* single quote */
-	case '"':	value = '"';	break;	/* double quote */
-	case 'a':	value = '\a';	break;	/* alert */
-	case 'b':	value = '\b';	break;	/* backspace */
-	case 'e':	value = ESCAPE;	break;	/* escape */
-	case 'f':	value = '\f';	break;	/* form-feed */
-	case 'n':	value = '\n';	break;	/* newline */
-	case 'r':	value = '\r';	break;	/* carriage-return */
-	case 't':	value = '\t';	break;	/* tab */
-	case 'v':	value = '\v';	break;	/* vertical-tab */
-
-	default:
-		warnx("unknown escape sequence `\\%c'", ch);
-		rval = 1;
-		value = ch;
-		break;
-	}
-
-	*conv_ch = value;
-	return str;
-}
-
-/* expand a string so that everything is printable */
-
-static char *
-conv_expand(const char *str)
-{
-	static char *conv_str;
-	char *cp;
-	int ch;
-
-	if (conv_str)
-		free(conv_str);
-	/* get a buffer that is definitely large enough.... */
-	conv_str = malloc(4 * strlen(str) + 1);
-	if (!conv_str)
-		return "<no memory>";
-	cp = conv_str;
-
-	while ((ch = *(unsigned char *)str++)) {
-		switch (ch) {
-		/* Use C escapes for expected control characters */
-		case '\\':	ch = '\\';	break;	/* backslash */
-		case '\'':	ch = '\'';	break;	/* single quote */
-		case '"':	ch = '"';	break;	/* double quote */
-		case '\a':	ch = 'a';	break;	/* alert */
-		case '\b':	ch = 'b';	break;	/* backspace */
-		case ESCAPE:	ch = 'e';	break;	/* escape */
-		case '\f':	ch = 'f';	break;	/* form-feed */
-		case '\n':	ch = 'n';	break;	/* newline */
-		case '\r':	ch = 'r';	break;	/* carriage-return */
-		case '\t':	ch = 't';	break;	/* tab */
-		case '\v':	ch = 'v';	break;	/* vertical-tab */
-		default:
-			/* Copy anything printable */
-			if (isprint(ch)) {
-				*cp++ = ch;
-				continue;
-			}
-			/* Use vis(3) encodings for the rest */
-			*cp++ = '\\';
-			if (ch & 0200) {
-				*cp++ = 'M';
-				ch &= ~0200;
-			}
-			if (ch == 0177) {
-				*cp++ = '^';
-				*cp++ = '?';
-				continue;
-			}
-			if (ch < 040) {
-				*cp++ = '^';
-				*cp++ = ch | 0100;
-				continue;
-			}
-			*cp++ = '-';
-			*cp++ = ch;
-			continue;
-		}
-		*cp++ = '\\';
-		*cp++ = ch;
-	}
-
-	*cp = 0;
-	return conv_str;
-}
-
-static char *
-mklong(const char *str, int ch)
-{
-	static char copy[64];
-	size_t len;	
-
-	len = strlen(str) + 2;
-	if (len > sizeof copy) {
-		warnx("format %s too complex\n", str);
-		len = 4;
-	}
-	(void)memmove(copy, str, len - 3);
-	copy[len - 3] = 'j';
-	copy[len - 2] = ch;
-	copy[len - 1] = '\0';
-	return (copy);	
-}
-
-static int
-getchr(void)
-{
-	if (!*gargv)
-		return ('\0');
-	return ((int)**gargv++);
-}
-
-static char *
-getstr(void)
-{
-	if (!*gargv)
-		return ("");
-	return (*gargv++);
-}
-
-static int
-getwidth(void)
-{
-	long val;
-	char *s, *ep;
-
-	s = *gargv;
-	if (!*gargv)
-		return (0);
-	gargv++;
-
-	errno = 0;
-	val = strtoul(s, &ep, 0);
-	check_conversion(s, ep);
-
-	/* Arbitrarily 'restrict' field widths to 1Mbyte */
-	if (val < 0 || val > 1 << 20) {
-		warnx("%s: invalid field width", s);
-		return 0;
-	}
-
-	return val;
-}
-
-static intmax_t
-getintmax(void)
-{
-	intmax_t val;
-	char *cp, *ep;
-
-	cp = *gargv;
-	if (cp == NULL)
-		return 0;
-	gargv++;
-
-	if (*cp == '\"' || *cp == '\'')
-		return *(cp+1);
-
-	errno = 0;
-	val = strtoimax(cp, &ep, 0);
-	check_conversion(cp, ep);
-	return val;
-}
-
-static uintmax_t
-getuintmax(void)
-{
-	uintmax_t val;
-	char *cp, *ep;
-
-	cp = *gargv;
-	if (cp == NULL)
-		return 0;
-	gargv++;
-
-	if (*cp == '\"' || *cp == '\'')
-		return *(cp+1);
-
-	/* strtoumax won't error -ve values */
-	while (isspace(*(unsigned char *)cp))
-		cp++;
-	if (*cp == '-') {
-		warnx("%s: expected positive numeric value", cp);
-		rval = 1;
-		return 0;
-	}
-
-	errno = 0;
-	val = strtoumax(cp, &ep, 0);
-	check_conversion(cp, ep);
-	return val;
-}
-
-#ifndef __KLIBC__
-static double
-getdouble(void)
-{
-	double val;
-	char *ep;
-
-	if (!*gargv)
-		return (0.0);
-
-	if (**gargv == '\"' || **gargv == '\'')
-		return (double) *((*gargv++)+1);
-
-	errno = 0;
-	val = strtod(*gargv, &ep);
-	check_conversion(*gargv++, ep);
-	return val;
-}
-#endif
-
-static void
-check_conversion(const char *s, const char *ep)
-{
-	if (*ep) {
-		if (ep == s)
-			warnx("%s: expected numeric value", s);
-		else
-			warnx("%s: not completely converted", s);
-		rval = 1;
-	} else if (errno == ERANGE) {
-		warnx("%s: %s", s, strerror(ERANGE));
-		rval = 1;
-	}
-}
-
-static void
-usage(void)
-{
-	(void)fprintf(stderr, "usage: printf format [arg ...]\n");
-}
diff --git a/ash/builtins.def b/ash/builtins.def
deleted file mode 100644
index ae772d0..0000000
--- a/ash/builtins.def
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/bin/sh -
-#	$NetBSD: builtins.def,v 1.20 2004/06/06 07:03:11 christos Exp $
-#
-# Copyright (c) 1991, 1993
-#	The Regents of the University of California.  All rights reserved.
-#
-# This code is derived from software contributed to Berkeley by
-# Kenneth Almquist.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. Neither the name of the University nor the names of its contributors
-#    may be used to endorse or promote products derived from this software
-#    without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-#	@(#)builtins.def	8.4 (Berkeley) 5/4/95
-
-#
-# This file lists all the builtin commands.  The first column is the name
-# of a C routine.
-# The -j flag specifies that this command is to be excluded from systems
-# without job control.
-# The -h flag specifies that this command is to be excluded from systems
-# based on the SMALL compile-time symbol.
-# The -s flag specifies that this is a posix 'special builtin' command.
-# The -u flag specifies that this is a posix 'standard utility'.
-# The rest of the line specifies the command name or names used to run
-# the command.
-
-bltincmd	-u command
-bgcmd -j	-u bg
-breakcmd	-s break -s continue
-cdcmd		-u cd chdir
-dotcmd		-s .
-echocmd		echo
-evalcmd		-s eval
-execcmd		-s exec
-exitcmd		-s exit
-expcmd		exp let
-exportcmd	-s export -s readonly
-falsecmd	-u false
-#ifdef KLIBC_SH_HISTORY
-histcmd -h	-u fc
-inputrc		inputrc
-#endif
-fgcmd -j	-u fg
-getoptscmd	-u getopts
-hashcmd		hash
-jobidcmd	jobid
-jobscmd		-u jobs
-localcmd	local
-#ifdef KLIBC_SH_PRINTF
-printfcmd	printf
-#endif
-pwdcmd		-u pwd
-readcmd		-u read
-returncmd	-s return
-setcmd		-s set
-setvarcmd	setvar
-shiftcmd	-s shift
-timescmd	-s times
-trapcmd		-s trap
-truecmd		-s : -u true
-typecmd		type
-umaskcmd	-u umask
-unsetcmd	-s unset
-waitcmd		-u wait
-#ifdef KLIBC_SH_ALIAS
-aliascmd	-u alias
-unaliascmd	-u unalias
-#endif
-#ifndef __KLIBC__
-ulimitcmd	ulimit
-#endif
-testcmd		test [
-killcmd		-j -u kill	# mandated by posix for 'kill %job'
-#newgrp		-u newgrp	# optional command in posix
-
-#exprcmd	expr
diff --git a/ash/cd.c b/ash/cd.c
deleted file mode 100644
index 5422acb..0000000
--- a/ash/cd.c
+++ /dev/null
@@ -1,455 +0,0 @@
-/*	$NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)cd.c	8.2 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-/*
- * The cd and pwd commands.
- */
-
-#include "shell.h"
-#include "var.h"
-#include "nodes.h"	/* for jobs.h */
-#include "jobs.h"
-#include "options.h"
-#include "output.h"
-#include "memalloc.h"
-#include "error.h"
-#include "exec.h"
-#include "redir.h"
-#include "mystring.h"
-#include "show.h"
-#include "cd.h"
-
-STATIC int docd(char *, int);
-STATIC char *getcomponent(void);
-STATIC void updatepwd(char *);
-STATIC void find_curdir(int noerror);
-
-char *curdir = NULL;		/* current working directory */
-char *prevdir;			/* previous working directory */
-STATIC char *cdcomppath;
-
-int
-cdcmd(int argc, char **argv)
-{
-	const char *dest;
-	const char *path;
-	char *p, *d;
-	struct stat statb;
-	int print = cdprint;	/* set -cdprint to enable */
-
-	(void)argc; (void)argv;
-
-	nextopt(nullstr);
-
-	/*
-	 * Try (quite hard) to have 'curdir' defined, nothing has set
-	 * it on entry to the shell, but we want 'cd fred; cd -' to work.
-	 */
-	getpwd(1);
-	dest = *argptr;
-	if (dest == NULL) {
-		dest = bltinlookup("HOME", 1);
-		if (dest == NULL)
-			error("HOME not set");
-	} else {
-		if (argptr[1]) {
-			/* Do 'ksh' style substitution */
-			if (!curdir)
-				error("PWD not set");
-			p = strstr(curdir, dest);
-			if (!p)
-				error("bad substitution");
-			d = stalloc(strlen(curdir) + strlen(argptr[1]) + 1);
-			memcpy(d, curdir, p - curdir);
-			strcpy(d + (p - curdir), argptr[1]);
-			strcat(d, p + strlen(dest));
-			dest = d;
-			print = 1;
-		}
-	}
-
-	if (dest[0] == '-' && dest[1] == '\0') {
-		dest = prevdir ? prevdir : curdir;
-		print = 1;
-	}
-	if (*dest == '\0')
-	        dest = ".";
-	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
-		path = nullstr;
-	while ((p = padvance(&path, dest)) != NULL) {
-		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
-			if (!print) {
-				/*
-				 * XXX - rethink
-				 */
-				if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
-					p += 2;
-				print = strcmp(p, dest);
-			}
-			if (docd(p, print) >= 0)
-				return 0;
-
-		}
-	}
-	error("can't cd to %s", dest);
-	/* NOTREACHED */
-}
-
-
-/*
- * Actually do the chdir.  In an interactive shell, print the
- * directory name if "print" is nonzero.
- */
-
-STATIC int
-docd(char *dest, int print)
-{
-	char *p;
-	char *q;
-	char *component;
-	struct stat statb;
-	int first;
-	int badstat;
-
-	TRACE(("docd(\"%s\", %d) called\n", dest, print));
-
-	/*
-	 *  Check each component of the path. If we find a symlink or
-	 *  something we can't stat, clear curdir to force a getcwd()
-	 *  next time we get the value of the current directory.
-	 */
-	badstat = 0;
-	cdcomppath = stalloc(strlen(dest) + 1);
-	scopy(dest, cdcomppath);
-	STARTSTACKSTR(p);
-	if (*dest == '/') {
-		STPUTC('/', p);
-		cdcomppath++;
-	}
-	first = 1;
-	while ((q = getcomponent()) != NULL) {
-		if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
-			continue;
-		if (! first)
-			STPUTC('/', p);
-		first = 0;
-		component = q;
-		while (*q)
-			STPUTC(*q++, p);
-		if (equal(component, ".."))
-			continue;
-		STACKSTRNUL(p);
-		if ((lstat(stackblock(), &statb) < 0)
-		    || (S_ISLNK(statb.st_mode)))  {
-			/* print = 1; */
-			badstat = 1;
-			break;
-		}
-	}
-
-	INTOFF;
-	if (chdir(dest) < 0) {
-		INTON;
-		return -1;
-	}
-	updatepwd(badstat ? NULL : dest);
-	INTON;
-	if (print && iflag && curdir)
-		out1fmt("%s\n", curdir);
-	return 0;
-}
-
-
-/*
- * Get the next component of the path name pointed to by cdcomppath.
- * This routine overwrites the string pointed to by cdcomppath.
- */
-
-STATIC char *
-getcomponent()
-{
-	char *p;
-	char *start;
-
-	if ((p = cdcomppath) == NULL)
-		return NULL;
-	start = cdcomppath;
-	while (*p != '/' && *p != '\0')
-		p++;
-	if (*p == '\0') {
-		cdcomppath = NULL;
-	} else {
-		*p++ = '\0';
-		cdcomppath = p;
-	}
-	return start;
-}
-
-
-
-/*
- * Update curdir (the name of the current directory) in response to a
- * cd command.  We also call hashcd to let the routines in exec.c know
- * that the current directory has changed.
- */
-
-STATIC void
-updatepwd(char *dir)
-{
-	char *new;
-	char *p;
-
-	hashcd();				/* update command hash table */
-
-	/*
-	 * If our argument is NULL, we don't know the current directory
-	 * any more because we traversed a symbolic link or something
-	 * we couldn't stat().
-	 */
-	if (dir == NULL || curdir == NULL)  {
-		if (prevdir)
-			ckfree(prevdir);
-		INTOFF;
-		prevdir = curdir;
-		curdir = NULL;
-		getpwd(1);
-		INTON;
-		if (curdir)
-			setvar("PWD", curdir, VEXPORT);
-		else
-			unsetvar("PWD", 0);
-		return;
-	}
-	cdcomppath = stalloc(strlen(dir) + 1);
-	scopy(dir, cdcomppath);
-	STARTSTACKSTR(new);
-	if (*dir != '/') {
-		p = curdir;
-		while (*p)
-			STPUTC(*p++, new);
-		if (p[-1] == '/')
-			STUNPUTC(new);
-	}
-	while ((p = getcomponent()) != NULL) {
-		if (equal(p, "..")) {
-			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
-		} else if (*p != '\0' && ! equal(p, ".")) {
-			STPUTC('/', new);
-			while (*p)
-				STPUTC(*p++, new);
-		}
-	}
-	if (new == stackblock())
-		STPUTC('/', new);
-	STACKSTRNUL(new);
-	INTOFF;
-	if (prevdir)
-		ckfree(prevdir);
-	prevdir = curdir;
-	curdir = savestr(stackblock());
-	setvar("PWD", curdir, VEXPORT);
-	INTON;
-}
-
-/*
- * Posix says the default should be 'pwd -L' (as below), however
- * the 'cd' command (above) does something much nearer to the
- * posix 'cd -P' (not the posix default of 'cd -L').
- * If 'cd' is changed to support -P/L then the default here
- * needs to be revisited if the historic behaviour is to be kept.
- */
-
-int
-pwdcmd(int argc, char **argv)
-{
-	int i;
-	char opt = 'L';
-
-	(void)argc; (void)argv;
-
-	while ((i = nextopt("LP")) != '\0')
-		opt = i;
-	if (*argptr)
-		error("unexpected argument");
-
-	if (opt == 'L')
-		getpwd(0);
-	else
-		find_curdir(0);
-
-	setvar("PWD", curdir, VEXPORT);
-	out1str(curdir);
-	out1c('\n');
-	return 0;
-}
-
-
-
-
-#define MAXPWD PATH_MAX
-
-/*
- * Find out what the current directory is. If we already know the current
- * directory, this routine returns immediately.
- */
-void
-getpwd(int noerror)
-{
-	char *pwd;
-	struct stat stdot, stpwd;
-	static int first = 1;
-
-	if (curdir)
-		return;
-
-	if (first) {
-		first = 0;
-		pwd = getenv("PWD");
-		if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
-		    stat(pwd, &stpwd) != -1 &&
-		    stdot.st_dev == stpwd.st_dev &&
-		    stdot.st_ino == stpwd.st_ino) {
-			curdir = savestr(pwd);
-			return;
-		}
-	}
-
-	find_curdir(noerror);
-
-	return;
-}
-
-STATIC void
-find_curdir(int noerror)
-{
-	int i;
-	char *pwd;
-
-	/*
-	 * Things are a bit complicated here; we could have just used
-	 * getcwd, but traditionally getcwd is implemented using popen
-	 * to /bin/pwd. This creates a problem for us, since we cannot
-	 * keep track of the job if it is being ran behind our backs.
-	 * So we re-implement getcwd(), and we suppress interrupts
-	 * throughout the process. This is not completely safe, since
-	 * the user can still break out of it by killing the pwd program.
-	 * We still try to use getcwd for systems that we know have a
-	 * c implementation of getcwd, that does not open a pipe to
-	 * /bin/pwd.
-	 */
-#if defined(__NetBSD__) || defined(__SVR4) || defined(__linux__)
-
-	for (i = MAXPWD;; i *= 2) {
-		pwd = stalloc(i);
-		if (getcwd(pwd, i) != NULL) {
-			curdir = savestr(pwd);
-			return;
-		}
-		stunalloc(pwd);
-		if (errno == ERANGE)
-			continue;
-		if (!noerror)
-			error("getcwd() failed: %s", strerror(errno));
-		return;
-	}
-#else
-	{
-		char *p;
-		int status;
-		struct job *jp;
-		int pip[2];
-
-		pwd = stalloc(MAXPWD);
-		INTOFF;
-		if (pipe(pip) < 0)
-			error("Pipe call failed");
-		jp = makejob((union node *)NULL, 1);
-		if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
-			(void) close(pip[0]);
-			if (pip[1] != 1) {
-				close(1);
-				copyfd(pip[1], 1);
-				close(pip[1]);
-			}
-			(void) execl("/bin/pwd", "pwd", (char *)0);
-			error("Cannot exec /bin/pwd");
-		}
-		(void) close(pip[1]);
-		pip[1] = -1;
-		p = pwd;
-		while ((i = read(pip[0], p, pwd + MAXPWD - p)) > 0
-		     || (i == -1 && errno == EINTR)) {
-			if (i > 0)
-				p += i;
-		}
-		(void) close(pip[0]);
-		pip[0] = -1;
-		status = waitforjob(jp);
-		if (status != 0)
-			error((char *)0);
-		if (i < 0 || p == pwd || p[-1] != '\n') {
-			if (noerror) {
-				INTON;
-				return;
-			}
-			error("pwd command failed");
-		}
-		p[-1] = '\0';
-		INTON;
-		curdir = savestr(pwd);
-		return;
-	}
-#endif
-}
diff --git a/ash/compat_linux.h b/ash/compat_linux.h
deleted file mode 100644
index 216ed87..0000000
--- a/ash/compat_linux.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * compat.h
- *
- * Compability functions for Linux / Klibc
- */
-
-#ifndef _LINUX_COMPAT_H
-#define _LINUX_COMPAT_H
-
-#define MAXPATHLEN PATH_MAX
-
-#ifdef __KLIBC__
-#define _PATH_TMP "/tmp/"
-#endif /* __KLIBC__ */
-
-#ifndef __KLIBC__
-static inline size_t strlcpy ( char *   	  d,
-			       const char *  	  s,
-			       size_t  	  bufsize )  	
-{
-    size_t len = strlen(s);
-    size_t ret = len;
-
-    if (bufsize <= 0) return 0;
-    if (len >= bufsize) len = bufsize-1;
-    memcpy(d, s, len);
-    d[len] = 0;
-    return ret;
-}
-#endif
-
-#endif
diff --git a/ash/errmsg.c b/ash/errmsg.c
deleted file mode 100644
index fb9dc84..0000000
--- a/ash/errmsg.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/*-
- * Copyright (c) 1991 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by the University of
- *	California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef lint
-/*static char sccsid[] = "from: @(#)errmsg.c	5.1 (Berkeley) 3/7/91";*/
-static char rcsid[] = "errmsg.c,v 1.4 1993/08/01 18:58:20 mycroft Exp";
-#endif /* not lint */
-
-#include "shell.h"
-#include "output.h"
-#include "errmsg.h"
-#include <errno.h>
-
-
-#define ALL (E_OPEN|E_CREAT|E_EXEC)
-
-
-struct errname {
-	short errcode;		/* error number */
-	short action;		/* operation which encountered the error */
-	char *msg;		/* text describing the error */
-};
-
-
-STATIC const struct errname errormsg[] = {
-	EINTR, ALL,	"interrupted",
-	EACCES, ALL,	"permission denied",
-	EIO, ALL,		"I/O error",
-	ENOENT, E_OPEN,	"no such file",
-	ENOENT, E_CREAT,	"directory nonexistent",
-	ENOENT, E_EXEC,	"not found",
-	ENOTDIR, E_OPEN,	"no such file",
-	ENOTDIR, E_CREAT,	"directory nonexistent",
-	ENOTDIR, E_EXEC,	"not found",
-	EISDIR, ALL,	"is a directory",
-/*    EMFILE, ALL,	"too many open files", */
-	ENFILE, ALL,	"file table overflow",
-	ENOSPC, ALL,	"file system full",
-#ifdef EDQUOT
-	EDQUOT, ALL,	"disk quota exceeded",
-#endif
-#ifdef ENOSR
-	ENOSR, ALL,	"no streams resources",
-#endif
-	ENXIO, ALL,	"no such device or address",
-	EROFS, ALL,	"read-only file system",
-	ETXTBSY, ALL,	"text busy",
-#ifdef SYSV
-	EAGAIN, E_EXEC,	"not enough memory",
-#endif
-	ENOMEM, ALL,	"not enough memory",
-#ifdef ENOLINK
-	ENOLINK, ALL,	"remote access failed"
-#endif
-#ifdef EMULTIHOP
-	EMULTIHOP, ALL,	"remote access failed",
-#endif
-#ifdef ECOMM
-	ECOMM, ALL,	"remote access failed",
-#endif
-#ifdef ESTALE
-	ESTALE, ALL,	"remote access failed",
-#endif
-#ifdef ETIMEDOUT
-	ETIMEDOUT, ALL,	"remote access failed",
-#endif
-#ifdef ELOOP
-	ELOOP, ALL,	"symbolic link loop",
-#endif
-	E2BIG, E_EXEC,	"argument list too long",
-#ifdef ELIBACC
-	ELIBACC, E_EXEC,	"shared library missing",
-#endif
-	0, 0,		NULL
-};
-
-
-/*
- * Return a string describing an error.  The returned string may be a
- * pointer to a static buffer that will be overwritten on the next call.
- * Action describes the operation that got the error.
- */
-
-char *
-errmsg(e, action) {
-	struct errname const *ep;
-	static char buf[12];
-
-	for (ep = errormsg ; ep->errcode ; ep++) {
-		if (ep->errcode == e && (ep->action & action) != 0)
-			return ep->msg;
-	}
-	fmtstr(buf, sizeof buf, "error %d", e);
-	return buf;
-}
diff --git a/ash/errmsg.h b/ash/errmsg.h
deleted file mode 100644
index b5a43b9..0000000
--- a/ash/errmsg.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*-
- * Copyright (c) 1991 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by the University of
- *	California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- *	from: @(#)errmsg.h	5.1 (Berkeley) 3/7/91
- *	errmsg.h,v 1.4 1993/08/01 18:58:33 mycroft Exp
- */
-
-#define E_OPEN 01
-#define E_CREAT 02
-#define E_EXEC 04
-
-#ifdef __STDC__
-char *errmsg(int, int);
-#else
-char *errmsg();
-#endif
diff --git a/ash/error.c b/ash/error.c
deleted file mode 100644
index 03a2dc4..0000000
--- a/ash/error.c
+++ /dev/null
@@ -1,374 +0,0 @@
-/*	$NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)error.c	8.2 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc Exp $");
-#endif
-#endif /* not lint */
-
-/*
- * Errors and exceptions.
- */
-
-#include <signal.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "shell.h"
-#include "main.h"
-#include "options.h"
-#include "output.h"
-#include "error.h"
-#include "show.h"
-
-
-/*
- * Code to handle exceptions in C.
- */
-
-struct jmploc *handler;
-int exception;
-volatile int suppressint;
-volatile int intpending;
-char *commandname;
-
-
-static void exverror(int, const char *, va_list)
-    __attribute__((__noreturn__));
-
-/*
- * Called to raise an exception.  Since C doesn't include exceptions, we
- * just do a longjmp to the exception handler.  The type of exception is
- * stored in the global variable "exception".
- */
-
-void
-exraise(int e)
-{
-	if (handler == NULL)
-		abort();
-	exception = e;
-	longjmp(handler->loc, 1);
-}
-
-
-/*
- * Called from trap.c when a SIGINT is received.  (If the user specifies
- * that SIGINT is to be trapped or ignored using the trap builtin, then
- * this routine is not called.)  Suppressint is nonzero when interrupts
- * are held using the INTOFF macro.  The call to _exit is necessary because
- * there is a short period after a fork before the signal handlers are
- * set to the appropriate value for the child.  (The test for iflag is
- * just defensive programming.)
- */
-
-void
-onint(void)
-{
-	sigset_t nsigset;
-
-	if (suppressint) {
-		intpending = 1;
-		return;
-	}
-	intpending = 0;
-	sigemptyset(&nsigset);
-	sigprocmask(SIG_SETMASK, &nsigset, NULL);
-	if (rootshell && iflag)
-		exraise(EXINT);
-	else {
-		bsd_signal(SIGINT, SIG_DFL);
-		raise(SIGINT);
-	}
-	/* NOTREACHED */
-}
-
-static void
-exvwarning(int sv_errno, const char *msg, va_list ap)
-{
-	/* Partially emulate line buffered output so that:
-	 *	printf '%d\n' 1 a 2
-	 * and
-	 *	printf '%d %d %d\n' 1 a 2
-	 * both generate sensible text when stdout and stderr are merged.
-	 */
-	if (output.nextc != output.buf && output.nextc[-1] == '\n')
-		flushout(&output);
-	if (commandname)
-		outfmt(&errout, "%s: ", commandname);
-	if (msg != NULL) {
-		doformat(&errout, msg, ap);
-		if (sv_errno >= 0)
-			outfmt(&errout, ": ");
-	}
-	if (sv_errno >= 0)
-		outfmt(&errout, "%s", strerror(sv_errno));
-	out2c('\n');
-	flushout(&errout);
-}
-
-/*
- * Exverror is called to raise the error exception.  If the second argument
- * is not NULL then error prints an error message using printf style
- * formatting.  It then raises the error exception.
- */
-static void
-exverror(int cond, const char *msg, va_list ap)
-{
-	CLEAR_PENDING_INT;
-	INTOFF;
-
-#ifdef DEBUG
-	if (msg) {
-		va_list ap_tmp;
-		va_copy(ap_tmp, ap);
-		TRACE(("exverror(%d, \"", cond));
-		TRACEV((msg, ap_tmp));
-		TRACE(("\") pid=%d\n", getpid()));
-		va_end(ap_tmp);
-	} else
-		TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
-#endif
-	if (msg)
-		exvwarning(-1, msg, ap);
-
-	flushall();
-	exraise(cond);
-	/* NOTREACHED */
-}
-
-
-void
-error(const char *msg, ...)
-{
-	va_list ap;
-
-	va_start(ap, msg);
-	exverror(EXERROR, msg, ap);
-	/* NOTREACHED */
-	va_end(ap);
-}
-
-
-void
-exerror(int cond, const char *msg, ...)
-{
-	va_list ap;
-
-	va_start(ap, msg);
-	exverror(cond, msg, ap);
-	/* NOTREACHED */
-	va_end(ap);
-}
-
-/*
- * error/warning routines for external builtins
- */
-
-void
-sh_exit(int rval)
-{
-	exerrno = rval & 255;
-	exraise(EXEXEC);
-}
-
-void
-sh_err(int status, const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	exvwarning(errno, fmt, ap);
-	va_end(ap);
-	sh_exit(status);
-}
-
-void
-sh_verr(int status, const char *fmt, va_list ap)
-{
-	exvwarning(errno, fmt, ap);
-	sh_exit(status);
-}
-
-void
-sh_errx(int status, const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	exvwarning(-1, fmt, ap);
-	va_end(ap);
-	sh_exit(status);
-}
-
-void
-sh_verrx(int status, const char *fmt, va_list ap)
-{
-	exvwarning(-1, fmt, ap);
-	sh_exit(status);
-}
-
-void
-sh_warn(const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	exvwarning(errno, fmt, ap);
-	va_end(ap);
-}
-
-void
-sh_vwarn(const char *fmt, va_list ap)
-{
-	exvwarning(errno, fmt, ap);
-}
-
-void
-sh_warnx(const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	exvwarning(-1, fmt, ap);
-	va_end(ap);
-}
-
-void
-sh_vwarnx(const char *fmt, va_list ap)
-{
-	exvwarning(-1, fmt, ap);
-}
-
-
-/*
- * Table of error messages.
- */
-
-struct errname {
-	short errcode;		/* error number */
-	short action;		/* operation which encountered the error */
-	const char *msg;	/* text describing the error */
-};
-
-
-#define ALL (E_OPEN|E_CREAT|E_EXEC)
-
-STATIC const struct errname errormsg[] = {
-	{ EINTR,	ALL,	"interrupted" },
-	{ EACCES,	ALL,	"permission denied" },
-	{ EIO,		ALL,	"I/O error" },
-	{ EEXIST,	ALL,	"file exists" },
-	{ ENOENT,	E_OPEN,	"no such file" },
-	{ ENOENT,	E_CREAT,"directory nonexistent" },
-	{ ENOENT,	E_EXEC,	"not found" },
-	{ ENOTDIR,	E_OPEN,	"no such file" },
-	{ ENOTDIR,	E_CREAT,"directory nonexistent" },
-	{ ENOTDIR,	E_EXEC,	"not found" },
-	{ EISDIR,	ALL,	"is a directory" },
-#ifdef EMFILE
-	{ EMFILE,	ALL,	"too many open files" },
-#endif
-	{ ENFILE,	ALL,	"file table overflow" },
-	{ ENOSPC,	ALL,	"file system full" },
-#ifdef EDQUOT
-	{ EDQUOT,	ALL,	"disk quota exceeded" },
-#endif
-#ifdef ENOSR
-	{ ENOSR,	ALL,	"no streams resources" },
-#endif
-	{ ENXIO,	ALL,	"no such device or address" },
-	{ EROFS,	ALL,	"read-only file system" },
-	{ ETXTBSY,	ALL,	"text busy" },
-#ifdef EAGAIN
-	{ EAGAIN,	E_EXEC,	"not enough memory" },
-#endif
-	{ ENOMEM,	ALL,	"not enough memory" },
-#ifdef ENOLINK
-	{ ENOLINK,	ALL,	"remote access failed" },
-#endif
-#ifdef EMULTIHOP
-	{ EMULTIHOP,	ALL,	"remote access failed" },
-#endif
-#ifdef ECOMM
-	{ ECOMM,	ALL,	"remote access failed" },
-#endif
-#ifdef ESTALE
-	{ ESTALE,	ALL,	"remote access failed" },
-#endif
-#ifdef ETIMEDOUT
-	{ ETIMEDOUT,	ALL,	"remote access failed" },
-#endif
-#ifdef ELOOP
-	{ ELOOP,	ALL,	"symbolic link loop" },
-#endif
-	{ E2BIG,	E_EXEC,	"argument list too long" },
-#ifdef ELIBACC
-	{ ELIBACC,	E_EXEC,	"shared library missing" },
-#endif
-	{ 0,		0,	NULL },
-};
-
-
-/*
- * Return a string describing an error.  The returned string may be a
- * pointer to a static buffer that will be overwritten on the next call.
- * Action describes the operation that got the error.
- */
-
-const char *
-errmsg(int e, int action)
-{
-	struct errname const *ep;
-	static char buf[12];
-
-	for (ep = errormsg ; ep->errcode ; ep++) {
-		if (ep->errcode == e && (ep->action & action) != 0)
-			return ep->msg;
-	}
-	fmtstr(buf, sizeof buf, "error %d", e);
-	return buf;
-}
diff --git a/ash/eval.c b/ash/eval.c
deleted file mode 100644
index b7ad2eb..0000000
--- a/ash/eval.c
+++ /dev/null
@@ -1,1279 +0,0 @@
-/*	$NetBSD: eval.c,v 1.76 2004/04/30 06:27:59 dsl Exp $	*/
-
-/*-
- * Copyright (c) 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
-#else
-__RCSID("$NetBSD: eval.c,v 1.76 2004/04/30 06:27:59 dsl Exp $");
-#endif
-#endif /* not lint */
-
-#include <stdlib.h>
-#include <signal.h>
-#include <stdio.h>
-#include <unistd.h>
-#ifdef __linux__
-#include <fcntl.h>
-#else
-#include <sys/fcntl.h>
-#endif
-#include <sys/times.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#ifndef __KLIBC__
-#include <sys/sysctl.h>
-#endif
-
-/*
- * Evaluate a command.
- */
-
-#include "shell.h"
-#include "nodes.h"
-#include "syntax.h"
-#include "expand.h"
-#include "parser.h"
-#include "jobs.h"
-#include "eval.h"
-#include "builtins.h"
-#include "options.h"
-#include "exec.h"
-#include "redir.h"
-#include "input.h"
-#include "output.h"
-#include "trap.h"
-#include "var.h"
-#include "memalloc.h"
-#include "error.h"
-#include "show.h"
-#include "mystring.h"
-#include "main.h"
-#ifdef KLIBC_SH_HISTORY
-#include "myhistedit.h"
-#endif
-
-#ifdef __linux__
-#include "compat_linux.h"
-#endif
-
-/* flags in argument to evaltree */
-#define EV_EXIT 01		/* exit after evaluating tree */
-#define EV_TESTED 02		/* exit status is checked; ignore -e flag */
-#define EV_BACKCMD 04		/* command executing within back quotes */
-
-int evalskip;			/* set if we are skipping commands */
-STATIC int skipcount;		/* number of levels to skip */
-MKINIT int loopnest;		/* current loop nesting level */
-int funcnest;			/* depth of function calls */
-
-
-char *commandname;
-struct strlist *cmdenviron;
-int exitstatus;			/* exit status of last command */
-int back_exitstatus;		/* exit status of backquoted command */
-
-
-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 void expredir(union node *);
-STATIC void evalpipe(union node *);
-STATIC void evalcommand(union node *, int, struct backcmd *);
-STATIC void prehash(union node *);
-
-
-/*
- * Called to reset things after an exception.
- */
-
-#ifdef mkinit
-INCLUDE "eval.h"
-
-RESET {
-	evalskip = 0;
-	loopnest = 0;
-	funcnest = 0;
-}
-
-SHELLPROC {
-	exitstatus = 0;
-}
-#endif
-
-static int
-sh_pipe(int fds[2])
-{
-	int nfd;
-
-	if (pipe(fds))
-		return -1;
-
-	if (fds[0] < 3) {
-		nfd = fcntl(fds[0], F_DUPFD, 3);
-		if (nfd != -1) {
-			close(fds[0]);
-			fds[0] = nfd;
-		}
-	}
-
-	if (fds[1] < 3) {
-		nfd = fcntl(fds[1], F_DUPFD, 3);
-		if (nfd != -1) {
-			close(fds[1]);
-			fds[1] = nfd;
-		}
-	}
-	return 0;
-}
-
-
-/*
- * The eval commmand.
- */
-
-int
-evalcmd(int argc, char **argv)
-{
-        char *p;
-        char *concat;
-        char **ap;
-
-        if (argc > 1) {
-                p = argv[1];
-                if (argc > 2) {
-                        STARTSTACKSTR(concat);
-                        ap = argv + 2;
-                        for (;;) {
-                                while (*p)
-                                        STPUTC(*p++, concat);
-                                if ((p = *ap++) == NULL)
-                                        break;
-                                STPUTC(' ', concat);
-                        }
-                        STPUTC('\0', concat);
-                        p = grabstackstr(concat);
-                }
-                evalstring(p, EV_TESTED);
-        }
-        return exitstatus;
-}
-
-
-/*
- * Execute a command or commands contained in a string.
- */
-
-void
-evalstring(char *s, int flag)
-{
-	union node *n;
-	struct stackmark smark;
-
-	setstackmark(&smark);
-	setinputstring(s, 1);
-
-	while ((n = parsecmd(0)) != NEOF) {
-		evaltree(n, flag);
-		popstackmark(&smark);
-	}
-	popfile();
-	popstackmark(&smark);
-}
-
-
-
-/*
- * Evaluate a parse tree.  The value is left in the global variable
- * exitstatus.
- */
-
-void
-evaltree(union node *n, int flags)
-{
-	if (n == NULL) {
-		TRACE(("evaltree(NULL) called\n"));
-		exitstatus = 0;
-		goto out;
-	}
-#ifdef KLIBC_SH_HISTORY
-	displayhist = 1;	/* show history substitutions done with fc */
-#endif
-	TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
-	    getpid(), n, n->type, flags));
-	switch (n->type) {
-	case NSEMI:
-		evaltree(n->nbinary.ch1, flags & EV_TESTED);
-		if (evalskip)
-			goto out;
-		evaltree(n->nbinary.ch2, flags);
-		break;
-	case NAND:
-		evaltree(n->nbinary.ch1, EV_TESTED);
-		if (evalskip || exitstatus != 0)
-			goto out;
-		evaltree(n->nbinary.ch2, flags | EV_TESTED);
-		break;
-	case NOR:
-		evaltree(n->nbinary.ch1, EV_TESTED);
-		if (evalskip || exitstatus == 0)
-			goto out;
-		evaltree(n->nbinary.ch2, flags | EV_TESTED);
-		break;
-	case NREDIR:
-		expredir(n->nredir.redirect);
-		redirect(n->nredir.redirect, REDIR_PUSH);
-		evaltree(n->nredir.n, flags);
-		popredir();
-		break;
-	case NSUBSHELL:
-		evalsubshell(n, flags);
-		break;
-	case NBACKGND:
-		evalsubshell(n, flags);
-		break;
-	case NIF: {
-		evaltree(n->nif.test, EV_TESTED);
-		if (evalskip)
-			goto out;
-		if (exitstatus == 0)
-			evaltree(n->nif.ifpart, flags);
-		else if (n->nif.elsepart)
-			evaltree(n->nif.elsepart, flags);
-		else
-			exitstatus = 0;
-		break;
-	}
-	case NWHILE:
-	case NUNTIL:
-		evalloop(n, flags);
-		break;
-	case NFOR:
-		evalfor(n, flags);
-		break;
-	case NCASE:
-		evalcase(n, flags);
-		break;
-	case NDEFUN:
-		defun(n->narg.text, n->narg.next);
-		exitstatus = 0;
-		break;
-	case NNOT:
-		evaltree(n->nnot.com, EV_TESTED);
-		exitstatus = !exitstatus;
-		break;
-	case NPIPE:
-		evalpipe(n);
-		break;
-	case NCMD:
-		evalcommand(n, flags, (struct backcmd *)NULL);
-		break;
-	default:
-		out1fmt("Node type = %d\n", n->type);
-		flushout(&output);
-		break;
-	}
-out:
-	if (pendingsigs)
-		dotrap();
-	if ((flags & EV_EXIT) != 0)
-		exitshell(exitstatus);
-}
-
-
-STATIC void
-evalloop(union node *n, int flags)
-{
-	int status;
-
-	loopnest++;
-	status = 0;
-	for (;;) {
-		evaltree(n->nbinary.ch1, EV_TESTED);
-		if (evalskip) {
-skipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
-				evalskip = 0;
-				continue;
-			}
-			if (evalskip == SKIPBREAK && --skipcount <= 0)
-				evalskip = 0;
-			break;
-		}
-		if (n->type == NWHILE) {
-			if (exitstatus != 0)
-				break;
-		} else {
-			if (exitstatus == 0)
-				break;
-		}
-		evaltree(n->nbinary.ch2, flags & EV_TESTED);
-		status = exitstatus;
-		if (evalskip)
-			goto skipping;
-	}
-	loopnest--;
-	exitstatus = status;
-}
-
-
-
-STATIC void
-evalfor(union node *n, int flags)
-{
-	struct arglist arglist;
-	union node *argp;
-	struct strlist *sp;
-	struct stackmark smark;
-	int status = 0;
-
-	setstackmark(&smark);
-	arglist.lastp = &arglist.list;
-	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
-		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
-		if (evalskip)
-			goto out;
-	}
-	*arglist.lastp = NULL;
-
-	loopnest++;
-	for (sp = arglist.list ; sp ; sp = sp->next) {
-		setvar(n->nfor.var, sp->text, 0);
-		evaltree(n->nfor.body, flags & EV_TESTED);
-		status = exitstatus;
-		if (evalskip) {
-			if (evalskip == SKIPCONT && --skipcount <= 0) {
-				evalskip = 0;
-				continue;
-			}
-			if (evalskip == SKIPBREAK && --skipcount <= 0)
-				evalskip = 0;
-			break;
-		}
-	}
-	loopnest--;
-	exitstatus = status;
-out:
-	popstackmark(&smark);
-}
-
-
-
-STATIC void
-evalcase(union node *n, int flags)
-{
-	union node *cp;
-	union node *patp;
-	struct arglist arglist;
-	struct stackmark smark;
-	int status = 0;
-
-	setstackmark(&smark);
-	arglist.lastp = &arglist.list;
-	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
-	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);
-					status = exitstatus;
-				}
-				goto out;
-			}
-		}
-	}
-out:
-	exitstatus = status;
-	popstackmark(&smark);
-}
-
-
-
-/*
- * Kick off a subshell to evaluate a tree.
- */
-
-STATIC void
-evalsubshell(union node *n, int flags)
-{
-	struct job *jp;
-	int backgnd = (n->type == NBACKGND);
-
-	expredir(n->nredir.redirect);
-	INTOFF;
-	jp = makejob(n, 1);
-	if (forkshell(jp, n, backgnd) == 0) {
-		INTON;
-		if (backgnd)
-			flags &=~ EV_TESTED;
-		redirect(n->nredir.redirect, 0);
-		/* never returns */
-		evaltree(n->nredir.n, flags | EV_EXIT);
-	}
-	if (! backgnd)
-		exitstatus = waitforjob(jp);
-	INTON;
-}
-
-
-
-/*
- * Compute the names of the files in a redirection list.
- */
-
-STATIC void
-expredir(union node *n)
-{
-	union node *redir;
-
-	for (redir = n ; redir ; redir = redir->nfile.next) {
-		struct arglist fn;
-		fn.lastp = &fn.list;
-		switch (redir->type) {
-		case NFROMTO:
-		case NFROM:
-		case NTO:
-		case NCLOBBER:
-		case NAPPEND:
-			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
-			redir->nfile.expfname = fn.list->text;
-			break;
-		case NFROMFD:
-		case NTOFD:
-			if (redir->ndup.vname) {
-				expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
-				fixredir(redir, fn.list->text, 1);
-			}
-			break;
-		}
-	}
-}
-
-
-
-/*
- * Evaluate a pipeline.  All the processes in the pipeline are children
- * of the process creating the pipeline.  (This differs from some versions
- * of the shell, which make the last process in a pipeline the parent
- * of all the rest.)
- */
-
-STATIC void
-evalpipe(union node *n)
-{
-	struct job *jp;
-	struct nodelist *lp;
-	int pipelen;
-	int prevfd;
-	int pip[2];
-
-	TRACE(("evalpipe(0x%lx) called\n", (long)n));
-	pipelen = 0;
-	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
-		pipelen++;
-	INTOFF;
-	jp = makejob(n, pipelen);
-	prevfd = -1;
-	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
-		prehash(lp->n);
-		pip[1] = -1;
-		if (lp->next) {
-			if (sh_pipe(pip) < 0) {
-				close(prevfd);
-				error("Pipe call failed");
-			}
-		}
-		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
-			INTON;
-			if (prevfd > 0) {
-				close(0);
-				copyfd(prevfd, 0);
-				close(prevfd);
-			}
-			if (pip[1] >= 0) {
-				close(pip[0]);
-				if (pip[1] != 1) {
-					close(1);
-					copyfd(pip[1], 1);
-					close(pip[1]);
-				}
-			}
-			evaltree(lp->n, EV_EXIT);
-		}
-		if (prevfd >= 0)
-			close(prevfd);
-		prevfd = pip[0];
-		close(pip[1]);
-	}
-	if (n->npipe.backgnd == 0) {
-		exitstatus = waitforjob(jp);
-		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
-	}
-	INTON;
-}
-
-
-
-/*
- * 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.
- */
-
-void
-evalbackcmd(union node *n, struct backcmd *result)
-{
-	int pip[2];
-	struct job *jp;
-	struct stackmark smark;		/* unnecessary */
-
-	setstackmark(&smark);
-	result->fd = -1;
-	result->buf = NULL;
-	result->nleft = 0;
-	result->jp = NULL;
-	if (n == NULL) {
-		goto out;
-	}
-#ifdef notyet
-	/*
-	 * For now we disable executing builtins in the same
-	 * context as the shell, because we are not keeping
-	 * enough state to recover from changes that are
-	 * supposed only to affect subshells. eg. echo "`cd /`"
-	 */
-	if (n->type == NCMD) {
-		exitstatus = oexitstatus;
-		evalcommand(n, EV_BACKCMD, result);
-	} else
-#endif
-	{
-		INTOFF;
-		if (sh_pipe(pip) < 0)
-			error("Pipe call failed");
-		jp = makejob(n, 1);
-		if (forkshell(jp, n, FORK_NOJOB) == 0) {
-			FORCEINTON;
-			close(pip[0]);
-			if (pip[1] != 1) {
-				close(1);
-				copyfd(pip[1], 1);
-				close(pip[1]);
-			}
-			eflag = 0;
-			evaltree(n, EV_EXIT);
-			/* NOTREACHED */
-		}
-		close(pip[1]);
-		result->fd = pip[0];
-		result->jp = jp;
-		INTON;
-	}
-out:
-	popstackmark(&smark);
-	TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
-		result->fd, result->buf, result->nleft, result->jp));
-}
-
-static const char *
-syspath(void)
-{
-	static char *sys_path = NULL;
-#ifdef _HAVE_SYSCTL
-	static int mib[] = {CTL_USER, USER_CS_PATH};
-	size_t len;
-#endif
-
-	if (sys_path == NULL) {
-#ifdef _HAVE_SYSCTL
-		if (sysctl(mib, 2, 0, &len, 0, 0) != -1 &&
-		    (sys_path = ckmalloc(len + 5)) != NULL &&
-		    sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) {
-			memcpy(sys_path, "PATH=", 5);
-		} else {
-#endif
-			ckfree(sys_path);
-			/* something to keep things happy */
-			sys_path = "PATH=/usr/bin:/bin:/usr/sbin:/sbin";
-#ifdef _HAVE_SYSCTL
-		}
-#endif
-	}
-	return sys_path;
-}
-
-static int
-parse_command_args(int argc, char **argv, int *use_syspath)
-{
-	int sv_argc = argc;
-	char *cp, c;
-
-	*use_syspath = 0;
-
-	for (;;) {
-		argv++;
-		if (--argc == 0)
-			break;
-		cp = *argv;
-		if (*cp++ != '-')
-			break;
-		if (*cp == '-' && cp[1] == 0) {
-			argv++;
-			argc--;
-			break;
-		}
-		while ((c = *cp++)) {
-			switch (c) {
-			case 'p':
-				*use_syspath = 1;
-				break;
-			default:
-				/* run 'typecmd' for other options */
-				return 0;
-			}
-		}
-	}
-	return sv_argc - argc;
-}
-
-int vforked = 0;
-
-/*
- * Execute a simple command.
- */
-
-STATIC void
-evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
-{
-	struct stackmark smark;
-	union node *argp;
-	struct arglist arglist;
-	struct arglist varlist;
-	char **argv;
-	int argc;
-	char **envp;
-	int varflag;
-	struct strlist *sp;
-	int mode;
-	int pip[2];
-	struct cmdentry cmdentry;
-	struct job *jp;
-	struct jmploc jmploc;
-	struct jmploc *volatile savehandler;
-	char *volatile savecmdname;
-	volatile struct shparam saveparam;
-	struct localvar *volatile savelocalvars;
-	volatile int e;
-	char *lastarg;
-	const char *path = pathval();
-	int temp_path = 0;
-#if __GNUC__
-	/* Avoid longjmp clobbering */
-	(void) &argv;
-	(void) &argc;
-	(void) &lastarg;
-	(void) &flags;
-	(void) &mode;
-	(void) &path;
-	(void) &temp_path;
-#endif
-
-	vforked = 0;
-	/* First expand the arguments. */
-	TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
-	setstackmark(&smark);
-	back_exitstatus = 0;
-
-	arglist.lastp = &arglist.list;
-	varflag = 1;
-	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
-		char *p = argp->narg.text;
-		if (varflag && is_name(*p)) {
-			do {
-				p++;
-			} while (is_in_name(*p));
-			if (*p == '=')
-				continue;
-		}
-		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
-		varflag = 0;
-	}
-	*arglist.lastp = NULL;
-
-	expredir(cmd->ncmd.redirect);
-
-	varlist.lastp = &varlist.list;
-	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
-		char *p = argp->narg.text;
-		if (!is_name(*p))
-			break;
-		do
-			p++;
-		while (is_in_name(*p));
-		if (*p != '=')
-			break;
-		expandarg(argp, &varlist, EXP_VARTILDE);
-	}
-	*varlist.lastp = NULL;
-
-	argc = 0;
-	for (sp = arglist.list ; sp ; sp = sp->next)
-		argc++;
-	argv = stalloc(sizeof (char *) * (argc + 1));
-
-	for (sp = arglist.list ; sp ; sp = sp->next) {
-		TRACE(("evalcommand arg: %s\n", sp->text));
-		*argv++ = sp->text;
-	}
-	*argv = NULL;
-	lastarg = NULL;
-	if (iflag && funcnest == 0 && argc > 0)
-		lastarg = argv[-1];
-	argv -= argc;
-
-	/* Print the command if xflag is set. */
-	if (xflag) {
-		char sep = 0;
-		out2str(ps4val());
-		for (sp = varlist.list ; sp ; sp = sp->next) {
-			if (sep != 0)
-				outc(sep, &errout);
-			out2str(sp->text);
-			sep = ' ';
-		}
-		for (sp = arglist.list ; sp ; sp = sp->next) {
-			if (sep != 0)
-				outc(sep, &errout);
-			out2str(sp->text);
-			sep = ' ';
-		}
-		outc('\n', &errout);
-		flushout(&errout);
-	}
-
-	/* Now locate the command. */
-	if (argc == 0) {
-		cmdentry.cmdtype = CMDSPLBLTIN;
-		cmdentry.u.bltin = bltincmd;
-	} else {
-		static const char PATH[] = "PATH=";
-		int cmd_flags = DO_ERR;
-
-		/*
-		 * Modify the command lookup path, if a PATH= assignment
-		 * is present
-		 */
-		for (sp = varlist.list; sp; sp = sp->next)
-			if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
-				path = sp->text + sizeof(PATH) - 1;
-
-		do {
-			int argsused, use_syspath;
-			find_command(argv[0], &cmdentry, cmd_flags, path);
-			if (cmdentry.cmdtype == CMDUNKNOWN) {
-				exitstatus = 127;
-				flushout(&errout);
-				goto out;
-			}
-
-			/* implement the 'command' builtin here */
-			if (cmdentry.cmdtype != CMDBUILTIN ||
-			    cmdentry.u.bltin != bltincmd)
-				break;
-			cmd_flags |= DO_NOFUNC;
-			argsused = parse_command_args(argc, argv, &use_syspath);
-			if (argsused == 0) {
-				/* use 'type' builting to display info */
-				cmdentry.u.bltin = typecmd;
-				break;
-			}
-			argc -= argsused;
-			argv += argsused;
-			if (use_syspath)
-				path = syspath() + 5;
-		} while (argc != 0);
-		if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
-			/* posix mandates that 'command <splbltin>' act as if
-			   <splbltin> was a normal builtin */
-			cmdentry.cmdtype = CMDBUILTIN;
-	}
-
-	/* Fork off a child process if necessary. */
-	if (cmd->ncmd.backgnd
-	 || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
-	 || ((flags & EV_BACKCMD) != 0
-	    && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN)
-		 || cmdentry.u.bltin == dotcmd
-		 || cmdentry.u.bltin == evalcmd))) {
-		INTOFF;
-		jp = makejob(cmd, 1);
-		mode = cmd->ncmd.backgnd;
-		if (flags & EV_BACKCMD) {
-			mode = FORK_NOJOB;
-			if (sh_pipe(pip) < 0)
-				error("Pipe call failed");
-		}
-#ifdef DO_SHAREDVFORK
-		/* It is essential that if DO_SHAREDVFORK is defined that the
-		 * child's address space is actually shared with the parent as
-		 * we rely on this.
-		 */
-		if (cmdentry.cmdtype == CMDNORMAL) {
-			pid_t	pid;
-
-			savelocalvars = localvars;
-			localvars = NULL;
-			vforked = 1;
-			switch (pid = vfork()) {
-			case -1:
-				TRACE(("Vfork failed, errno=%d\n", errno));
-				INTON;
-				error("Cannot vfork");
-				break;
-			case 0:
-				/* Make sure that exceptions only unwind to
-				 * after the vfork(2)
-				 */
-				if (setjmp(jmploc.loc)) {
-					if (exception == EXSHELLPROC) {
-						/* We can't progress with the vfork,
-						 * so, set vforked = 2 so the parent
-						 * knows, and _exit();
-						 */
-						vforked = 2;
-						_exit(0);
-					} else {
-						_exit(exerrno);
-					}
-				}
-				savehandler = handler;
-				handler = &jmploc;
-				listmklocal(varlist.list, VEXPORT | VNOFUNC);
-				forkchild(jp, cmd, mode, vforked);
-				break;
-			default:
-				handler = savehandler;	/* restore from vfork(2) */
-				poplocalvars();
-				localvars = savelocalvars;
-				if (vforked == 2) {
-					vforked = 0;
-
-					(void)waitpid(pid, NULL, 0);
-					/* We need to progress in a normal fork fashion */
-					goto normal_fork;
-				}
-				vforked = 0;
-				forkparent(jp, cmd, mode, pid);
-				goto parent;
-			}
-		} else {
-normal_fork:
-#endif
-			if (forkshell(jp, cmd, mode) != 0)
-				goto parent;	/* at end of routine */
-			FORCEINTON;
-#ifdef DO_SHAREDVFORK
-		}
-#endif
-		if (flags & EV_BACKCMD) {
-			if (!vforked) {
-				FORCEINTON;
-			}
-			close(pip[0]);
-			if (pip[1] != 1) {
-				close(1);
-				copyfd(pip[1], 1);
-				close(pip[1]);
-			}
-		}
-		flags |= EV_EXIT;
-	}
-
-	/* This is the child process if a fork occurred. */
-	/* Execute the command. */
-	switch (cmdentry.cmdtype) {
-	case CMDFUNCTION:
-#ifdef DEBUG
-		trputs("Shell function:  ");  trargs(argv);
-#endif
-		redirect(cmd->ncmd.redirect, REDIR_PUSH);
-		saveparam = shellparam;
-		shellparam.malloc = 0;
-		shellparam.reset = 1;
-		shellparam.nparam = argc - 1;
-		shellparam.p = argv + 1;
-		shellparam.optnext = NULL;
-		INTOFF;
-		savelocalvars = localvars;
-		localvars = NULL;
-		INTON;
-		if (setjmp(jmploc.loc)) {
-			if (exception == EXSHELLPROC) {
-				freeparam((volatile struct shparam *)
-				    &saveparam);
-			} else {
-				freeparam(&shellparam);
-				shellparam = saveparam;
-			}
-			poplocalvars();
-			localvars = savelocalvars;
-			handler = savehandler;
-			longjmp(handler->loc, 1);
-		}
-		savehandler = handler;
-		handler = &jmploc;
-		listmklocal(varlist.list, 0);
-		/* stop shell blowing its stack */
-		if (++funcnest > 1000)
-			error("too many nested function calls");
-		evaltree(cmdentry.u.func, flags & EV_TESTED);
-		funcnest--;
-		INTOFF;
-		poplocalvars();
-		localvars = savelocalvars;
-		freeparam(&shellparam);
-		shellparam = saveparam;
-		handler = savehandler;
-		popredir();
-		INTON;
-		if (evalskip == SKIPFUNC) {
-			evalskip = 0;
-			skipcount = 0;
-		}
-		if (flags & EV_EXIT)
-			exitshell(exitstatus);
-		break;
-
-	case CMDBUILTIN:
-	case CMDSPLBLTIN:
-#ifdef DEBUG
-		trputs("builtin command:  ");  trargs(argv);
-#endif
-		mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH;
-		if (flags == EV_BACKCMD) {
-			memout.nleft = 0;
-			memout.nextc = memout.buf;
-			memout.bufsize = 64;
-			mode |= REDIR_BACKQ;
-		}
-		e = -1;
-		savehandler = handler;
-		handler = &jmploc;
-		if (!setjmp(jmploc.loc)) {
-			/* We need to ensure the command hash table isn't
-			 * corruped by temporary PATH assignments.
-			 * However we must ensure the 'local' command works!
-			 */
-			if (path != pathval() && (cmdentry.u.bltin == hashcmd ||
-			    cmdentry.u.bltin == typecmd)) {
-				savelocalvars = localvars;
-				localvars = 0;
-				mklocal(path - 5 /* PATH= */, 0);
-				temp_path = 1;
-			} else
-				temp_path = 0;
-			redirect(cmd->ncmd.redirect, mode);
-
-			savecmdname = commandname;
-			/* exec is a special builtin, but needs this list... */
-			cmdenviron = varlist.list;
-			/* we must check 'readonly' flag for all builtins */
-			listsetvar(varlist.list,
-				cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET);
-			commandname = argv[0];
-			/* initialize nextopt */
-			argptr = argv + 1;
-			optptr = NULL;
-			/* and getopt */
-#ifndef __linux__
-			optreset = 1;
-#endif
-			optind = 1;
-			exitstatus = cmdentry.u.bltin(argc, argv);
-		} else {
-			e = exception;
-			exitstatus = e == EXINT ? SIGINT + 128 :
-					e == EXEXEC ? exerrno : 2;
-		}
-		handler = savehandler;
-		flushall();
-		out1 = &output;
-		out2 = &errout;
-		freestdout();
-		if (temp_path) {
-			poplocalvars();
-			localvars = savelocalvars;
-		}
-		cmdenviron = NULL;
-		if (e != EXSHELLPROC) {
-			commandname = savecmdname;
-			if (flags & EV_EXIT)
-				exitshell(exitstatus);
-		}
-		if (e != -1) {
-			if ((e != EXERROR && e != EXEXEC)
-			    || cmdentry.cmdtype == CMDSPLBLTIN)
-				exraise(e);
-			FORCEINTON;
-		}
-		if (cmdentry.u.bltin != execcmd)
-			popredir();
-		if (flags == EV_BACKCMD) {
-			backcmd->buf = memout.buf;
-			backcmd->nleft = memout.nextc - memout.buf;
-			memout.buf = NULL;
-		}
-		break;
-
-	default:
-#ifdef DEBUG
-		trputs("normal command:  ");  trargs(argv);
-#endif
-		clearredir(vforked);
-		redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0);
-		if (!vforked)
-			for (sp = varlist.list ; sp ; sp = sp->next)
-				setvareq(sp->text, VEXPORT|VSTACK);
-		envp = environment();
-		shellexec(argv, envp, path, cmdentry.u.index, vforked);
-		break;
-	}
-	goto out;
-
-parent:	/* parent process gets here (if we forked) */
-	if (mode == FORK_FG) {	/* argument to fork */
-		exitstatus = waitforjob(jp);
-	} else if (mode == FORK_NOJOB) {
-		backcmd->fd = pip[0];
-		close(pip[1]);
-		backcmd->jp = jp;
-	}
-	FORCEINTON;
-
-out:
-	if (lastarg)
-		/* dsl: I think this is intended to be used to support
-		 * '_' in 'vi' command mode during line editing...
-		 * However I implemented that within libedit itself.
-		 */
-		setvar("_", lastarg, 0);
-	popstackmark(&smark);
-
-	if (eflag && exitstatus && !(flags & EV_TESTED))
-		exitshell(exitstatus);
-}
-
-
-/*
- * Search for a command.  This is called before we fork so that the
- * location of the command will be available in the parent as well as
- * the child.  The check for "goodname" is an overly conservative
- * check that the name will not be subject to expansion.
- */
-
-STATIC void
-prehash(union node *n)
-{
-	struct cmdentry entry;
-
-	if (n->type == NCMD && n->ncmd.args)
-		if (goodname(n->ncmd.args->narg.text))
-			find_command(n->ncmd.args->narg.text, &entry, 0,
-				     pathval());
-}
-
-
-
-/*
- * Builtin commands.  Builtin commands whose functions are closely
- * tied to evaluation are implemented here.
- */
-
-/*
- * No command given.
- */
-
-int
-bltincmd(int argc, char **argv)
-{
-	(void)argc; (void)argv;
-  
-	/*
-	 * Preserve exitstatus of a previous possible redirection
-	 * as POSIX mandates
-	 */
-	return back_exitstatus;
-}
-
-
-/*
- * Handle break and continue commands.  Break, continue, and return are
- * all handled by setting the evalskip flag.  The evaluation routines
- * above all check this flag, and if it is set they start skipping
- * commands rather than executing them.  The variable skipcount is
- * the number of loops to break/continue, or the number of function
- * levels to return.  (The latter is always 1.)  It should probably
- * be an error to break out of more loops than exist, but it isn't
- * in the standard shell so we don't make it one here.
- */
-
-int
-breakcmd(int argc, char **argv)
-{
-	int n = argc > 1 ? number(argv[1]) : 1;
-
-	if (n > loopnest)
-		n = loopnest;
-	if (n > 0) {
-		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
-		skipcount = n;
-	}
-	return 0;
-}
-
-
-/*
- * The return command.
- */
-
-int
-returncmd(int argc, char **argv)
-{
-	int ret = argc > 1 ? number(argv[1]) : exitstatus;
-
-	if (funcnest) {
-		evalskip = SKIPFUNC;
-		skipcount = 1;
-		return ret;
-	}
-	else {
-		/* Do what ksh does; skip the rest of the file */
-		evalskip = SKIPFILE;
-		skipcount = 1;
-		return ret;
-	}
-}
-
-
-int
-falsecmd(int argc, char **argv)
-{
-	(void)argc; (void)argv;
-	return 1;
-}
-
-
-int
-truecmd(int argc, char **argv)
-{
-	(void)argc; (void)argv;
-	return 0;
-}
-
-
-int
-execcmd(int argc, char **argv)
-{
-	if (argc > 1) {
-		struct strlist *sp;
-
-		iflag = 0;		/* exit on error */
-		mflag = 0;
-		optschanged();
-		for (sp = cmdenviron; sp; sp = sp->next)
-			setvareq(sp->text, VEXPORT|VSTACK);
-		shellexec(argv + 1, environment(), pathval(), 0, 0);
-	}
-	return 0;
-}
-
-static int
-conv_time(clock_t ticks, char *seconds, size_t l)
-{
-	static clock_t tpm = HZ;
-	clock_t mins;
-	int i;
-#ifndef __KLIBC__
-	if (!tpm)
-		tpm = sysconf(_SC_CLK_TCK) * 60;
-#endif
-
-	mins = ticks / tpm;
-	snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm );
-
-	if (seconds[0] == '6' && seconds[1] == '0') {
-		/* 59.99995 got rounded up... */
-		mins++;
-		strlcpy(seconds, "0.0", l);
-		return mins;
-	}
-
-	/* suppress trailing zeros */
-	i = strlen(seconds) - 1;
-	for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--)
-		seconds[i] = 0;
-	return mins;
-}
-
-int
-timescmd(int argc, char **argv)
-{
-	struct tms tms;
-	int u, s, cu, cs;
-	char us[8], ss[8], cus[8], css[8];
-
-	(void)argc; (void)argv;
-	
-	nextopt("");
-
-	times(&tms);
-
-	u = conv_time(tms.tms_utime, us, sizeof(us));
-	s = conv_time(tms.tms_stime, ss, sizeof(ss));
-	cu = conv_time(tms.tms_cutime, cus, sizeof(cus));
-	cs = conv_time(tms.tms_cstime, css, sizeof(css));
-
-	outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
-		u, us, s, ss, cu, cus, cs, css);
-
-	return 0;
-}
diff --git a/ash/exec.c b/ash/exec.c
deleted file mode 100644
index 95b272e..0000000
--- a/ash/exec.c
+++ /dev/null
@@ -1,1084 +0,0 @@
-/*	$NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)exec.c	8.4 (Berkeley) 6/8/95";
-#else
-__RCSID("$NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-/*
- * When commands are first encountered, they are entered in a hash table.
- * This ensures that a full path search will not have to be done for them
- * on each invocation.
- *
- * We should investigate converting to a linear search, even though that
- * would make the command name "hash" a misnomer.
- */
-
-#include "shell.h"
-#include "main.h"
-#include "nodes.h"
-#include "parser.h"
-#include "redir.h"
-#include "eval.h"
-#include "exec.h"
-#include "builtins.h"
-#include "var.h"
-#include "options.h"
-#include "input.h"
-#include "output.h"
-#include "syntax.h"
-#include "memalloc.h"
-#include "error.h"
-#include "init.h"
-#include "mystring.h"
-#include "show.h"
-#include "jobs.h"
-#include "alias.h"
-
-#ifdef __KLIBC__
-/* Values for the second argument to access.
-   These may be OR'd together.  */
-#define	R_OK	4		/* Test for read permission.  */
-#define	W_OK	2		/* Test for write permission.  */
-#define	X_OK	1		/* Test for execute permission.  */
-#define	F_OK	0		/* Test for existence.  */
-#endif
-
-#define CMDTABLESIZE 31		/* should be prime */
-#define ARB 1			/* actual size determined at run time */
-
-
-
-struct tblentry {
-	struct tblentry *next;	/* next entry in hash chain */
-	union param param;	/* definition of builtin function */
-	short cmdtype;		/* index identifying command */
-	char rehash;		/* if set, cd done since entry created */
-	char cmdname[ARB];	/* name of command */
-};
-
-
-STATIC struct tblentry *cmdtable[CMDTABLESIZE];
-STATIC int builtinloc = -1;		/* index in path of %builtin, or -1 */
-int exerrno = 0;			/* Last exec error */
-
-
-STATIC void tryexec(char *, char **, char **, int);
-STATIC void execinterp(char **, char **);
-STATIC void printentry(struct tblentry *, int);
-STATIC void clearcmdentry(int);
-STATIC struct tblentry *cmdlookup(const char *, int);
-STATIC void delete_cmd_entry(void);
-
-
-extern char *const parsekwd[];
-
-/*
- * Exec a program.  Never returns.  If you change this routine, you may
- * have to change the find_command routine as well.
- */
-
-void
-shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
-{
-	char *cmdname;
-	int e;
-
-	if (strchr(argv[0], '/') != NULL) {
-		tryexec(argv[0], argv, envp, vforked);
-		e = errno;
-	} else {
-		e = ENOENT;
-		while ((cmdname = padvance(&path, argv[0])) != NULL) {
-			if (--idx < 0 && pathopt == NULL) {
-				tryexec(cmdname, argv, envp, vforked);
-				if (errno != ENOENT && errno != ENOTDIR)
-					e = errno;
-			}
-			stunalloc(cmdname);
-		}
-	}
-
-	/* Map to POSIX errors */
-	switch (e) {
-	case EACCES:
-		exerrno = 126;
-		break;
-	case ENOENT:
-		exerrno = 127;
-		break;
-	default:
-		exerrno = 2;
-		break;
-	}
-	TRACE(("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n",
-		argv[0], e, vforked, suppressint ));
-	exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
-	/* NOTREACHED */
-}
-
-
-STATIC void
-tryexec(char *cmd, char **argv, char **envp, int vforked)
-{
-	int e;
-#ifndef BSD
-	char *p;
-#endif
-
-#ifdef SYSV
-	do {
-		execve(cmd, argv, envp);
-	} while (errno == EINTR);
-#else
-	execve(cmd, argv, envp);
-#endif
-	e = errno;
-	if (e == ENOEXEC) {
-		if (vforked) {
-			/* We are currently vfork(2)ed, so raise an
-			 * exception, and evalcommand will try again
-			 * with a normal fork(2).
-			 */
-			exraise(EXSHELLPROC);
-		}
-		initshellproc();
-		setinputfile(cmd, 0);
-		commandname = arg0 = savestr(argv[0]);
-#if !defined(BSD) && !defined(__linux__)
-		pgetc(); pungetc();		/* fill up input buffer */
-		p = parsenextc;
-		if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
-			argv[0] = cmd;
-			execinterp(argv, envp);
-		}
-#endif
-		setparam(argv + 1);
-		exraise(EXSHELLPROC);
-	}
-	errno = e;
-}
-
-
-#if !defined(BSD) && !defined(__linux__)
-/*
- * Execute an interpreter introduced by "#!", for systems where this
- * feature has not been built into the kernel.  If the interpreter is
- * the shell, return (effectively ignoring the "#!").  If the execution
- * of the interpreter fails, exit.
- *
- * This code peeks inside the input buffer in order to avoid actually
- * reading any input.  It would benefit from a rewrite.
- */
-
-#define NEWARGS 5
-
-STATIC void
-execinterp(char **argv, char **envp)
-{
-	int n;
-	char *inp;
-	char *outp;
-	char c;
-	char *p;
-	char **ap;
-	char *newargs[NEWARGS];
-	int i;
-	char **ap2;
-	char **new;
-
-	n = parsenleft - 2;
-	inp = parsenextc + 2;
-	ap = newargs;
-	for (;;) {
-		while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
-			inp++;
-		if (n < 0)
-			goto bad;
-		if ((c = *inp++) == '\n')
-			break;
-		if (ap == &newargs[NEWARGS])
-bad:		  error("Bad #! line");
-		STARTSTACKSTR(outp);
-		do {
-			STPUTC(c, outp);
-		} while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
-		STPUTC('\0', outp);
-		n++, inp--;
-		*ap++ = grabstackstr(outp);
-	}
-	if (ap == newargs + 1) {	/* if no args, maybe no exec is needed */
-		p = newargs[0];
-		for (;;) {
-			if (equal(p, "sh") || equal(p, "ash")) {
-				return;
-			}
-			while (*p != '/') {
-				if (*p == '\0')
-					goto break2;
-				p++;
-			}
-			p++;
-		}
-break2:;
-	}
-	i = (char *)ap - (char *)newargs;		/* size in bytes */
-	if (i == 0)
-		error("Bad #! line");
-	for (ap2 = argv ; *ap2++ != NULL ; );
-	new = ckmalloc(i + ((char *)ap2 - (char *)argv));
-	ap = newargs, ap2 = new;
-	while ((i -= sizeof (char **)) >= 0)
-		*ap2++ = *ap++;
-	ap = argv;
-	while (*ap2++ = *ap++);
-	shellexec(new, envp, pathval(), 0);
-	/* NOTREACHED */
-}
-#endif
-
-
-
-/*
- * Do a path search.  The variable path (passed by reference) should be
- * set to the start of the path before the first call; padvance will update
- * this value as it proceeds.  Successive calls to padvance will return
- * the possible path expansions in sequence.  If an option (indicated by
- * a percent sign) appears in the path entry then the global variable
- * pathopt will be set to point to it; otherwise pathopt will be set to
- * NULL.
- */
-
-const char *pathopt;
-
-char *
-padvance(const char **path, const char *name)
-{
-	const char *p;
-	char *q;
-	const char *start;
-	int len;
-
-	if (*path == NULL)
-		return NULL;
-	start = *path;
-	for (p = start ; *p && *p != ':' && *p != '%' ; p++);
-	len = p - start + strlen(name) + 2;	/* "2" is for '/' and '\0' */
-	while (stackblocksize() < len)
-		growstackblock();
-	q = stackblock();
-	if (p != start) {
-		memcpy(q, start, p - start);
-		q += p - start;
-		*q++ = '/';
-	}
-	strcpy(q, name);
-	pathopt = NULL;
-	if (*p == '%') {
-		pathopt = ++p;
-		while (*p && *p != ':')  p++;
-	}
-	if (*p == ':')
-		*path = p + 1;
-	else
-		*path = NULL;
-	return stalloc(len);
-}
-
-
-
-/*** Command hashing code ***/
-
-
-int
-hashcmd(int argc, char **argv)
-{
-	struct tblentry **pp;
-	struct tblentry *cmdp;
-	int c;
-	int verbose;
-	struct cmdentry entry;
-	char *name;
-
-	verbose = 0;
-	while ((c = nextopt("rv")) != '\0') {
-		if (c == 'r') {
-			clearcmdentry(0);
-		} else if (c == 'v') {
-			verbose++;
-		}
-	}
-	if (*argptr == NULL) {
-		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
-			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
-				if (verbose || cmdp->cmdtype == CMDNORMAL)
-					printentry(cmdp, verbose);
-			}
-		}
-		return 0;
-	}
-	while ((name = *argptr) != NULL) {
-		if ((cmdp = cmdlookup(name, 0)) != NULL
-		 && (cmdp->cmdtype == CMDNORMAL
-		     || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
-			delete_cmd_entry();
-		find_command(name, &entry, DO_ERR, pathval());
-		if (verbose) {
-			if (entry.cmdtype != CMDUNKNOWN) {	/* if no error msg */
-				cmdp = cmdlookup(name, 0);
-				printentry(cmdp, verbose);
-			}
-			flushall();
-		}
-		argptr++;
-	}
-	return 0;
-}
-
-
-STATIC void
-printentry(struct tblentry *cmdp, int verbose)
-{
-	int idx;
-	const char *path;
-	char *name;
-
-	switch (cmdp->cmdtype) {
-	case CMDNORMAL:
-		idx = cmdp->param.index;
-		path = pathval();
-		do {
-			name = padvance(&path, cmdp->cmdname);
-			stunalloc(name);
-		} while (--idx >= 0);
-		out1str(name);
-		break;
-	case CMDSPLBLTIN:
-		out1fmt("special builtin %s", cmdp->cmdname);
-		break;
-	case CMDBUILTIN:
-		out1fmt("builtin %s", cmdp->cmdname);
-		break;
-	case CMDFUNCTION:
-		out1fmt("function %s", cmdp->cmdname);
-		if (verbose) {
-			struct procstat ps;
-			INTOFF;
-			commandtext(&ps, cmdp->param.func);
-			INTON;
-			out1str("() { ");
-			out1str(ps.cmd);
-			out1str("; }");
-		}
-		break;
-	default:
-		error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype);
-	}
-	if (cmdp->rehash)
-		out1c('*');
-	out1c('\n');
-}
-
-
-
-/*
- * Resolve a command name.  If you change this routine, you may have to
- * change the shellexec routine as well.
- */
-
-void
-find_command(char *name, struct cmdentry *entry, int act, const char *path)
-{
-	struct tblentry *cmdp, loc_cmd;
-	int idx;
-	int prev;
-	char *fullname;
-	struct stat statb;
-	int e;
-	int (*bltin)(int,char **);
-
-	/* If name contains a slash, don't use PATH or hash table */
-	if (strchr(name, '/') != NULL) {
-		if (act & DO_ABS) {
-			while (stat(name, &statb) < 0) {
-#ifdef SYSV
-				if (errno == EINTR)
-					continue;
-#endif
-				if (errno != ENOENT && errno != ENOTDIR)
-					e = errno;
-				entry->cmdtype = CMDUNKNOWN;
-				entry->u.index = -1;
-				return;
-			}
-			entry->cmdtype = CMDNORMAL;
-			entry->u.index = -1;
-			return;
-		}
-		entry->cmdtype = CMDNORMAL;
-		entry->u.index = 0;
-		return;
-	}
-
-	if (path != pathval())
-		act |= DO_ALTPATH;
-
-	if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
-		act |= DO_ALTBLTIN;
-
-	/* If name is in the table, check answer will be ok */
-	if ((cmdp = cmdlookup(name, 0)) != NULL) {
-		do {
-			switch (cmdp->cmdtype) {
-			case CMDNORMAL:
-				if (act & DO_ALTPATH) {
-					cmdp = NULL;
-					continue;
-				}
-				break;
-			case CMDFUNCTION:
-				if (act & DO_NOFUNC) {
-					cmdp = NULL;
-					continue;
-				}
-				break;
-			case CMDBUILTIN:
-				if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
-					cmdp = NULL;
-					continue;
-				}
-				break;
-			}
-			/* if not invalidated by cd, we're done */
-			if (cmdp->rehash == 0)
-				goto success;
-		} while (0);
-	}
-
-	/* If %builtin not in path, check for builtin next */
-	if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
-	    (bltin = find_builtin(name)) != 0)
-		goto builtin_success;
-
-	/* We have to search path. */
-	prev = -1;		/* where to start */
-	if (cmdp) {		/* doing a rehash */
-		if (cmdp->cmdtype == CMDBUILTIN)
-			prev = builtinloc;
-		else
-			prev = cmdp->param.index;
-	}
-
-	e = ENOENT;
-	idx = -1;
-loop:
-	while ((fullname = padvance(&path, name)) != NULL) {
-		stunalloc(fullname);
-		idx++;
-		if (pathopt) {
-			if (prefix("builtin", pathopt)) {
-				if ((bltin = find_builtin(name)) == 0)
-					goto loop;
-				goto builtin_success;
-			} else if (prefix("func", pathopt)) {
-				/* handled below */
-			} else {
-				/* ignore unimplemented options */
-				goto loop;
-			}
-		}
-		/* if rehash, don't redo absolute path names */
-		if (fullname[0] == '/' && idx <= prev) {
-			if (idx < prev)
-				goto loop;
-			TRACE(("searchexec \"%s\": no change\n", name));
-			goto success;
-		}
-		while (stat(fullname, &statb) < 0) {
-#ifdef SYSV
-			if (errno == EINTR)
-				continue;
-#endif
-			if (errno != ENOENT && errno != ENOTDIR)
-				e = errno;
-			goto loop;
-		}
-		e = EACCES;	/* if we fail, this will be the error */
-		if (!S_ISREG(statb.st_mode))
-			goto loop;
-		if (pathopt) {		/* this is a %func directory */
-			if (act & DO_NOFUNC)
-				goto loop;
-			stalloc(strlen(fullname) + 1);
-			readcmdfile(fullname);
-			if ((cmdp = cmdlookup(name, 0)) == NULL ||
-			    cmdp->cmdtype != CMDFUNCTION)
-				error("%s not defined in %s", name, fullname);
-			stunalloc(fullname);
-			goto success;
-		}
-#ifdef notdef
-		/* XXX this code stops root executing stuff, and is buggy
-		   if you need a group from the group list. */
-		if (statb.st_uid == geteuid()) {
-			if ((statb.st_mode & 0100) == 0)
-				goto loop;
-		} else if (statb.st_gid == getegid()) {
-			if ((statb.st_mode & 010) == 0)
-				goto loop;
-		} else {
-			if ((statb.st_mode & 01) == 0)
-				goto loop;
-		}
-#endif
-		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
-		INTOFF;
-		if (act & DO_ALTPATH) {
-			stalloc(strlen(fullname) + 1);
-			cmdp = &loc_cmd;
-		} else
-			cmdp = cmdlookup(name, 1);
-		cmdp->cmdtype = CMDNORMAL;
-		cmdp->param.index = idx;
-		INTON;
-		goto success;
-	}
-
-	/* We failed.  If there was an entry for this command, delete it */
-	if (cmdp)
-		delete_cmd_entry();
-	if (act & DO_ERR)
-		outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
-	entry->cmdtype = CMDUNKNOWN;
-	return;
-
-builtin_success:
-	INTOFF;
-	if (act & DO_ALTPATH)
-		cmdp = &loc_cmd;
-	else
-		cmdp = cmdlookup(name, 1);
-	if (cmdp->cmdtype == CMDFUNCTION)
-		/* DO_NOFUNC must have been set */
-		cmdp = &loc_cmd;
-	cmdp->cmdtype = CMDBUILTIN;
-	cmdp->param.bltin = bltin;
-	INTON;
-success:
-	cmdp->rehash = 0;
-	entry->cmdtype = cmdp->cmdtype;
-	entry->u = cmdp->param;
-}
-
-
-
-/*
- * Search the table of builtin commands.
- */
-
-int
-(*find_builtin(name))(int, char **)
-	char *name;
-{
-	const struct builtincmd *bp;
-
-	for (bp = builtincmd ; bp->name ; bp++) {
-		if (*bp->name == *name && equal(bp->name, name))
-			return bp->builtin;
-	}
-	return 0;
-}
-
-int
-(*find_splbltin(name))(int, char **)
-	char *name;
-{
-	const struct builtincmd *bp;
-
-	for (bp = splbltincmd ; bp->name ; bp++) {
-		if (*bp->name == *name && equal(bp->name, name))
-			return bp->builtin;
-	}
-	return 0;
-}
-
-/*
- * At shell startup put special builtins into hash table.
- * ensures they are executed first (see posix).
- * We stop functions being added with the same name
- * (as they are impossible to call)
- */
-
-void
-hash_special_builtins(void)
-{
-	const struct builtincmd *bp;
-	struct tblentry *cmdp;
-
-	for (bp = splbltincmd ; bp->name ; bp++) {
-		cmdp = cmdlookup(bp->name, 1);
-		cmdp->cmdtype = CMDSPLBLTIN;
-		cmdp->param.bltin = bp->builtin;
-	}
-}
-
-
-
-/*
- * Called when a cd is done.  Marks all commands so the next time they
- * are executed they will be rehashed.
- */
-
-void
-hashcd(void)
-{
-	struct tblentry **pp;
-	struct tblentry *cmdp;
-
-	for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
-		for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
-			if (cmdp->cmdtype == CMDNORMAL
-			 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
-				cmdp->rehash = 1;
-		}
-	}
-}
-
-
-
-/*
- * Fix command hash table when PATH changed.
- * Called before PATH is changed.  The argument is the new value of PATH;
- * pathval() still returns the old value at this point.
- * Called with interrupts off.
- */
-
-void
-changepath(const char *newval)
-{
-	const char *old, *new;
-	int idx;
-	int firstchange;
-	int bltin;
-
-	old = pathval();
-	new = newval;
-	firstchange = 9999;	/* assume no change */
-	idx = 0;
-	bltin = -1;
-	for (;;) {
-		if (*old != *new) {
-			firstchange = idx;
-			if ((*old == '\0' && *new == ':')
-			 || (*old == ':' && *new == '\0'))
-				firstchange++;
-			old = new;	/* ignore subsequent differences */
-		}
-		if (*new == '\0')
-			break;
-		if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
-			bltin = idx;
-		if (*new == ':') {
-			idx++;
-		}
-		new++, old++;
-	}
-	if (builtinloc < 0 && bltin >= 0)
-		builtinloc = bltin;		/* zap builtins */
-	if (builtinloc >= 0 && bltin < 0)
-		firstchange = 0;
-	clearcmdentry(firstchange);
-	builtinloc = bltin;
-}
-
-
-/*
- * Clear out command entries.  The argument specifies the first entry in
- * PATH which has changed.
- */
-
-STATIC void
-clearcmdentry(int firstchange)
-{
-	struct tblentry **tblp;
-	struct tblentry **pp;
-	struct tblentry *cmdp;
-
-	INTOFF;
-	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
-		pp = tblp;
-		while ((cmdp = *pp) != NULL) {
-			if ((cmdp->cmdtype == CMDNORMAL &&
-			     cmdp->param.index >= firstchange)
-			 || (cmdp->cmdtype == CMDBUILTIN &&
-			     builtinloc >= firstchange)) {
-				*pp = cmdp->next;
-				ckfree(cmdp);
-			} else {
-				pp = &cmdp->next;
-			}
-		}
-	}
-	INTON;
-}
-
-
-/*
- * Delete all functions.
- */
-
-#ifdef mkinit
-MKINIT void deletefuncs(void);
-MKINIT void hash_special_builtins(void);
-
-INIT {
-	hash_special_builtins();
-}
-
-SHELLPROC {
-	deletefuncs();
-}
-#endif
-
-void
-deletefuncs(void)
-{
-	struct tblentry **tblp;
-	struct tblentry **pp;
-	struct tblentry *cmdp;
-
-	INTOFF;
-	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
-		pp = tblp;
-		while ((cmdp = *pp) != NULL) {
-			if (cmdp->cmdtype == CMDFUNCTION) {
-				*pp = cmdp->next;
-				freefunc(cmdp->param.func);
-				ckfree(cmdp);
-			} else {
-				pp = &cmdp->next;
-			}
-		}
-	}
-	INTON;
-}
-
-
-
-/*
- * Locate a command in the command hash table.  If "add" is nonzero,
- * add the command to the table if it is not already present.  The
- * variable "lastcmdentry" is set to point to the address of the link
- * pointing to the entry, so that delete_cmd_entry can delete the
- * entry.
- */
-
-struct tblentry **lastcmdentry;
-
-
-STATIC struct tblentry *
-cmdlookup(const char *name, int add)
-{
-	int hashval;
-	const char *p;
-	struct tblentry *cmdp;
-	struct tblentry **pp;
-
-	p = name;
-	hashval = *p << 4;
-	while (*p)
-		hashval += *p++;
-	hashval &= 0x7FFF;
-	pp = &cmdtable[hashval % CMDTABLESIZE];
-	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
-		if (equal(cmdp->cmdname, name))
-			break;
-		pp = &cmdp->next;
-	}
-	if (add && cmdp == NULL) {
-		INTOFF;
-		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
-					+ strlen(name) + 1);
-		cmdp->next = NULL;
-		cmdp->cmdtype = CMDUNKNOWN;
-		cmdp->rehash = 0;
-		strcpy(cmdp->cmdname, name);
-		INTON;
-	}
-	lastcmdentry = pp;
-	return cmdp;
-}
-
-/*
- * Delete the command entry returned on the last lookup.
- */
-
-STATIC void
-delete_cmd_entry(void)
-{
-	struct tblentry *cmdp;
-
-	INTOFF;
-	cmdp = *lastcmdentry;
-	*lastcmdentry = cmdp->next;
-	ckfree(cmdp);
-	INTON;
-}
-
-
-
-#ifdef notdef
-void
-getcmdentry(char *name, struct cmdentry *entry)
-{
-	struct tblentry *cmdp = cmdlookup(name, 0);
-
-	if (cmdp) {
-		entry->u = cmdp->param;
-		entry->cmdtype = cmdp->cmdtype;
-	} else {
-		entry->cmdtype = CMDUNKNOWN;
-		entry->u.index = 0;
-	}
-}
-#endif
-
-
-/*
- * Add a new command entry, replacing any existing command entry for
- * the same name - except special builtins.
- */
-
-STATIC void
-addcmdentry(char *name, struct cmdentry *entry)
-{
-	struct tblentry *cmdp;
-
-	INTOFF;
-	cmdp = cmdlookup(name, 1);
-	if (cmdp->cmdtype != CMDSPLBLTIN) {
-		if (cmdp->cmdtype == CMDFUNCTION) {
-			freefunc(cmdp->param.func);
-		}
-		cmdp->cmdtype = entry->cmdtype;
-		cmdp->param = entry->u;
-	}
-	INTON;
-}
-
-
-/*
- * Define a shell function.
- */
-
-void
-defun(char *name, union node *func)
-{
-	struct cmdentry entry;
-
-	INTOFF;
-	entry.cmdtype = CMDFUNCTION;
-	entry.u.func = copyfunc(func);
-	addcmdentry(name, &entry);
-	INTON;
-}
-
-
-/*
- * Delete a function if it exists.
- */
-
-int
-unsetfunc(char *name)
-{
-	struct tblentry *cmdp;
-
-	if ((cmdp = cmdlookup(name, 0)) != NULL &&
-	    cmdp->cmdtype == CMDFUNCTION) {
-		freefunc(cmdp->param.func);
-		delete_cmd_entry();
-		return (0);
-	}
-	return (1);
-}
-
-/*
- * Locate and print what a word is...
- * also used for 'command -[v|V]'
- */
-
-int
-typecmd(int argc, char **argv)
-{
-	struct cmdentry entry;
-	struct tblentry *cmdp;
-	char * const *pp;
-	int err = 0;
-	char *arg;
-	int c;
-	int V_flag = 0;
-	int v_flag = 0;
-	int p_flag = 0;
-#ifdef KLIBC_SH_ALIAS
-	struct alias *ap;
-#endif
-
-	(void)argc; (void)argv;
-
-	while ((c = nextopt("vVp")) != 0) {
-		switch (c) {
-		case 'v': v_flag = 1; break;
-		case 'V': V_flag = 1; break;
-		case 'p': p_flag = 1; break;
-		}
-	}
-
-	if (p_flag && (v_flag || V_flag))
-		error("cannot specify -p with -v or -V");
-
-	while ((arg = *argptr++)) {
-		if (!v_flag)
-			out1str(arg);
-		/* First look at the keywords */
-		for (pp = parsekwd; *pp; pp++)
-			if (**pp == *arg && equal(*pp, arg))
-				break;
-
-		if (*pp) {
-			if (v_flag)
-				err = 1;
-			else
-				out1str(" is a shell keyword\n");
-			continue;
-		}
-
-		/* Then look at the aliases */
-#ifdef KLIBC_SH_ALIAS
-		if ((ap = lookupalias(arg, 1)) != NULL) {
-			if (!v_flag)
-				out1fmt(" is an alias for \n");
-			out1fmt("%s\n", ap->val);
-			continue;
-		}
-#endif
-
-		/* Then check if it is a tracked alias */
-		if ((cmdp = cmdlookup(arg, 0)) != NULL) {
-			entry.cmdtype = cmdp->cmdtype;
-			entry.u = cmdp->param;
-		} else {
-			/* Finally use brute force */
-			find_command(arg, &entry, DO_ABS, pathval());
-		}
-
-		switch (entry.cmdtype) {
-		case CMDNORMAL: {
-			if (strchr(arg, '/') == NULL) {
-				const char *path = pathval();
-				char *name;
-				int j = entry.u.index;
-				do {
-					name = padvance(&path, arg);
-					stunalloc(name);
-				} while (--j >= 0);
-#ifdef KLIBC_SH_ALIAS
-				if (!v_flag)
-					out1fmt(" is%s ",
-					    cmdp ? " a tracked alias for" : "");
-#endif
-				out1fmt("%s\n", name);
-			} else {
-				if (access(arg, X_OK) == 0) {
-					if (!v_flag)
-						out1fmt(" is ");
-					out1fmt("%s\n", arg);
-				} else {
-					if (!v_flag)
-						out1fmt(": %s\n",
-						    strerror(errno));
-					else
-						err = 126;
-				}
-			}
- 			break;
-		}
-		case CMDFUNCTION:
-			if (!v_flag)
-				out1str(" is a shell function\n");
-			else
-				out1fmt("%s\n", arg);
-			break;
-
-		case CMDBUILTIN:
-			if (!v_flag)
-				out1str(" is a shell builtin\n");
-			else
-				out1fmt("%s\n", arg);
-			break;
-
-		case CMDSPLBLTIN:
-			if (!v_flag)
-				out1str(" is a special shell builtin\n");
-			else
-				out1fmt("%s\n", arg);
-			break;
-
-		default:
-			if (!v_flag)
-				out1str(": not found\n");
-			err = 127;
-			break;
-		}
-	}
-	return err;
-}
diff --git a/ash/expand.c b/ash/expand.c
deleted file mode 100644
index 7431b34..0000000
--- a/ash/expand.c
+++ /dev/null
@@ -1,1519 +0,0 @@
-/*	$NetBSD: expand.c,v 1.60 2003/12/21 08:32:39 jdolecek Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
-#else
-__RCSID("$NetBSD: expand.c,v 1.60 2003/12/21 08:32:39 jdolecek Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <dirent.h>
-#include <unistd.h>
-#ifdef _HAVE_PWD_H
-#include <pwd.h>
-#endif
-#include <stdlib.h>
-#include <stdio.h>
-
-/*
- * Routines to expand arguments to commands.  We have to deal with
- * backquotes, shell variables, and file metacharacters.
- */
-
-#include "shell.h"
-#include "main.h"
-#include "nodes.h"
-#include "eval.h"
-#include "expand.h"
-#include "syntax.h"
-#include "parser.h"
-#include "jobs.h"
-#include "options.h"
-#include "var.h"
-#include "input.h"
-#include "output.h"
-#include "memalloc.h"
-#include "error.h"
-#include "mystring.h"
-#include "show.h"
-
-/*
- * Structure specifying which parts of the string should be searched
- * for IFS characters.
- */
-
-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 */
-};
-
-
-char *expdest;			/* output of current string */
-struct nodelist *argbackq;	/* list of back quote expressions */
-struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
-struct ifsregion *ifslastp;	/* last struct in list */
-struct arglist exparg;		/* holds expanded arg list */
-
-STATIC void argstr(char *, int);
-STATIC char *exptilde(char *, int);
-STATIC void expbackq(union node *, int, int);
-STATIC int subevalvar(char *, char *, int, int, int, int);
-STATIC char *evalvar(char *, int);
-STATIC int varisset(char *, int);
-STATIC void varvalue(char *, int, int, int);
-STATIC void recordregion(int, int, int);
-STATIC void removerecordregions(int); 
-STATIC void ifsbreakup(char *, struct arglist *);
-STATIC void ifsfree(void);
-STATIC void expandmeta(struct strlist *, int);
-STATIC void expmeta(char *, char *);
-STATIC void addfname(char *);
-STATIC struct strlist *expsort(struct strlist *);
-STATIC struct strlist *msort(struct strlist *, int);
-STATIC int pmatch(char *, char *, int);
-STATIC char *cvtnum(int, char *);
-
-/*
- * Expand shell variables and backquotes inside a here document.
- */
-
-void
-expandhere(union node *arg, int fd)
-{
-	herefd = fd;
-	expandarg(arg, (struct arglist *)NULL, 0);
-	xwrite(fd, stackblock(), expdest - stackblock());
-}
-
-
-/*
- * Perform variable substitution and command substitution on an argument,
- * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
- * perform splitting and file name expansion.  When arglist is NULL, perform
- * here document expansion.
- */
-
-void
-expandarg(union node *arg, struct arglist *arglist, int flag)
-{
-	struct strlist *sp;
-	char *p;
-
-	argbackq = arg->narg.backquote;
-	STARTSTACKSTR(expdest);
-	ifsfirst.next = NULL;
-	ifslastp = NULL;
-	argstr(arg->narg.text, flag);
-	if (arglist == NULL) {
-		return;			/* here document expanded */
-	}
-	STPUTC('\0', expdest);
-	p = grabstackstr(expdest);
-	exparg.lastp = &exparg.list;
-	/*
-	 * TODO - EXP_REDIR
-	 */
-	if (flag & EXP_FULL) {
-		ifsbreakup(p, &exparg);
-		*exparg.lastp = NULL;
-		exparg.lastp = &exparg.list;
-		expandmeta(exparg.list, flag);
-	} else {
-		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
-			rmescapes(p);
-		sp = (struct strlist *)stalloc(sizeof (struct strlist));
-		sp->text = p;
-		*exparg.lastp = sp;
-		exparg.lastp = &sp->next;
-	}
-	ifsfree();
-	*exparg.lastp = NULL;
-	if (exparg.list) {
-		*arglist->lastp = exparg.list;
-		arglist->lastp = exparg.lastp;
-	}
-}
-
-
-
-/*
- * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
- * characters to allow for further processing.  Otherwise treat
- * $@ like $* since no splitting will be performed.
- */
-
-STATIC void
-argstr(char *p, int flag)
-{
-	char c;
-	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
-	int firsteq = 1;
-
-	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
-		p = exptilde(p, flag);
-	for (;;) {
-		switch (c = *p++) {
-		case '\0':
-		case CTLENDVAR: /* ??? */
-			goto breakloop;
-		case CTLQUOTEMARK:
-			/* "$@" syntax adherence hack */
-			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
-				break;
-			if ((flag & EXP_FULL) != 0)
-				STPUTC(c, expdest);
-			break;
-		case CTLESC:
-			if (quotes)
-				STPUTC(c, expdest);
-			c = *p++;
-			STPUTC(c, expdest);
-			break;
-		case CTLVAR:
-			p = evalvar(p, flag);
-			break;
-		case CTLBACKQ:
-		case CTLBACKQ|CTLQUOTE:
-			expbackq(argbackq->n, c & CTLQUOTE, flag);
-			argbackq = argbackq->next;
-			break;
-		case CTLENDARI:
-			expari(flag);
-			break;
-		case ':':
-		case '=':
-			/*
-			 * sort of a hack - expand tildes in variable
-			 * assignments (after the first '=' and after ':'s).
-			 */
-			STPUTC(c, expdest);
-			if (flag & EXP_VARTILDE && *p == '~') {
-				if (c == '=') {
-					if (firsteq)
-						firsteq = 0;
-					else
-						break;
-				}
-				p = exptilde(p, flag);
-			}
-			break;
-		default:
-			STPUTC(c, expdest);
-		}
-	}
-breakloop:;
-	return;
-}
-
-STATIC char *
-exptilde(char *p, int flag)
-{
-	char c, *startp = p;
-#ifdef _HAVE_PWD_H
-	struct passwd *pw;
-#endif
-	const char *home;
-	int quotes = flag & (EXP_FULL | EXP_CASE);
-
-	while ((c = *p) != '\0') {
-		switch(c) {
-		case CTLESC:
-			return (startp);
-		case CTLQUOTEMARK:
-			return (startp);
-		case ':':
-			if (flag & EXP_VARTILDE)
-				goto done;
-			break;
-		case '/':
-			goto done;
-		}
-		p++;
-	}
-done:
-	*p = '\0';
-	if (*(startp+1) == '\0') {
-		if ((home = lookupvar("HOME")) == NULL)
-			goto lose;
-	} else {
-#ifdef _HAVE_PWD_H
-		if ((pw = getpwnam(startp+1)) == NULL)
-			goto lose;
-		home = pw->pw_dir;
-#else
-		goto lose;
-#endif
-	}
-	if (*home == '\0')
-		goto lose;
-	*p = c;
-	while ((c = *home++) != '\0') {
-		if (quotes && SQSYNTAX[(int)c] == CCTL)
-			STPUTC(CTLESC, expdest);
-		STPUTC(c, expdest);
-	}
-	return (p);
-lose:
-	*p = c;
-	return (startp);
-}
-
-
-STATIC void 
-removerecordregions(int endoff)
-{
-	if (ifslastp == NULL)
-		return;
-
-	if (ifsfirst.endoff > endoff) {
-		while (ifsfirst.next != NULL) {
-			struct ifsregion *ifsp;
-			INTOFF;
-			ifsp = ifsfirst.next->next;
-			ckfree(ifsfirst.next);
-			ifsfirst.next = ifsp;
-			INTON;
-		}
-		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;
-		INTOFF;
-		ifsp = ifslastp->next->next;
-		ckfree(ifslastp->next);
-		ifslastp->next = ifsp;
-		INTON;
-	}
-	if (ifslastp->endoff > endoff)
-		ifslastp->endoff = endoff;
-}
-
-
-/*
- * Expand arithmetic expression.  Backup to start of expression,
- * evaluate, place result in (backed up) result, adjust string position.
- */
-void
-expari(int flag)
-{
-	char *p, *start;
-	int result;
-	int begoff;
-	int quotes = flag & (EXP_FULL | EXP_CASE);
-	int quoted;
-
-	/*	ifsfree(); */
-
-	/*
-	 * This routine is slightly over-complicated for
-	 * efficiency.  First we make sure there is
-	 * enough space for the result, which may be bigger
-	 * than the expression if we add exponentation.  Next we
-	 * scan backwards looking for the start of arithmetic.  If the
-	 * next previous character is a CTLESC character, then we
-	 * have to rescan starting from the beginning since CTLESC
-	 * characters have to be processed left to right.
-	 */
-#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
-#error "integers with more than 10 digits are not supported"
-#endif
-	CHECKSTRSPACE(12 - 2, expdest);
-	USTPUTC('\0', expdest);
-	start = stackblock();
-	p = expdest - 1;
-	while (*p != CTLARI && p >= start)
-		--p;
-	if (*p != CTLARI)
-		error("missing CTLARI (shouldn't happen)");
-	if (p > start && *(p-1) == CTLESC)
-		for (p = start; *p != CTLARI; p++)
-			if (*p == CTLESC)
-				p++;
-
-	if (p[1] == '"')
-		quoted=1;
-	else
-		quoted=0;
-	begoff = p - start;
-	removerecordregions(begoff);
-	if (quotes)
-		rmescapes(p+2);
-	result = arith(p+2);
-	fmtstr(p, 12, "%d", result);
-
-	while (*p++)
-		;
-
-	if (quoted == 0)
-		recordregion(begoff, p - 1 - start, 0);
-	result = expdest - p + 1;
-	STADJUST(-result, expdest);
-}
-
-
-/*
- * Expand stuff in backwards quotes.
- */
-
-STATIC void
-expbackq(union node *cmd, int quoted, int flag)
-{
-	struct backcmd in;
-	int i;
-	char buf[128];
-	char *p;
-	char *dest = expdest;
-	struct ifsregion saveifs, *savelastp;
-	struct nodelist *saveargbackq;
-	char lastc;
-	int startloc = dest - stackblock();
-	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
-	int saveherefd;
-	int quotes = flag & (EXP_FULL | EXP_CASE);
-
-	INTOFF;
-	saveifs = ifsfirst;
-	savelastp = ifslastp;
-	saveargbackq = argbackq;
-	saveherefd = herefd;
-	herefd = -1;
-	p = grabstackstr(dest);
-	evalbackcmd(cmd, &in);
-	ungrabstackstr(p, dest);
-	ifsfirst = saveifs;
-	ifslastp = savelastp;
-	argbackq = saveargbackq;
-	herefd = saveherefd;
-
-	p = in.buf;
-	lastc = '\0';
-	for (;;) {
-		if (--in.nleft < 0) {
-			if (in.fd < 0)
-				break;
-			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
-			TRACE(("expbackq: read returns %d\n", i));
-			if (i <= 0)
-				break;
-			p = buf;
-			in.nleft = i - 1;
-		}
-		lastc = *p++;
-		if (lastc != '\0') {
-			if (quotes && syntax[(int)lastc] == CCTL)
-				STPUTC(CTLESC, dest);
-			STPUTC(lastc, dest);
-		}
-	}
-
-	/* Eat all trailing newlines */
-	for (p--; lastc == '\n'; lastc = *--p)
-		STUNPUTC(dest);
-
-	if (in.fd >= 0)
-		close(in.fd);
-	if (in.buf)
-		ckfree(in.buf);
-	if (in.jp)
-		back_exitstatus = waitforjob(in.jp);
-	if (quoted == 0)
-		recordregion(startloc, dest - stackblock(), 0);
-	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
-		(dest - stackblock()) - startloc,
-		(dest - stackblock()) - startloc,
-		stackblock() + startloc));
-	expdest = dest;
-	INTON;
-}
-
-
-
-STATIC int
-subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags)
-{
-	char *startp;
-	char *loc = NULL;
-	char *q;
-	int c = 0;
-	int saveherefd = herefd;
-	struct nodelist *saveargbackq = argbackq;
-	int amount;
-
-	herefd = -1;
-	argstr(p, 0);
-	STACKSTRNUL(expdest);
-	herefd = saveherefd;
-	argbackq = saveargbackq;
-	startp = stackblock() + startloc;
-	if (str == NULL)
-	    str = stackblock() + strloc;
-
-	switch (subtype) {
-	case VSASSIGN:
-		setvar(str, startp, 0);
-		amount = startp - expdest;
-		STADJUST(amount, expdest);
-		varflags &= ~VSNUL;
-		if (c != 0)
-			*loc = c;
-		return 1;
-
-	case VSQUESTION:
-		if (*p != CTLENDVAR) {
-			outfmt(&errout, "%s\n", startp);
-			error((char *)NULL);
-		}
-		error("%.*s: parameter %snot set", p - str - 1,
-		      str, (varflags & VSNUL) ? "null or "
-					      : nullstr);
-		/* NOTREACHED */
-
-	case VSTRIMLEFT:
-		for (loc = startp; loc < str; loc++) {
-			c = *loc;
-			*loc = '\0';
-			if (patmatch(str, startp, varflags & VSQUOTE))
-				goto recordleft;
-			*loc = c;
-			if ((varflags & VSQUOTE) && *loc == CTLESC)
-			        loc++;
-		}
-		return 0;
-
-	case VSTRIMLEFTMAX:
-		for (loc = str - 1; loc >= startp;) {
-			c = *loc;
-			*loc = '\0';
-			if (patmatch(str, startp, varflags & VSQUOTE))
-				goto recordleft;
-			*loc = c;
-			loc--;
-			if ((varflags & VSQUOTE) && loc > startp &&
-			    *(loc - 1) == CTLESC) {
-				for (q = startp; q < loc; q++)
-					if (*q == CTLESC)
-						q++;
-				if (q > loc)
-					loc--;
-			}
-		}
-		return 0;
-
-	case VSTRIMRIGHT:
-	        for (loc = str - 1; loc >= startp;) {
-			if (patmatch(str, loc, varflags & VSQUOTE))
-				goto recordright;
-			loc--;
-			if ((varflags & VSQUOTE) && loc > startp &&
-			    *(loc - 1) == CTLESC) { 
-				for (q = startp; q < loc; q++)
-					if (*q == CTLESC)
-						q++;
-				if (q > loc)
-					loc--;
-			}
-		}
-		return 0;
-
-	case VSTRIMRIGHTMAX:
-		for (loc = startp; loc < str - 1; loc++) {
-			if (patmatch(str, loc, varflags & VSQUOTE))
-				goto recordright;
-			if ((varflags & VSQUOTE) && *loc == CTLESC)
-			        loc++;
-		}
-		return 0;
-
-	default:
-		abort();
-	}
-
-recordleft:
-	*loc = c;
-	amount = ((str - 1) - (loc - startp)) - expdest;
-	STADJUST(amount, expdest);
-	while (loc != str - 1)
-		*startp++ = *loc++;
-	return 1;
-
-recordright:
-	amount = loc - expdest;
-	STADJUST(amount, expdest);
-	STPUTC('\0', expdest);
-	STADJUST(-1, expdest);
-	return 1;
-}
-
-
-/*
- * Expand a variable, and return a pointer to the next character in the
- * input string.
- */
-
-STATIC char *
-evalvar(char *p, int flag)
-{
-	int subtype;
-	int varflags;
-	char *var;
-	char *val;
-	int patloc;
-	int c;
-	int set;
-	int special;
-	int startloc;
-	int varlen;
-	int easy;
-	int quotes = flag & (EXP_FULL | EXP_CASE);
-
-	varflags = (unsigned char)*p++;
-	subtype = varflags & VSTYPE;
-	var = p;
-	special = !is_name(*p);
-	p = strchr(p, '=') + 1;
-again: /* jump here after setting a variable with ${var=text} */
-	if (special) {
-		set = varisset(var, varflags & VSNUL);
-		val = NULL;
-	} else {
-		val = lookupvar(var);
-		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
-			val = NULL;
-			set = 0;
-		} else
-			set = 1;
-	}
-	varlen = 0;
-	startloc = expdest - stackblock();
-	if (!set && uflag)
-		switch (subtype) {
-		case VSNORMAL:
-		case VSTRIMLEFT:
-		case VSTRIMLEFTMAX:
-		case VSTRIMRIGHT:
-		case VSTRIMRIGHTMAX:
-		case VSLENGTH:
-			error("%.*s: parameter not set", p - var - 1, var);
-			/* NOTREACHED */
-		}
-	if (set && subtype != VSPLUS) {
-		/* insert the value of the variable */
-		if (special) {
-			varvalue(var, varflags & VSQUOTE, subtype, flag);
-			if (subtype == VSLENGTH) {
-				varlen = expdest - stackblock() - startloc;
-				STADJUST(-varlen, expdest);
-			}
-		} else {
-			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
-								  : BASESYNTAX;
-
-			if (subtype == VSLENGTH) {
-				for (;*val; val++)
-					varlen++;
-			}
-			else {
-				while (*val) {
-					if (quotes && syntax[(int)*val] == CCTL)
-						STPUTC(CTLESC, expdest);
-					STPUTC(*val++, expdest);
-				}
-
-			}
-		}
-	}
-
-	if (subtype == VSPLUS)
-		set = ! set;
-
-	easy = ((varflags & VSQUOTE) == 0 ||
-		(*var == '@' && shellparam.nparam != 1));
-
-
-	switch (subtype) {
-	case VSLENGTH:
-		expdest = cvtnum(varlen, expdest);
-		goto record;
-
-	case VSNORMAL:
-		if (!easy)
-			break;
-record:
-		recordregion(startloc, expdest - stackblock(),
-			     varflags & VSQUOTE);
-		break;
-
-	case VSPLUS:
-	case VSMINUS:
-		if (!set) {
-		        argstr(p, flag);
-			break;
-		}
-		if (easy)
-			goto record;
-		break;
-
-	case VSTRIMLEFT:
-	case VSTRIMLEFTMAX:
-	case VSTRIMRIGHT:
-	case VSTRIMRIGHTMAX:
-		if (!set)
-			break;
-		/*
-		 * Terminate the string and start recording the pattern
-		 * right after it
-		 */
-		STPUTC('\0', expdest);
-		patloc = expdest - stackblock();
-		if (subevalvar(p, NULL, patloc, subtype,
-			       startloc, varflags) == 0) {
-			int amount = (expdest - stackblock() - patloc) + 1;
-			STADJUST(-amount, expdest);
-		}
-		/* Remove any recorded regions beyond start of variable */
-		removerecordregions(startloc);
-		goto record;
-
-	case VSASSIGN:
-	case VSQUESTION:
-		if (!set) {
-			if (subevalvar(p, var, 0, subtype, startloc,
-				       varflags)) {
-				varflags &= ~VSNUL;
-				/* 
-				 * Remove any recorded regions beyond 
-				 * start of variable 
-				 */
-				removerecordregions(startloc);
-				goto again;
-			}
-			break;
-		}
-		if (easy)
-			goto record;
-		break;
-
-	default:
-		abort();
-	}
-
-	if (subtype != VSNORMAL) {	/* skip to end of alternative */
-		int nesting = 1;
-		for (;;) {
-			if ((c = *p++) == CTLESC)
-				p++;
-			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
-				if (set)
-					argbackq = argbackq->next;
-			} else if (c == CTLVAR) {
-				if ((*p++ & VSTYPE) != VSNORMAL)
-					nesting++;
-			} else if (c == CTLENDVAR) {
-				if (--nesting == 0)
-					break;
-			}
-		}
-	}
-	return p;
-}
-
-
-
-/*
- * Test whether a specialized variable is set.
- */
-
-STATIC int
-varisset(char *name, int nulok)
-{
-	if (*name == '!')
-		return backgndpid != -1;
-	else if (*name == '@' || *name == '*') {
-		if (*shellparam.p == NULL)
-			return 0;
-
-		if (nulok) {
-			char **av;
-
-			for (av = shellparam.p; *av; av++)
-				if (**av != '\0')
-					return 1;
-			return 0;
-		}
-	} else if (is_digit(*name)) {
-		char *ap;
-		int num = atoi(name);
-
-		if (num > shellparam.nparam)
-			return 0;
-
-		if (num == 0)
-			ap = arg0;
-		else
-			ap = shellparam.p[num - 1];
-
-		if (nulok && (ap == NULL || *ap == '\0'))
-			return 0;
-	}
-	return 1;
-}
-
-
-
-/*
- * Add the value of a specialized variable to the stack string.
- */
-
-STATIC void
-varvalue(char *name, int quoted, int subtype, int flag)
-{
-	int num;
-	char *p;
-	int i;
-	char sep;
-	char **ap;
-	char const *syntax;
-
-#define STRTODEST(p) \
-	do {\
-	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \
-		syntax = quoted? DQSYNTAX : BASESYNTAX; \
-		while (*p) { \
-			if (syntax[(int)*p] == CCTL) \
-				STPUTC(CTLESC, expdest); \
-			STPUTC(*p++, expdest); \
-		} \
-	} else \
-		while (*p) \
-			STPUTC(*p++, expdest); \
-	} while (0)
-
-
-	switch (*name) {
-	case '$':
-		num = rootpid;
-		goto numvar;
-	case '?':
-		num = exitstatus;
-		goto numvar;
-	case '#':
-		num = shellparam.nparam;
-		goto numvar;
-	case '!':
-		num = backgndpid;
-numvar:
-		expdest = cvtnum(num, expdest);
-		break;
-	case '-':
-		for (i = 0; optlist[i].name; i++) {
-			if (optlist[i].val)
-				STPUTC(optlist[i].letter, expdest);
-		}
-		break;
-	case '@':
-		if (flag & EXP_FULL && quoted) {
-			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
-				STRTODEST(p);
-				if (*ap)
-					STPUTC('\0', expdest);
-			}
-			break;
-		}
-		/* fall through */
-	case '*':
-		if (ifsset() != 0)
-			sep = ifsval()[0];
-		else
-			sep = ' ';
-		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
-			STRTODEST(p);
-			if (*ap && sep)
-				STPUTC(sep, expdest);
-		}
-		break;
-	case '0':
-		p = arg0;
-		STRTODEST(p);
-		break;
-	default:
-		if (is_digit(*name)) {
-			num = atoi(name);
-			if (num > 0 && num <= shellparam.nparam) {
-				p = shellparam.p[num - 1];
-				STRTODEST(p);
-			}
-		}
-		break;
-	}
-}
-
-
-
-/*
- * 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 {
-		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
-		ifslastp->next = ifsp;
-	}
-	ifslastp = ifsp;
-	ifslastp->next = NULL;
-	ifslastp->begoff = start;
-	ifslastp->endoff = end;
-	ifslastp->nulonly = nulonly;
-}
-
-
-
-/*
- * Break the argument string into pieces based upon IFS and add the
- * strings to the argument list.  The regions of the string to be
- * searched for IFS characters have been stored by recordregion.
- */
-STATIC void
-ifsbreakup(char *string, struct arglist *arglist)
-{
-	struct ifsregion *ifsp;
-	struct strlist *sp;
-	char *start;
-	char *p;
-	char *q;
-	const char *ifs;
-	int ifsspc;
-	int nulonly;
-
-
-	start = string;
-	ifsspc = 0;
-	nulonly = 0;
-	if (ifslastp != NULL) {
-		ifsp = &ifsfirst;
-		do {
-			p = string + ifsp->begoff;
-			nulonly = ifsp->nulonly;
-			ifs = nulonly ? nullstr : 
-				( ifsset() ? ifsval() : " \t\n" );
-			ifsspc = 0;
-			while (p < string + ifsp->endoff) {
-				q = p;
-				if (*p == CTLESC)
-					p++;
-				if (strchr(ifs, *p)) {
-					if (!nulonly)
-						ifsspc = (strchr(" \t\n", *p) != NULL);
-					/* Ignore IFS whitespace at start */
-					if (q == start && ifsspc) {
-						p++;
-						start = p;
-						continue;
-					}
-					*q = '\0';
-					sp = (struct strlist *)stalloc(sizeof *sp);
-					sp->text = start;
-					*arglist->lastp = sp;
-					arglist->lastp = &sp->next;
-					p++;
-					if (!nulonly) {
-						for (;;) {
-							if (p >= string + ifsp->endoff) {
-								break;
-							}
-							q = p;
-							if (*p == CTLESC)
-								p++;
-							if (strchr(ifs, *p) == NULL ) {
-								p = q;
-								break;
-							} else if (strchr(" \t\n",*p) == NULL) {
-								if (ifsspc) {
-									p++;
-									ifsspc = 0;
-								} else {
-									p = q;
-									break;
-								}
-							} else
-								p++;
-						}
-					}
-					start = p;
-				} else
-					p++;
-			}
-		} while ((ifsp = ifsp->next) != NULL);
-		if (*start || (!ifsspc && start > string && 
-			(nulonly || 1))) {
-			sp = (struct strlist *)stalloc(sizeof *sp);
-			sp->text = start;
-			*arglist->lastp = sp;
-			arglist->lastp = &sp->next;
-		}
-	} else {
-		sp = (struct strlist *)stalloc(sizeof *sp);
-		sp->text = start;
-		*arglist->lastp = sp;
-		arglist->lastp = &sp->next;
-	}
-}
-
-STATIC void
-ifsfree(void)
-{
-	while (ifsfirst.next != NULL) {
-		struct ifsregion *ifsp;
-		INTOFF;
-		ifsp = ifsfirst.next->next;
-		ckfree(ifsfirst.next);
-		ifsfirst.next = ifsp;
-		INTON;
-	}
-	ifslastp = NULL;
-	ifsfirst.next = NULL;
-}
-
-
-
-/*
- * Expand shell metacharacters.  At this point, the only control characters
- * should be escapes.  The results are stored in the list exparg.
- */
-
-char *expdir;
-
-
-STATIC void
-expandmeta(struct strlist *str, int flag)
-{
-	char *p;
-	struct strlist **savelastp;
-	struct strlist *sp;
-	char c;
-	/* TODO - EXP_REDIR */
-
-	(void)flag;
-
-	while (str) {
-		if (fflag)
-			goto nometa;
-		p = str->text;
-		for (;;) {			/* fast check for meta chars */
-			if ((c = *p++) == '\0')
-				goto nometa;
-			if (c == '*' || c == '?' || c == '[' || c == '!')
-				break;
-		}
-		savelastp = exparg.lastp;
-		INTOFF;
-		if (expdir == NULL) {
-			int i = strlen(str->text);
-			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
-		}
-
-		expmeta(expdir, str->text);
-		ckfree(expdir);
-		expdir = NULL;
-		INTON;
-		if (exparg.lastp == savelastp) {
-			/*
-			 * no matches
-			 */
-nometa:
-			*exparg.lastp = str;
-			rmescapes(str->text);
-			exparg.lastp = &str->next;
-		} else {
-			*exparg.lastp = NULL;
-			*savelastp = sp = expsort(*savelastp);
-			while (sp->next != NULL)
-				sp = sp->next;
-			exparg.lastp = &sp->next;
-		}
-		str = str->next;
-	}
-}
-
-
-/*
- * Do metacharacter (i.e. *, ?, [...]) expansion.
- */
-
-STATIC void
-expmeta(char *enddir, char *name)
-{
-	char *p;
-	const char *cp;
-	char *q;
-	char *start;
-	char *endname;
-	int metaflag;
-	struct stat statb;
-	DIR *dirp;
-	struct dirent *dp;
-	int atend;
-	int matchdot;
-
-	metaflag = 0;
-	start = name;
-	for (p = name ; ; p++) {
-		if (*p == '*' || *p == '?')
-			metaflag = 1;
-		else if (*p == '[') {
-			q = p + 1;
-			if (*q == '!')
-				q++;
-			for (;;) {
-				while (*q == CTLQUOTEMARK)
-					q++;
-				if (*q == CTLESC)
-					q++;
-				if (*q == '/' || *q == '\0')
-					break;
-				if (*++q == ']') {
-					metaflag = 1;
-					break;
-				}
-			}
-		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
-			metaflag = 1;
-		} else if (*p == '\0')
-			break;
-		else if (*p == CTLQUOTEMARK)
-			continue;
-		else if (*p == CTLESC)
-			p++;
-		if (*p == '/') {
-			if (metaflag)
-				break;
-			start = p + 1;
-		}
-	}
-	if (metaflag == 0) {	/* we've reached the end of the file name */
-		if (enddir != expdir)
-			metaflag++;
-		for (p = name ; ; p++) {
-			if (*p == CTLQUOTEMARK)
-				continue;
-			if (*p == CTLESC)
-				p++;
-			*enddir++ = *p;
-			if (*p == '\0')
-				break;
-		}
-		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
-			addfname(expdir);
-		return;
-	}
-	endname = p;
-	if (start != name) {
-		p = name;
-		while (p < start) {
-			while (*p == CTLQUOTEMARK)
-				p++;
-			if (*p == CTLESC)
-				p++;
-			*enddir++ = *p++;
-		}
-	}
-	if (enddir == expdir) {
-		cp = ".";
-	} else if (enddir == expdir + 1 && *expdir == '/') {
-		cp = "/";
-	} else {
-		cp = expdir;
-		enddir[-1] = '\0';
-	}
-	if ((dirp = opendir(cp)) == NULL)
-		return;
-	if (enddir != expdir)
-		enddir[-1] = '/';
-	if (*endname == 0) {
-		atend = 1;
-	} else {
-		atend = 0;
-		*endname++ = '\0';
-	}
-	matchdot = 0;
-	p = start;
-	while (*p == CTLQUOTEMARK)
-		p++;
-	if (*p == CTLESC)
-		p++;
-	if (*p == '.')
-		matchdot++;
-	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
-		if (dp->d_name[0] == '.' && ! matchdot)
-			continue;
-		if (patmatch(start, dp->d_name, 0)) {
-			if (atend) {
-				scopy(dp->d_name, enddir);
-				addfname(expdir);
-			} else {
-				for (p = enddir, cp = dp->d_name;
-				     (*p++ = *cp++) != '\0';)
-					continue;
-				p[-1] = '/';
-				expmeta(p, endname);
-			}
-		}
-	}
-	closedir(dirp);
-	if (! atend)
-		endname[-1] = '/';
-}
-
-
-/*
- * Add a file name to the list.
- */
-
-STATIC void
-addfname(char *name)
-{
-	char *p;
-	struct strlist *sp;
-
-	p = stalloc(strlen(name) + 1);
-	scopy(name, p);
-	sp = (struct strlist *)stalloc(sizeof *sp);
-	sp->text = p;
-	*exparg.lastp = sp;
-	exparg.lastp = &sp->next;
-}
-
-
-/*
- * Sort the results of file name expansion.  It calculates the number of
- * strings to sort and then calls msort (short for merge sort) to do the
- * work.
- */
-
-STATIC struct strlist *
-expsort(struct strlist *str)
-{
-	int len;
-	struct strlist *sp;
-
-	len = 0;
-	for (sp = str ; sp ; sp = sp->next)
-		len++;
-	return msort(str, len);
-}
-
-
-STATIC struct strlist *
-msort(struct strlist *list, int len)
-{
-	struct strlist *p, *q = NULL;
-	struct strlist **lpp;
-	int half;
-	int n;
-
-	if (len <= 1)
-		return list;
-	half = len >> 1;
-	p = list;
-	for (n = half ; --n >= 0 ; ) {
-		q = p;
-		p = p->next;
-	}
-	q->next = NULL;			/* terminate first half of list */
-	q = msort(list, half);		/* sort first half of list */
-	p = msort(p, len - half);		/* sort second half */
-	lpp = &list;
-	for (;;) {
-		if (strcmp(p->text, q->text) < 0) {
-			*lpp = p;
-			lpp = &p->next;
-			if ((p = *lpp) == NULL) {
-				*lpp = q;
-				break;
-			}
-		} else {
-			*lpp = q;
-			lpp = &q->next;
-			if ((q = *lpp) == NULL) {
-				*lpp = p;
-				break;
-			}
-		}
-	}
-	return list;
-}
-
-
-
-/*
- * Returns true if the pattern matches the string.
- */
-
-int
-patmatch(char *pattern, char *string, int squoted)
-{
-#ifdef notdef
-	if (pattern[0] == '!' && pattern[1] == '!')
-		return 1 - pmatch(pattern + 2, string);
-	else
-#endif
-		return pmatch(pattern, string, squoted);
-}
-
-
-STATIC int
-pmatch(char *pattern, char *string, int squoted)
-{
-	char *p, *q;
-	char c;
-
-	p = pattern;
-	q = string;
-	for (;;) {
-		switch (c = *p++) {
-		case '\0':
-			goto breakloop;
-		case CTLESC:
-			if (squoted && *q == CTLESC)
-				q++;
-			if (*q++ != *p++)
-				return 0;
-			break;
-		case CTLQUOTEMARK:
-			continue;
-		case '?':
-			if (squoted && *q == CTLESC)
-				q++;
-			if (*q++ == '\0')
-				return 0;
-			break;
-		case '*':
-			c = *p;
-			while (c == CTLQUOTEMARK || c == '*')
-				c = *++p;
-			if (c != CTLESC &&  c != CTLQUOTEMARK &&
-			    c != '?' && c != '*' && c != '[') {
-				while (*q != c) {
-					if (squoted && *q == CTLESC &&
-					    q[1] == c)
-						break;
-					if (*q == '\0')
-						return 0;
-					if (squoted && *q == CTLESC)
-						q++;
-					q++;
-				}
-			}
-			do {
-				if (pmatch(p, q, squoted))
-					return 1;
-				if (squoted && *q == CTLESC)
-					q++;
-			} while (*q++ != '\0');
-			return 0;
-		case '[': {
-			char *endp;
-			int invert, found;
-			char chr;
-
-			endp = p;
-			if (*endp == '!')
-				endp++;
-			for (;;) {
-				while (*endp == CTLQUOTEMARK)
-					endp++;
-				if (*endp == '\0')
-					goto dft;		/* no matching ] */
-				if (*endp == CTLESC)
-					endp++;
-				if (*++endp == ']')
-					break;
-			}
-			invert = 0;
-			if (*p == '!') {
-				invert++;
-				p++;
-			}
-			found = 0;
-			chr = *q++;
-			if (squoted && chr == CTLESC)
-				chr = *q++;
-			if (chr == '\0')
-				return 0;
-			c = *p++;
-			do {
-				if (c == CTLQUOTEMARK)
-					continue;
-				if (c == CTLESC)
-					c = *p++;
-				if (*p == '-' && p[1] != ']') {
-					p++;
-					while (*p == CTLQUOTEMARK)
-						p++;
-					if (*p == CTLESC)
-						p++;
-					if (chr >= c && chr <= *p)
-						found = 1;
-					p++;
-				} else {
-					if (chr == c)
-						found = 1;
-				}
-			} while ((c = *p++) != ']');
-			if (found == invert)
-				return 0;
-			break;
-		}
-dft:	        default:
-			if (squoted && *q == CTLESC)
-				q++;
-			if (*q++ != c)
-				return 0;
-			break;
-		}
-	}
-breakloop:
-	if (*q != '\0')
-		return 0;
-	return 1;
-}
-
-
-
-/*
- * Remove any CTLESC characters from a string.
- */
-
-void
-rmescapes(char *str)
-{
-	char *p, *q;
-
-	p = str;
-	while (*p != CTLESC && *p != CTLQUOTEMARK) {
-		if (*p++ == '\0')
-			return;
-	}
-	q = p;
-	while (*p) {
-		if (*p == CTLQUOTEMARK) {
-			p++;
-			continue;
-		}
-		if (*p == CTLESC)
-			p++;
-		*q++ = *p++;
-	}
-	*q = '\0';
-}
-
-
-
-/*
- * See if a pattern matches in a case statement.
- */
-
-int
-casematch(union node *pattern, char *val)
-{
-	struct stackmark smark;
-	int result;
-	char *p;
-
-	setstackmark(&smark);
-	argbackq = pattern->narg.backquote;
-	STARTSTACKSTR(expdest);
-	ifslastp = NULL;
-	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
-	STPUTC('\0', expdest);
-	p = grabstackstr(expdest);
-	result = patmatch(p, val, 0);
-	popstackmark(&smark);
-	return result;
-}
-
-/*
- * Our own itoa().
- */
-
-STATIC char *
-cvtnum(int num, char *buf)
-{
-	char temp[32];
-	int neg = num < 0;
-	char *p = temp + 31;
-
-	temp[31] = '\0';
-
-	do {
-		*--p = num % 10 + '0';
-	} while ((num /= 10) != 0);
-
-	if (neg)
-		*--p = '-';
-
-	while (*p)
-		STPUTC(*p++, buf);
-	return buf;
-}
diff --git a/ash/jobs.c b/ash/jobs.c
deleted file mode 100644
index 89fc2aa..0000000
--- a/ash/jobs.c
+++ /dev/null
@@ -1,1482 +0,0 @@
-/*	$NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)jobs.c	8.5 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos Exp $");
-#endif
-#endif /* not lint */
-
-#include <fcntl.h>
-#include <signal.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#ifndef __KLIBC__
-#include <paths.h>
-#else
-#define _PATH_DEVNULL "/dev/null"
-#endif
-#include <sys/types.h>
-#include <sys/param.h>
-#ifdef BSD
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#endif
-#ifdef __KLIBC__
-#include <sys/wait.h>
-#define killpg(s,i) kill(-(s),i)
-#endif
-#include <sys/ioctl.h>
-
-#include "shell.h"
-#if JOBS
-#if OLD_TTY_DRIVER
-#include "sgtty.h"
-#else
-#include <termios.h>
-#endif
-#undef CEOF			/* syntax.h redefines this */
-#endif
-#include "redir.h"
-#include "show.h"
-#include "main.h"
-#include "parser.h"
-#include "nodes.h"
-#include "jobs.h"
-#include "options.h"
-#include "trap.h"
-#include "syntax.h"
-#include "input.h"
-#include "output.h"
-#include "memalloc.h"
-#include "error.h"
-#include "mystring.h"
-
-
-static struct job *jobtab;		/* array of jobs */
-static int njobs;			/* size of array */
-static int jobs_invalid;		/* set in child */
-MKINIT pid_t backgndpid = -1;	/* pid of last background process */
-#if JOBS
-int initialpgrp;		/* pgrp of shell on invocation */
-static int curjob = -1;		/* current job */
-#ifndef WCOREDUMP
-#define WCOREDUMP(status)     ((status) & 0x80)
-#endif
-#endif
-static int ttyfd = -1;
-
-STATIC void restartjob(struct job *);
-STATIC void freejob(struct job *);
-STATIC struct job *getjob(const char *, int);
-STATIC int dowait(int, struct job *);
-STATIC int onsigchild(void);
-STATIC int waitproc(int, struct job *, int *);
-STATIC void cmdtxt(union node *);
-STATIC void cmdlist(union node *, int);
-STATIC void cmdputs(const char *);
-
-#if JOBS
-/*
- * Turn job control on and off.
- *
- * Note:  This code assumes that the third arg to ioctl is a character
- * pointer, which is true on Berkeley systems but not System V.  Since
- * System V doesn't have job control yet, this isn't a problem now.
- */
-
-MKINIT int jobctl;
-
-void
-setjobctl(int on)
-{
-#ifdef OLD_TTY_DRIVER
-	int ldisc;
-#endif
-
-	if (on == jobctl || rootshell == 0)
-		return;
-	if (on) {
-#if defined(FIOCLEX) || defined(FD_CLOEXEC)
-		int err;
-		int i;
-		if (ttyfd != -1)
-			close(ttyfd);
-		if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) {
-			for (i = 0; i < 3; i++) {
-				if (isatty(i) && (ttyfd = dup(i)) != -1)
-					break;
-			}
-			if (i == 3)
-				goto out;
-		}
-		/* Move to a high fd */
-		for (i = 10; i > 2; i--) {
-			if ((err = fcntl(ttyfd, F_DUPFD, (1 << i) - 1)) != -1)
-				break;
-		}
-		if (err != -1) {
-			close(ttyfd);
-			ttyfd = err;
-		}
-#ifdef FIOCLEX
-		err = ioctl(ttyfd, FIOCLEX, 0);
-#elif FD_CLOEXEC
-		err = fcntl(ttyfd, F_SETFD,
-		    fcntl(ttyfd, F_GETFD, 0) | FD_CLOEXEC);
-#endif
-		if (err == -1) {
-			close(ttyfd);
-			ttyfd = -1;
-			goto out;
-		}
-#else
-		out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control");
-		goto out;
-#endif
-		do { /* while we are in the background */
-			if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) {
-out:
-				out2str("sh: can't access tty; job control turned off\n");
-				mflag = 0;
-				return;
-			}
-			if (initialpgrp == -1)
-				initialpgrp = getpgrp();
-			else if (initialpgrp != getpgrp()) {
-				killpg(0, SIGTTIN);
-				continue;
-			}
-		} while (0);
-
-#ifdef OLD_TTY_DRIVER
-		if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0
-		    || ldisc != NTTYDISC) {
-			out2str("sh: need new tty driver to run job control; job control turned off\n");
-			mflag = 0;
-			return;
-		}
-#endif
-		setsignal(SIGTSTP, 0);
-		setsignal(SIGTTOU, 0);
-		setsignal(SIGTTIN, 0);
-		if (getpgid(0) != rootpid && setpgid(0, rootpid) == -1)
-			error("Cannot set process group (%s) at %d",
-			    strerror(errno), __LINE__);
-		if (tcsetpgrp(ttyfd, rootpid) == -1)
-			error("Cannot set tty process group (%s) at %d",
-			    strerror(errno), __LINE__);
-	} else { /* turning job control off */
-		if (getpgid(0) != initialpgrp && setpgid(0, initialpgrp) == -1)
-			error("Cannot set process group (%s) at %d",
-			    strerror(errno), __LINE__);
-		if (tcsetpgrp(ttyfd, initialpgrp) == -1)
-			error("Cannot set tty process group (%s) at %d",
-			    strerror(errno), __LINE__);
-		close(ttyfd);
-		ttyfd = -1;
-		setsignal(SIGTSTP, 0);
-		setsignal(SIGTTOU, 0);
-		setsignal(SIGTTIN, 0);
-	}
-	jobctl = on;
-}
-#endif /* JOBS */
-
-#ifdef mkinit
-INCLUDE <stdlib.h>
-
-SHELLPROC {
-	backgndpid = -1;
-#if JOBS
-	jobctl = 0;
-#endif
-}
-
-#endif
-
-
-
-#if JOBS
-int
-fgcmd(int argc, char **argv)
-{
-	struct job *jp;
-	int i;
-	int status;
-
-	nextopt("");
-	jp = getjob(*argptr, 0);
-	if (jp->jobctl == 0)
-		error("job not created under job control");
-	out1fmt("%s", jp->ps[0].cmd);
-	for (i = 1; i < jp->nprocs; i++)
-		out1fmt(" | %s", jp->ps[i].cmd );
-	out1c('\n');
-	flushall();
-
-	for (i = 0; i < jp->nprocs; i++)
-	    if (tcsetpgrp(ttyfd, jp->ps[i].pid) != -1)
-		    break;
-
-	if (i >= jp->nprocs) {
-		error("Cannot set tty process group (%s) at %d",
-		    strerror(errno), __LINE__);
-	}
-	restartjob(jp);
-	INTOFF;
-	status = waitforjob(jp);
-	INTON;
-	return status;
-}
-
-static void
-set_curjob(struct job *jp, int mode)
-{
-	struct job *jp1, *jp2;
-	int i, ji;
-
-	ji = jp - jobtab;
-
-	/* first remove from list */
-	if (ji == curjob)
-		curjob = jp->prev_job;
-	else {
-		for (i = 0; i < njobs; i++) {
-			if (jobtab[i].prev_job != ji)
-				continue;
-			jobtab[i].prev_job = jp->prev_job;
-			break;
-		}
-	}
-
-	/* Then re-insert in correct position */
-	switch (mode) {
-	case 0:	/* job being deleted */
-		jp->prev_job = -1;
-		break;
-	case 1:	/* newly created job or backgrounded job,
-		   put after all stopped jobs. */
-		if (curjob != -1 && jobtab[curjob].state == JOBSTOPPED) {
-			for (jp1 = jobtab + curjob; ; jp1 = jp2) {
-				if (jp1->prev_job == -1)
-					break;
-				jp2 = jobtab + jp1->prev_job;
-				if (jp2->state != JOBSTOPPED)
-					break;
-			}
-			jp->prev_job = jp1->prev_job;
-			jp1->prev_job = ji;
-			break;
-		}
-		/* FALLTHROUGH */
-	case 2:	/* newly stopped job - becomes curjob */
-		jp->prev_job = curjob;
-		curjob = ji;
-		break;
-	}
-}
-
-int
-bgcmd(int argc, char **argv)
-{
-	struct job *jp;
-	int i;
-
-	nextopt("");
-	do {
-		jp = getjob(*argptr, 0);
-		if (jp->jobctl == 0)
-			error("job not created under job control");
-		set_curjob(jp, 1);
-		out1fmt("[%ld] %s", (long)(jp - jobtab + 1), jp->ps[0].cmd);
-		for (i = 1; i < jp->nprocs; i++)
-			out1fmt(" | %s", jp->ps[i].cmd );
-		out1c('\n');
-		flushall();
-		restartjob(jp);
-	} while (*argptr && *++argptr);
-	return 0;
-}
-
-
-STATIC void
-restartjob(struct job *jp)
-{
-	struct procstat *ps;
-	int i;
-
-	if (jp->state == JOBDONE)
-		return;
-	INTOFF;
-	for (i = 0; i < jp->nprocs; i++)
-		if (killpg(jp->ps[i].pid, SIGCONT) != -1)
-			break;
-	if (i >= jp->nprocs)
-		error("Cannot continue job (%s)", strerror(errno));
-	for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
-		if (WIFSTOPPED(ps->status)) {
-			ps->status = -1;
-			jp->state = JOBRUNNING;
-		}
-	}
-	INTON;
-}
-#endif /* JOBS */
-
-static void
-showjob(struct output *out, struct job *jp, int mode)
-{
-	int procno;
-	int st;
-	struct procstat *ps;
-	int col;
-	char s[64];
-
-#if JOBS
-	if (mode & SHOW_PGID) {
-		/* just output process (group) id of pipeline */
-		outfmt(out, "%ld\n", (long)jp->ps->pid);
-		return;
-	}
-#endif
-
-	procno = jp->nprocs;
-	if (!procno)
-		return;
-
-	if (mode & SHOW_PID)
-		mode |= SHOW_MULTILINE;
-
-	if ((procno > 1 && !(mode & SHOW_MULTILINE))
-	    || (mode & SHOW_SIGNALLED)) {
-		/* See if we have more than one status to report */
-		ps = jp->ps;
-		st = ps->status;
-		do {
-			int st1 = ps->status;
-			if (st1 != st)
-				/* yes - need multi-line output */
-				mode |= SHOW_MULTILINE;
-			if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1))
-				continue;
-			if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f)
-			    && st1 != SIGINT && st1 != SIGPIPE))
-				mode |= SHOW_ISSIG;
-
-		} while (ps++, --procno);
-		procno = jp->nprocs;
-	}
-
-	if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) {
-		if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) {
-			TRACE(("showjob: freeing job %d\n", jp - jobtab + 1));
-			freejob(jp);
-		}
-		return;
-	}
-
-	for (ps = jp->ps; --procno >= 0; ps++) {	/* for each process */
-		if (ps == jp->ps)
-			fmtstr(s, 16, "[%ld] %c ",
-				(long)(jp - jobtab + 1),
-#if JOBS
-				jp == jobtab + curjob ? '+' :
-				curjob != -1 && jp == jobtab +
-					    jobtab[curjob].prev_job ? '-' :
-#endif
-				' ');
-		else
-			fmtstr(s, 16, "      " );
-		col = strlen(s);
-		if (mode & SHOW_PID) {
-			fmtstr(s + col, 16, "%ld ", (long)ps->pid);
-			     col += strlen(s + col);
-		}
-		if (ps->status == -1) {
-			scopy("Running", s + col);
-		} else if (WIFEXITED(ps->status)) {
-			st = WEXITSTATUS(ps->status);
-			if (st)
-				fmtstr(s + col, 16, "Done(%d)", st);
-			else
-				fmtstr(s + col, 16, "Done");
-		} else {
-#if JOBS
-			if (WIFSTOPPED(ps->status)) 
-				st = WSTOPSIG(ps->status);
-			else /* WIFSIGNALED(ps->status) */
-#endif
-				st = WTERMSIG(ps->status);
-			st &= 0x7f;
-			if (st < NSIG && sys_siglist[st])
-				scopyn(sys_siglist[st], s + col, 32);
-			else
-				fmtstr(s + col, 16, "Signal %d", st);
-			if (WCOREDUMP(ps->status)) {
-				col += strlen(s + col);
-				scopyn(" (core dumped)", s + col,  64 - col);
-			}
-		}
-		col += strlen(s + col);
-		outstr(s, out);
-		do {
-			outc(' ', out);
-			col++;
-		} while (col < 30);
-		outstr(ps->cmd, out);
-		if (mode & SHOW_MULTILINE) {
-			if (procno > 0) {
-				outc(' ', out);
-				outc('|', out);
-			}
-		} else {
-			while (--procno >= 0)
-				outfmt(out, " | %s", (++ps)->cmd );
-		}
-		outc('\n', out);
-	}
-	flushout(out);
-	jp->changed = 0;
-	if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE))
-		freejob(jp);
-}
-
-
-int
-jobscmd(int argc, char **argv)
-{
-	int mode, m;
-	int sv = jobs_invalid;
-
-	(void)argc; (void)argv;
-
-	jobs_invalid = 0;
-	mode = 0;
-	while ((m = nextopt("lp")))
-		if (m == 'l')
-			mode = SHOW_PID;
-		else
-			mode = SHOW_PGID;
-	if (*argptr)
-		do
-			showjob(out1, getjob(*argptr,0), mode);
-		while (*++argptr);
-	else
-		showjobs(out1, mode);
-	jobs_invalid = sv;
-	return 0;
-}
-
-
-/*
- * Print a list of jobs.  If "change" is nonzero, only print jobs whose
- * statuses have changed since the last call to showjobs.
- *
- * If the shell is interrupted in the process of creating a job, the
- * result may be a job structure containing zero processes.  Such structures
- * will be freed here.
- */
-
-void
-showjobs(struct output *out, int mode)
-{
-	int jobno;
-	struct job *jp;
-	int silent = 0, gotpid;
-
-	TRACE(("showjobs(%x) called\n", mode));
-
-	/* If not even one one job changed, there is nothing to do */
-	gotpid = dowait(0, NULL);
-	while (dowait(0, NULL) > 0)
-		continue;
-#if JOBS
-	/*
-	 * Check if we are not in our foreground group, and if not
-	 * put us in it.
-	 */
-	if (mflag && gotpid != -1 && tcgetpgrp(ttyfd) != getpid()) {
-		if (tcsetpgrp(ttyfd, getpid()) == -1)
-			error("Cannot set tty process group (%s) at %d",
-			    strerror(errno), __LINE__);
-		TRACE(("repaired tty process group\n"));
-		silent = 1;
-	}
-#endif
-	if (jobs_invalid)
-		return;
-
-	for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
-		if (!jp->used)
-			continue;
-		if (jp->nprocs == 0) {
-			freejob(jp);
-			continue;
-		}
-		if ((mode & SHOW_CHANGED) && !jp->changed)
-			continue;
-		if (silent && jp->changed) {
-			jp->changed = 0;
-			continue;
-		}
-		showjob(out, jp, mode);
-	}
-}
-
-/*
- * Mark a job structure as unused.
- */
-
-STATIC void
-freejob(struct job *jp)
-{
-	INTOFF;
-	if (jp->ps != &jp->ps0) {
-		ckfree(jp->ps);
-		jp->ps = &jp->ps0;
-	}
-	jp->nprocs = 0;
-	jp->used = 0;
-#if JOBS
-	set_curjob(jp, 0);
-#endif
-	INTON;
-}
-
-
-
-int
-waitcmd(int argc, char **argv)
-{
-	struct job *job;
-	int status, retval = 0;
-	struct job *jp;
-
-	(void)argc; (void)argv;
-
-	nextopt("");
-
-	if (!*argptr) {
-		/* wait for all jobs */
-		jp = jobtab;
-		if (jobs_invalid)
-			return 0;
-		for (;;) {
-			if (jp >= jobtab + njobs) {
-				/* no running procs */
-				return 0;
-			}
-			if (!jp->used || jp->state != JOBRUNNING) {
-				jp++;
-				continue;
-			}
-			if (dowait(1, (struct job *)NULL) == -1)
-			       return 128 + SIGINT;
-			jp = jobtab;
-		}
-	}
-
-	for (; *argptr; argptr++) {
-		job = getjob(*argptr, 1);
-		if (!job) {
-			retval = 127;
-			continue;
-		}
-		/* loop until process terminated or stopped */
-		while (job->state == JOBRUNNING) {
-			if (dowait(1, (struct job *)NULL) == -1)
-			       return 128 + SIGINT;
-		}
-		status = job->ps[job->nprocs].status;
-		if (WIFEXITED(status))
-			retval = WEXITSTATUS(status);
-#if JOBS
-		else if (WIFSTOPPED(status))
-			retval = WSTOPSIG(status) + 128;
-#endif
-		else {
-			/* XXX: limits number of signals */
-			retval = WTERMSIG(status) + 128;
-		}
-		if (!iflag)
-			freejob(job);
-	}
-	return retval;
-}
-
-
-
-int
-jobidcmd(int argc, char **argv)
-{
-	struct job *jp;
-	int i;
-
-	(void)argc; (void)argv;
-
-	nextopt("");
-	jp = getjob(*argptr, 0);
-	for (i = 0 ; i < jp->nprocs ; ) {
-		out1fmt("%ld", (long)jp->ps[i].pid);
-		out1c(++i < jp->nprocs ? ' ' : '\n');
-	}
-	return 0;
-}
-
-int
-getjobpgrp(const char *name)
-{
-	struct job *jp;
-
-	jp = getjob(name, 1);
-	if (jp == 0)
-		return 0;
-	return -jp->ps[0].pid;
-}
-
-/*
- * Convert a job name to a job structure.
- */
-
-STATIC struct job *
-getjob(const char *name, int noerror)
-{
-	int jobno = -1;
-	struct job *jp;
-	int pid;
-	int i;
-	const char *err_msg = "No such job: %s";
-		
-	if (name == NULL) {
-#if JOBS
-		jobno = curjob;
-#endif
-		err_msg = "No current job";
-	} else if (name[0] == '%') {
-		if (is_number(name + 1)) {
-			jobno = number(name + 1) - 1;
-		} else if (!name[2]) {
-			switch (name[1]) {
-#if JOBS
-			case 0:
-			case '+':
-			case '%':
-				jobno = curjob;
-				err_msg = "No current job";
-				break;
-			case '-':
-				jobno = curjob;
-				if (jobno != -1)
-					jobno = jobtab[jobno].prev_job;
-				err_msg = "No previous job";
-				break;
-#endif
-			default:
-				goto check_pattern;
-			}
-		} else {
-			struct job *found;
-    check_pattern:
-			found = NULL;
-			for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
-				if (!jp->used || jp->nprocs <= 0)
-					continue;
-				if ((name[1] == '?'
-					&& strstr(jp->ps[0].cmd, name + 2))
-				    || prefix(name + 1, jp->ps[0].cmd)) {
-					if (found) {
-						err_msg = "%s: ambiguous";
-						found = 0;
-						break;
-					}
-					found = jp;
-				}
-			}
-			if (found)
-				return found;
-		}
-
-	} else if (is_number(name)) {
-		pid = number(name);
-		for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
-			if (jp->used && jp->nprocs > 0
-			 && jp->ps[jp->nprocs - 1].pid == pid)
-				return jp;
-		}
-	}
-
-	if (!jobs_invalid && jobno >= 0 && jobno < njobs) {
-		jp = jobtab + jobno;
-		if (jp->used)
-			return jp;
-	}
-	if (!noerror)
-		error(err_msg, name);
-	return 0;
-}
-
-
-
-/*
- * Return a new job structure,
- */
-
-struct job *
-makejob(union node *node, int nprocs)
-{
-	int i;
-	struct job *jp;
-
-	(void)node;
-
-	if (jobs_invalid) {
-		for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) {
-			if (jp->used)
-				freejob(jp);
-		}
-		jobs_invalid = 0;
-	}
-
-	for (i = njobs, jp = jobtab ; ; jp++) {
-		if (--i < 0) {
-			INTOFF;
-			if (njobs == 0) {
-				jobtab = ckmalloc(4 * sizeof jobtab[0]);
-			} else {
-				jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
-				memcpy(jp, jobtab, njobs * sizeof jp[0]);
-				/* Relocate `ps' pointers */
-				for (i = 0; i < njobs; i++)
-					if (jp[i].ps == &jobtab[i].ps0)
-						jp[i].ps = &jp[i].ps0;
-				ckfree(jobtab);
-				jobtab = jp;
-			}
-			jp = jobtab + njobs;
-			for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
-			INTON;
-			break;
-		}
-		if (jp->used == 0)
-			break;
-	}
-	INTOFF;
-	jp->state = JOBRUNNING;
-	jp->used = 1;
-	jp->changed = 0;
-	jp->nprocs = 0;
-#if JOBS
-	jp->jobctl = jobctl;
-	set_curjob(jp, 1);
-#endif
-	if (nprocs > 1) {
-		jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
-	} else {
-		jp->ps = &jp->ps0;
-	}
-	INTON;
-	TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
-	    jp - jobtab + 1));
-	return jp;
-}
-
-
-/*
- * Fork off a subshell.  If we are doing job control, give the subshell its
- * own process group.  Jp is a job structure that the job is to be added to.
- * N is the command that will be evaluated by the child.  Both jp and n may
- * be NULL.  The mode parameter can be one of the following:
- *	FORK_FG - Fork off a foreground process.
- *	FORK_BG - Fork off a background process.
- *	FORK_NOJOB - Like FORK_FG, but don't give the process its own
- *		     process group even if job control is on.
- *
- * When job control is turned off, background processes have their standard
- * input redirected to /dev/null (except for the second and later processes
- * in a pipeline).
- */
-
-int
-forkshell(struct job *jp, union node *n, int mode)
-{
-	int pid;
-
-	TRACE(("forkshell(%%%d, %p, %d) called\n", jp - jobtab, n, mode));
-	switch ((pid = fork())) {
-	case -1:
-		TRACE(("Fork failed, errno=%d\n", errno));
-		INTON;
-		error("Cannot fork");
-		break;
-	case 0:
-		forkchild(jp, n, mode, 0);
-		return 0;
-	default:
-		return forkparent(jp, n, mode, pid);
-	}
-}
-
-int
-forkparent(struct job *jp, union node *n, int mode, pid_t pid)
-{
-	int pgrp;
-
-	if (rootshell && mode != FORK_NOJOB && mflag) {
-		if (jp == NULL || jp->nprocs == 0)
-			pgrp = pid;
-		else
-			pgrp = jp->ps[0].pid;
-		/* This can fail because we are doing it in the child also */
-		(void)setpgid(pid, pgrp);
-	}
-	if (mode == FORK_BG)
-		backgndpid = pid;		/* set $! */
-	if (jp) {
-		struct procstat *ps = &jp->ps[jp->nprocs++];
-		ps->pid = pid;
-		ps->status = -1;
-		ps->cmd[0] = 0;
-		if (/* iflag && rootshell && */ n)
-			commandtext(ps, n);
-	}
-	TRACE(("In parent shell:  child = %d\n", pid));
-	return pid;
-}
-
-void
-forkchild(struct job *jp, union node *n, int mode, int vforked)
-{
-	int wasroot;
-	int pgrp;
-	const char *devnull = _PATH_DEVNULL;
-	const char *nullerr = "Can't open %s";
-
-	(void)pgrp; (void)n;
-
-	wasroot = rootshell;
-	TRACE(("Child shell %d\n", getpid()));
-	if (!vforked)
-		rootshell = 0;
-
-	closescript(vforked);
-	clear_traps(vforked);
-#if JOBS
-	if (!vforked)
-		jobctl = 0;		/* do job control only in root shell */
-	if (wasroot && mode != FORK_NOJOB && mflag) {
-		if (jp == NULL || jp->nprocs == 0)
-			pgrp = getpid();
-		else
-			pgrp = jp->ps[0].pid;
-		/* This can fail because we are doing it in the parent also */
-		(void)setpgid(0, pgrp);
-		if (mode == FORK_FG) {
-			if (tcsetpgrp(ttyfd, pgrp) == -1)
-				error("Cannot set tty process group (%s) at %d",
-				    strerror(errno), __LINE__);
-		}
-		setsignal(SIGTSTP, vforked);
-		setsignal(SIGTTOU, vforked);
-	} else if (mode == FORK_BG) {
-		ignoresig(SIGINT, vforked);
-		ignoresig(SIGQUIT, vforked);
-		if ((jp == NULL || jp->nprocs == 0) &&
-		    ! fd0_redirected_p ()) {
-			close(0);
-			if (open(devnull, O_RDONLY) != 0)
-				error(nullerr, devnull);
-		}
-	}
-#else
-	if (mode == FORK_BG) {
-		ignoresig(SIGINT, vforked);
-		ignoresig(SIGQUIT, vforked);
-		if ((jp == NULL || jp->nprocs == 0) &&
-		    ! fd0_redirected_p ()) {
-			close(0);
-			if (open(devnull, O_RDONLY) != 0)
-				error(nullerr, devnull);
-		}
-	}
-#endif
-	if (wasroot && iflag) {
-		setsignal(SIGINT, vforked);
-		setsignal(SIGQUIT, vforked);
-		setsignal(SIGTERM, vforked);
-	}
-
-	if (!vforked)
-		jobs_invalid = 1;
-}
-
-/*
- * Wait for job to finish.
- *
- * Under job control we have the problem that while a child process is
- * running interrupts generated by the user are sent to the child but not
- * to the shell.  This means that an infinite loop started by an inter-
- * active user may be hard to kill.  With job control turned off, an
- * interactive user may place an interactive program inside a loop.  If
- * the interactive program catches interrupts, the user doesn't want
- * these interrupts to also abort the loop.  The approach we take here
- * is to have the shell ignore interrupt signals while waiting for a
- * forground process to terminate, and then send itself an interrupt
- * signal if the child process was terminated by an interrupt signal.
- * Unfortunately, some programs want to do a bit of cleanup and then
- * exit on interrupt; unless these processes terminate themselves by
- * sending a signal to themselves (instead of calling exit) they will
- * confuse this approach.
- */
-
-int
-waitforjob(struct job *jp)
-{
-#if JOBS
-	int mypgrp = getpgrp();
-#endif
-	int status;
-	int st;
-
-	INTOFF;
-	TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
-	while (jp->state == JOBRUNNING) {
-		dowait(1, jp);
-	}
-#if JOBS
-	if (jp->jobctl) {
-		if (tcsetpgrp(ttyfd, mypgrp) == -1)
-			error("Cannot set tty process group (%s) at %d",
-			    strerror(errno), __LINE__);
-	}
-	if (jp->state == JOBSTOPPED && curjob != jp - jobtab)
-		set_curjob(jp, 2);
-#endif
-	status = jp->ps[jp->nprocs - 1].status;
-	/* convert to 8 bits */
-	if (WIFEXITED(status))
-		st = WEXITSTATUS(status);
-#if JOBS
-	else if (WIFSTOPPED(status))
-		st = WSTOPSIG(status) + 128;
-#endif
-	else
-		st = WTERMSIG(status) + 128;
-	TRACE(("waitforjob: job %d, nproc %d, status %x, st %x\n",
-		jp - jobtab + 1, jp->nprocs, status, st ));
-#if JOBS
-	if (jp->jobctl) {
-		/*
-		 * This is truly gross.
-		 * If we're doing job control, then we did a TIOCSPGRP which
-		 * caused us (the shell) to no longer be in the controlling
-		 * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
-		 * intuit from the subprocess exit status whether a SIGINT
-		 * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
-		 */
-		if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
-			raise(SIGINT);
-	}
-#endif
-	if (! JOBS || jp->state == JOBDONE)
-		freejob(jp);
-	INTON;
-	return st;
-}
-
-
-
-/*
- * Wait for a process to terminate.
- */
-
-STATIC int
-dowait(int block, struct job *job)
-{
-	int pid;
-	int status;
-	struct procstat *sp;
-	struct job *jp;
-	struct job *thisjob;
-	int done;
-	int stopped;
-	extern volatile char gotsig[];
-
-	TRACE(("dowait(%d) called\n", block));
-	do {
-		pid = waitproc(block, job, &status);
-		TRACE(("wait returns pid %d, status %d\n", pid, status));
-	} while (pid == -1 && errno == EINTR && gotsig[SIGINT - 1] == 0);
-	if (pid <= 0)
-		return pid;
-	INTOFF;
-	thisjob = NULL;
-	for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
-		if (jp->used) {
-			done = 1;
-			stopped = 1;
-			for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
-				if (sp->pid == -1)
-					continue;
-				if (sp->pid == pid) {
-					TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jp - jobtab + 1, pid, sp->status, status));
-					sp->status = status;
-					thisjob = jp;
-				}
-				if (sp->status == -1)
-					stopped = 0;
-				else if (WIFSTOPPED(sp->status))
-					done = 0;
-			}
-			if (stopped) {		/* stopped or done */
-				int state = done ? JOBDONE : JOBSTOPPED;
-				if (jp->state != state) {
-					TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
-					jp->state = state;
-#if JOBS
-					if (done)
-						set_curjob(jp, 0);
-#endif
-				}
-			}
-		}
-	}
-
-	if (thisjob && thisjob->state != JOBRUNNING) {
-		int mode = 0;
-		if (!rootshell || !iflag)
-			mode = SHOW_SIGNALLED;
-		if (job == thisjob)
-			mode = SHOW_SIGNALLED | SHOW_NO_FREE;
-		if (mode)
-			showjob(out2, thisjob, mode);
-		else {
-			TRACE(("Not printing status, rootshell=%d, job=%p\n",
-				rootshell, job));
-			thisjob->changed = 1;
-		}
-	}
-
-	INTON;
-	return pid;
-}
-
-
-
-/*
- * Do a wait system call.  If job control is compiled in, we accept
- * stopped processes.  If block is zero, we return a value of zero
- * rather than blocking.
- *
- * System V doesn't have a non-blocking wait system call.  It does
- * have a SIGCLD signal that is sent to a process when one of it's
- * children dies.  The obvious way to use SIGCLD would be to install
- * a handler for SIGCLD which simply bumped a counter when a SIGCLD
- * was received, and have waitproc bump another counter when it got
- * the status of a process.  Waitproc would then know that a wait
- * system call would not block if the two counters were different.
- * This approach doesn't work because if a process has children that
- * have not been waited for, System V will send it a SIGCLD when it
- * installs a signal handler for SIGCLD.  What this means is that when
- * a child exits, the shell will be sent SIGCLD signals continuously
- * until is runs out of stack space, unless it does a wait call before
- * restoring the signal handler.  The code below takes advantage of
- * this (mis)feature by installing a signal handler for SIGCLD and
- * then checking to see whether it was called.  If there are any
- * children to be waited for, it will be.
- *
- * If neither SYSV nor BSD is defined, we don't implement nonblocking
- * waits at all.  In this case, the user will not be informed when
- * a background process until the next time she runs a real program
- * (as opposed to running a builtin command or just typing return),
- * and the jobs command may give out of date information.
- */
-
-#ifdef SYSV
-STATIC int gotsigchild;
-
-STATIC int onsigchild(void) {
-	gotsigchild = 1;
-}
-#endif
-
-
-STATIC int
-waitproc(int block, struct job *jp, int *status)
-{
-	(void)jp;
-
-#ifdef BSD
-	int flags = 0;
-
-#if JOBS
-	if (jp != NULL && jp->jobctl)
-		flags |= WUNTRACED;
-#endif
-	if (block == 0)
-		flags |= WNOHANG;
-	return wait3(status, flags, (struct rusage *)NULL);
-#else
-#ifdef SYSV
-	int (*save)();
-
-	if (block == 0) {
-		gotsigchild = 0;
-		save = signal(SIGCLD, onsigchild);
-		signal(SIGCLD, save);
-		if (gotsigchild == 0)
-			return 0;
-	}
-	return wait(status);
-#else
-	if (block == 0)
-		return 0;
-	return wait(status);
-#endif
-#endif
-}
-
-/*
- * return 1 if there are stopped jobs, otherwise 0
- */
-int job_warning = 0;
-int
-stoppedjobs(void)
-{
-	int jobno;
-	struct job *jp;
-
-	if (job_warning || jobs_invalid)
-		return (0);
-	for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
-		if (jp->used == 0)
-			continue;
-		if (jp->state == JOBSTOPPED) {
-			out2str("You have stopped jobs.\n");
-			job_warning = 2;
-			return (1);
-		}
-	}
-
-	return (0);
-}
-
-/*
- * Return a string identifying a command (to be printed by the
- * jobs command).
- */
-
-STATIC char *cmdnextc;
-STATIC int cmdnleft;
-
-void
-commandtext(struct procstat *ps, union node *n)
-{
-	int len;
-
-	cmdnextc = ps->cmd;
-	if (iflag || mflag || sizeof ps->cmd < 100)
-		len = sizeof(ps->cmd);
-	else
-		len = sizeof(ps->cmd) / 10;
-	cmdnleft = len;
-	cmdtxt(n);
-	if (cmdnleft <= 0) {
-		char *p = ps->cmd + len - 4;
-		p[0] = '.';
-		p[1] = '.';
-		p[2] = '.';
-		p[3] = 0;
-	} else
-		*cmdnextc = '\0';
-	TRACE(("commandtext: ps->cmd %x, end %x, left %d\n\t\"%s\"\n",
-		ps->cmd, cmdnextc, cmdnleft, ps->cmd));
-}
-
-
-STATIC void
-cmdtxt(union node *n)
-{
-	union node *np;
-	struct nodelist *lp;
-	const char *p;
-	int i;
-	char s[2];
-
-	if (n == NULL || cmdnleft <= 0)
-		return;
-	switch (n->type) {
-	case NSEMI:
-		cmdtxt(n->nbinary.ch1);
-		cmdputs("; ");
-		cmdtxt(n->nbinary.ch2);
-		break;
-	case NAND:
-		cmdtxt(n->nbinary.ch1);
-		cmdputs(" && ");
-		cmdtxt(n->nbinary.ch2);
-		break;
-	case NOR:
-		cmdtxt(n->nbinary.ch1);
-		cmdputs(" || ");
-		cmdtxt(n->nbinary.ch2);
-		break;
-	case NPIPE:
-		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
-			cmdtxt(lp->n);
-			if (lp->next)
-				cmdputs(" | ");
-		}
-		break;
-	case NSUBSHELL:
-		cmdputs("(");
-		cmdtxt(n->nredir.n);
-		cmdputs(")");
-		break;
-	case NREDIR:
-	case NBACKGND:
-		cmdtxt(n->nredir.n);
-		break;
-	case NIF:
-		cmdputs("if ");
-		cmdtxt(n->nif.test);
-		cmdputs("; then ");
-		cmdtxt(n->nif.ifpart);
-		if (n->nif.elsepart) {
-			cmdputs("; else ");
-			cmdtxt(n->nif.elsepart);
-		}
-		cmdputs("; fi");
-		break;
-	case NWHILE:
-		cmdputs("while ");
-		goto until;
-	case NUNTIL:
-		cmdputs("until ");
-until:
-		cmdtxt(n->nbinary.ch1);
-		cmdputs("; do ");
-		cmdtxt(n->nbinary.ch2);
-		cmdputs("; done");
-		break;
-	case NFOR:
-		cmdputs("for ");
-		cmdputs(n->nfor.var);
-		cmdputs(" in ");
-		cmdlist(n->nfor.args, 1);
-		cmdputs("; do ");
-		cmdtxt(n->nfor.body);
-		cmdputs("; done");
-		break;
-	case NCASE:
-		cmdputs("case ");
-		cmdputs(n->ncase.expr->narg.text);
-		cmdputs(" in ");
-		for (np = n->ncase.cases; np; np = np->nclist.next) {
-			cmdtxt(np->nclist.pattern);
-			cmdputs(") ");
-			cmdtxt(np->nclist.body);
-			cmdputs(";; ");
-		}
-		cmdputs("esac");
-		break;
-	case NDEFUN:
-		cmdputs(n->narg.text);
-		cmdputs("() { ... }");
-		break;
-	case NCMD:
-		cmdlist(n->ncmd.args, 1);
-		cmdlist(n->ncmd.redirect, 0);
-		break;
-	case NARG:
-		cmdputs(n->narg.text);
-		break;
-	case NTO:
-		p = ">";  i = 1;  goto redir;
-	case NCLOBBER:
-		p = ">|";  i = 1;  goto redir;
-	case NAPPEND:
-		p = ">>";  i = 1;  goto redir;
-	case NTOFD:
-		p = ">&";  i = 1;  goto redir;
-	case NFROM:
-		p = "<";  i = 0;  goto redir;
-	case NFROMFD:
-		p = "<&";  i = 0;  goto redir;
-	case NFROMTO:
-		p = "<>";  i = 0;  goto redir;
-redir:
-		if (n->nfile.fd != i) {
-			s[0] = n->nfile.fd + '0';
-			s[1] = '\0';
-			cmdputs(s);
-		}
-		cmdputs(p);
-		if (n->type == NTOFD || n->type == NFROMFD) {
-			s[0] = n->ndup.dupfd + '0';
-			s[1] = '\0';
-			cmdputs(s);
-		} else {
-			cmdtxt(n->nfile.fname);
-		}
-		break;
-	case NHERE:
-	case NXHERE:
-		cmdputs("<<...");
-		break;
-	default:
-		cmdputs("???");
-		break;
-	}
-}
-
-STATIC void
-cmdlist(union node *np, int sep)
-{
-	for (; np; np = np->narg.next) {
-		if (!sep)
-			cmdputs(" ");
-		cmdtxt(np);
-		if (sep && np->narg.next)
-			cmdputs(" ");
-	}
-}
-
-
-STATIC void
-cmdputs(const char *s)
-{
-	const char *p, *str = 0;
-	char c, cc[2] = " ";
-	char *nextc;
-	int nleft;
-	int subtype = 0;
-	int quoted = 0;
-	static char vstype[16][4] = { "", "}", "-", "+", "?", "=",
-					"#", "##", "%", "%%" };
-
-	p = s;
-	nextc = cmdnextc;
-	nleft = cmdnleft;
-	while (nleft > 0 && (c = *p++) != 0) {
-		switch (c) {
-		case CTLESC:
-			c = *p++;
-			break;
-		case CTLVAR:
-			subtype = *p++;
-			if ((subtype & VSTYPE) == VSLENGTH)
-				str = "${#";
-			else
-				str = "${";
-			if (!(subtype & VSQUOTE) != !(quoted & 1)) {
-				quoted ^= 1;
-				c = '"';
-			} else
-				c = *str++;
-			break;
-		case CTLENDVAR:
-			if (quoted & 1) {
-				c = '"';
-				str = "}";
-			} else
-				c = '}';
-			quoted >>= 1;
-			subtype = 0;
-			break;
-		case CTLBACKQ:
-			c = '$';
-			str = "(...)";
-			break;
-		case CTLBACKQ+CTLQUOTE:
-			c = '"';
-			str = "$(...)\"";
-			break;
-		case CTLARI:
-			c = '$';
-			str = "((";
-			break;
-		case CTLENDARI:
-			c = ')';
-			str = ")";
-			break;
-		case CTLQUOTEMARK:
-			quoted ^= 1;
-			c = '"';
-			break;
-		case '=':
-			if (subtype == 0)
-				break;
-			str = vstype[subtype & VSTYPE];
-			if (subtype & VSNUL)
-				c = ':';
-			else
-				c = *str++;
-			if (c != '}')
-				quoted <<= 1;
-			break;
-		case '\'':
-		case '\\':
-		case '"':
-		case '$':
-			/* These can only happen inside quotes */
-			cc[0] = c;
-			str = cc;
-			c = '\\';
-			break;
-		default:
-			break;
-		}
-		do {
-			*nextc++ = c;
-		} while (--nleft > 0 && str && (c = *str++));
-		str = 0;
-	}
-	if ((quoted & 1) && nleft) {
-		*nextc++ = '"';
-		nleft--;
-	}
-	cmdnleft = nleft;
-	cmdnextc = nextc;
-}
diff --git a/ash/mkbuiltins b/ash/mkbuiltins
deleted file mode 100644
index 0d66ef0..0000000
--- a/ash/mkbuiltins
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/bin/sh -
-#	$NetBSD: mkbuiltins,v 1.21 2004/06/06 07:03:11 christos Exp $
-#
-# Copyright (c) 1991, 1993
-#	The Regents of the University of California.  All rights reserved.
-#
-# This code is derived from software contributed to Berkeley by
-# Kenneth Almquist.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. Neither the name of the University nor the names of its contributors
-#    may be used to endorse or promote products derived from this software
-#    without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-#	@(#)mkbuiltins	8.2 (Berkeley) 5/4/95
-
-havehist=1
-if [ "X$1" = "X-h" ]; then
-	havehist=0
-	shift
-fi
-
-shell=$1
-builtins=$2
-objdir=$3
-
-havejobs=0
-if grep '^#define JOBS[	 ]*1' ${shell} > /dev/null
-then
-	havejobs=1
-fi
-
-exec <$builtins 3> ${objdir}/builtins.c 4> ${objdir}/builtins.h
-
-echo '/*
- * This file was generated by the mkbuiltins program.
- */
-
-#include "shell.h"
-#include "builtins.h"
-
-const struct builtincmd builtincmd[] = {
-' >&3
-
-echo '/*
- * This file was generated by the mkbuiltins program.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-
-struct builtincmd {
-      const char *name;
-      int (*builtin)(int, char **);
-};
-
-extern const struct builtincmd builtincmd[];
-extern const struct builtincmd splbltincmd[];
-
-' >&4
-
-specials=
-
-while read line
-do
-	set -- $line
-	[ -z "$1" ] && continue
-	case "$1" in
-	\#if*|\#def*|\#end*)
-		echo $line >&3
-		echo $line >&4
-		continue
-		;;
-	esac
-	l1="${line###}"
-	[ "$l1" != "$line" ] && continue
-
-
-	func=$1
-	shift
-	[ x"$1" = x'-j' ] && {
-		[ $havejobs = 0 ] && continue
-		shift
-	}
-	[ x"$1" = x'-h' ] && {
-		[ $havehist = 0 ] && continue
-		shift
-	}
-	echo 'int '"$func"'(int, char **);' >&4
-	while
-		[ $# != 0 -a "$1" != '#' ]
-	do
-		[ "$1" = '-s' ] && {
-			specials="$specials $2 $func"
-			shift 2
-			continue;
-		}
-		[ "$1" = '-u' ] && shift
-		echo '	{ "'$1'",	'"$func"' },' >&3
-		shift
-	done
-done
-
-echo '	{ 0, 0 },' >&3
-echo '};' >&3
-echo >&3
-echo 'const struct builtincmd splbltincmd[] = {' >&3
-
-set -- $specials
-while
-	[ $# != 0 ]
-do
-	echo '	{ "'$1'",	'"$2"' },' >&3
-	shift 2
-done
-
-echo '	{ 0, 0 },' >&3
-echo "};" >&3
diff --git a/ash/mkinit.sh b/ash/mkinit.sh
deleted file mode 100644
index 76819ad..0000000
--- a/ash/mkinit.sh
+++ /dev/null
@@ -1,195 +0,0 @@
-#! /bin/sh
-#	$NetBSD: mkinit.sh,v 1.1 2004/01/17 11:47:31 dsl Exp $
-
-# Copyright (c) 2003 The NetBSD Foundation, Inc.
-# All rights reserved.
-#
-# This code is derived from software contributed to The NetBSD Foundation
-# by David Laight.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. Neither the name of The NetBSD Foundation nor the names of its
-#    contributors may be used to endorse or promote products derived
-#    from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-
-srcs="$*"
-
-nl='
-'
-
-includes=' "shell.h" "mystring.h" "init.h" '
-defines=
-decles=
-event_init=
-event_reset=
-event_shellproc=
-
-for src in $srcs; do
-	exec <$src
-	decnl="$nl"
-	while IFS=; read -r line; do
-		[ "$line" = x ]
-		case "$line " in
-		INIT["{ 	"]* ) event=init;;
-		RESET["{ 	"]* ) event=reset;;
-		SHELLPROC["{ 	"]* ) event=shellproc;;
-		INCLUDE[\ \	]* )
-			IFS=' 	'
-			set -- $line
-			# ignore duplicates
-			[ "${includes}" != "${includes%* $2 }" ] && continue
-			includes="$includes$2 "
-			continue
-			;;
-		MKINIT\  )
-			# struct declaration
-			decles="$decles$nl"
-			while
-				read -r line
-				decles="${decles}${line}${nl}"
-				[ "$line" != "};" ]
-			do
-				:
-			done
-			decnl="$nl"
-			continue
-			;;
-		MKINIT["{ 	"]* )
-			# strip initialiser
-			def=${line#MKINIT}
-			comment="${def#*;}"
-			def="${def%;$comment}"
-			def="${def%%=*}"
-			def="${def% }"
-			decles="${decles}${decnl}extern${def};${comment}${nl}"
-			decnl=
-			continue
-			;;
-		\#define[\ \	]* )
-			IFS=' 	'
-			set -- $line
-			# Ignore those with arguments
-			[ "$2" = "${2##*(}" ] || continue
-			# and multiline definitions
-			[ "$line" = "${line%\\}" ] || continue
-			defines="${defines}#undef  $2${nl}${line}${nl}"
-			continue
-			;;
-		* ) continue;;
-		esac
-		# code for events
-		ev="${nl}      /* from $src: */${nl}      {${nl}"
-		while
-			read -r line
-			[ "$line" != "}" ]
-		do
-			# The C program indented by an extra 6 chars using
-			# tabs then spaces. I need to compare the output :-(
-			indent=6
-			while
-				l=${line#	}
-				[ "$l" != "$line" ]
-			do
-				indent=$(($indent + 8))
-				line="$l"
-			done
-			while
-				l=${line# }
-				[ "$l" != "$line" ]
-			do
-				indent=$(($indent + 1))
-				line="$l"
-			done
-			[ -z "$line" -o "$line" != "${line###}" ] && indent=0
-			while
-				[ $indent -ge 8 ]
-			do
-				ev="$ev	"
-				indent="$(($indent - 8))"
-			done
-			while
-				[ $indent -gt 0 ]
-			do
-				ev="$ev "
-				indent="$(($indent - 1))"
-			done
-			ev="${ev}${line}${nl}"
-		done
-		ev="${ev}      }${nl}"
-		eval event_$event=\"\$event_$event\$ev\"
-	done
-done
-
-exec >init.c.tmp
-
-echo "/*"
-echo " * This file was generated by the mkinit program."
-echo " */"
-echo
-
-IFS=' '
-for f in $includes; do
-	echo "#include $f"
-done
-
-echo
-echo
-echo
-echo "$defines"
-echo
-echo "$decles"
-echo
-echo
-echo "/*"
-echo " * Initialization code."
-echo " */"
-echo
-echo "void"
-echo "init() {"
-echo "${event_init%$nl}"
-echo "}"
-echo
-echo
-echo
-echo "/*"
-echo " * This routine is called when an error or an interrupt occurs in an"
-echo " * interactive shell and control is returned to the main command loop."
-echo " */"
-echo
-echo "void"
-echo "reset() {"
-echo "${event_reset%$nl}"
-echo "}"
-echo
-echo
-echo
-echo "/*"
-echo " * This routine is called to initialize the shell to run a shell procedure."
-echo " */"
-echo
-echo "void"
-echo "initshellproc() {"
-echo "${event_shellproc%$nl}"
-echo "}"
-
-exec >&-
-mv init.c.tmp init.c
diff --git a/ash/mknodes.sh b/ash/mknodes.sh
deleted file mode 100644
index 54d2e3d..0000000
--- a/ash/mknodes.sh
+++ /dev/null
@@ -1,217 +0,0 @@
-#! /bin/sh
-#	$NetBSD: mknodes.sh,v 1.1 2004/01/16 23:24:38 dsl Exp $
-
-# Copyright (c) 2003 The NetBSD Foundation, Inc.
-# All rights reserved.
-#
-# This code is derived from software contributed to The NetBSD Foundation
-# by David Laight.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. Neither the name of The NetBSD Foundation nor the names of its
-#    contributors may be used to endorse or promote products derived
-#    from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-
-nodetypes=$1
-nodes_pat=$2
-objdir="$3"
-
-exec <$nodetypes
-exec >$objdir/nodes.h.tmp
-
-echo "/*"
-echo " * This file was generated by mknodes.sh"
-echo " */"
-echo
-
-tagno=0
-while IFS=; read -r line; do
-	line="${line%%#*}"
-	IFS=' 	'
-	set -- $line
-	IFS=
-	[ -z "$2" ] && continue
-	case "$line" in
-	[" 	"]* )
-		IFS=' '
-		[ $field = 0 ] && struct_list="$struct_list $struct"
-		eval field_${struct}_$field=\"\$*\"
-		eval numfld_$struct=\$field
-		field=$(($field + 1))
-		;;
-	* )
-		define=$1
-		struct=$2
-		echo "#define $define $tagno"
-		tagno=$(($tagno + 1))
-		eval define_$struct=\"\$define_$struct \$define\"
-		struct_define="$struct_define $struct"
-		field=0
-		;;
-	esac
-done
-
-echo
-
-IFS=' '
-for struct in $struct_list; do
-	echo
-	echo
-	echo "struct $struct {"
-	field=0
-	while
-		eval line=\"\$field_${struct}_$field\"
-		field=$(($field + 1))
-		[ -n "$line" ]
-	do
-		IFS=' '
-		set -- $line
-		name=$1
-		case $2 in
-		nodeptr ) type="union node *";;
-		nodelist ) type="struct nodelist *";;
-		string ) type="char *";;
-		int ) type="int ";;
-		* ) name=; shift 2; type="$*";;
-		esac
-		echo "      $type$name;"
-	done
-	echo "};"
-done
-
-echo
-echo
-echo "union node {"
-echo "      int type;"
-for struct in $struct_list; do
-	echo "      struct $struct $struct;"
-done
-echo "};"
-echo
-echo
-echo "struct nodelist {"
-echo "	struct nodelist *next;"
-echo "	union node *n;"
-echo "};"
-echo
-echo
-echo "union node *copyfunc(union node *);"
-echo "void freefunc(union node *);"
-
-mv $objdir/nodes.h.tmp $objdir/nodes.h || exit 1
-
-exec <$nodes_pat
-exec >$objdir/nodes.c.tmp
-
-echo "/*"
-echo " * This file was generated by mknodes.sh"
-echo " */"
-echo
-
-while IFS=; read -r line; do
-	IFS=' 	'
-	set -- $line
-	IFS=
-	case "$1" in
-	'%SIZES' )
-		echo "static const short nodesize[$tagno] = {"
-		IFS=' '
-		for struct in $struct_define; do
-			echo "      SHELL_ALIGN(sizeof (struct $struct)),"
-		done
-		echo "};"
-		;;
-	'%CALCSIZE' )
-		echo "      if (n == NULL)"
-		echo "	    return;"
-		echo "      funcblocksize += nodesize[n->type];"
-		echo "      switch (n->type) {"
-		IFS=' '
-		for struct in $struct_list; do
-			eval defines=\"\$define_$struct\"
-			for define in $defines; do
-				echo "      case $define:"
-			done
-			eval field=\$numfld_$struct
-			while
-				[ $field != 0 ]
-			do
-				eval line=\"\$field_${struct}_$field\"
-				field=$(($field - 1))
-				IFS=' '
-				set -- $line
-				name=$1
-				cl=")"
-				case $2 in
-				nodeptr ) fn=calcsize;;
-				nodelist ) fn=sizenodelist;;
-				string ) fn="funcstringsize += strlen"
-					cl=") + 1";;
-				* ) continue;;
-				esac
-				echo "	    ${fn}(n->$struct.$name${cl};"
-			done
-			echo "	    break;"
-		done
-		echo "      };"
-		;;
-	'%COPY' )
-		echo "      if (n == NULL)"
-		echo "	    return NULL;"
-		echo "      new = funcblock;"
-		echo "      funcblock = (char *) funcblock + nodesize[n->type];"
-		echo "      switch (n->type) {"
-		IFS=' '
-		for struct in $struct_list; do
-			eval defines=\"\$define_$struct\"
-			for define in $defines; do
-				echo "      case $define:"
-			done
-			eval field=\$numfld_$struct
-			while
-				[ $field != 0 ]
-			do
-				eval line=\"\$field_${struct}_$field\"
-				field=$(($field - 1))
-				IFS=' '
-				set -- $line
-				name=$1
-				case $2 in
-				nodeptr ) fn="copynode(";;
-				nodelist ) fn="copynodelist(";;
-				string ) fn="nodesavestr(";;
-				int ) fn=;;
-				* ) continue;;
-				esac
-				f="$struct.$name"
-				echo "	    new->$f = ${fn}n->$f${fn:+)};"
-			done
-			echo "	    break;"
-		done
-		echo "      };"
-		echo "      new->type = n->type;"
-		;;
-	* ) echo "$line";;
-	esac
-done
-
-mv $objdir/nodes.c.tmp $objdir/nodes.c || exit 1
diff --git a/ash/mt b/ash/mt
deleted file mode 100644
index f07a32e..0000000
--- a/ash/mt
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/bin/sh -
-#
-# Copyright (c) 1991 The Regents of the University of California.
-# All rights reserved.
-#
-# This code is derived from software contributed to Berkeley by
-# Kenneth Almquist.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#	This product includes software developed by the University of
-#	California, Berkeley and its contributors.
-# 4. Neither the name of the University nor the names of its contributors
-#    may be used to endorse or promote products derived from this software
-#    without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-#	@(#)mktokens	5.1 (Berkeley) 3/7/91
-#
-#	/b/source/CVS/src/bin/sh/mt,v 1.3 1993/03/23 00:28:48 cgd Exp
-
-# The following is a list of tokens.  The second column is nonzero if the
-# token marks the end of a list.  The third column is the name to print in
-# error messages.
-
-cat > /tmp/ka$$ <<\!
-TEOF	1	end of file
-TNL	0	newline
-TSEMI	0	";"
-TBACKGND 0	"&"
-TAND	0	"&&"
-TOR	0	"||"
-TPIPE	0	"|"
-TLP	0	"("
-TRP	1	")"
-TENDCASE 1	";;"
-TENDBQUOTE 1	"`"
-TREDIR	0	redirection
-TWORD	0	word
-TIF	0	"if"
-TTHEN	1	"then"
-TELSE	1	"else"
-TELIF	1	"elif"
-TFI	1	"fi"
-TWHILE	0	"while"
-TUNTIL	0	"until"
-TFOR	0	"for"
-TDO	1	"do"
-TDONE	1	"done"
-TBEGIN	0	"{"
-TEND	1	"}"
-TCASE	0	"case"
-TESAC	1	"esac"
-!
-nl=`wc -l /tmp/ka$$`
-exec 
-awk "-F "  '{print $1 "#define " NR-1}' /tmp/ka$$
-
-rm /tmp/ka$$
diff --git a/ash/options.h b/ash/options.h
deleted file mode 100644
index 4cc7dbe..0000000
--- a/ash/options.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*	$NetBSD: options.h,v 1.17 2003/08/07 09:05:36 agc Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- *	@(#)options.h	8.2 (Berkeley) 5/4/95
- */
-
-struct shparam {
-	int nparam;		/* # of positional parameters (without $0) */
-	unsigned char malloc;	/* if parameter list dynamically allocated */
-	unsigned char reset;	/* if getopts has been reset */
-	char **p;		/* parameter list */
-	char **optnext;		/* next parameter to be processed by getopts */
-	char *optptr;		/* used by getopts */
-};
-
-
-struct optent {
-	const char *name;		/* for set -o <name> */
-	const char letter;		/* set [+/-]<letter> and $- */
-	const char opt_set;		/* mutually exclusive option set */
-	char val;			/* value of <letter>flag */
-};
-
-/* Those marked [U] are required by posix, but have no effect! */
-
-#ifdef DEFINE_OPTIONS
-#define DEF_OPTS(name, letter, opt_set) {name, letter, opt_set, 0},
-struct optent optlist[] = {
-#else
-#define DEF_OPTS(name, letter, opt_set)
-#endif
-#define DEF_OPT(name,letter) DEF_OPTS(name, letter, 0)
-
-DEF_OPT( "errexit",	'e' )	/* exit on error */
-#define eflag optlist[0].val
-DEF_OPT( "noglob",	'f' )	/* no pathname expansion */
-#define fflag optlist[1].val
-DEF_OPT( "ignoreeof",	'I' )	/* do not exit on EOF */
-#define Iflag optlist[2].val
-DEF_OPT( "interactive",'i' )	/* interactive shell */
-#define iflag optlist[3].val
-DEF_OPT( "monitor",	'm' )	/* job control */
-#define mflag optlist[4].val
-DEF_OPT( "noexec",	'n' )	/* [U] do not exec commands */
-#define nflag optlist[5].val
-DEF_OPT( "stdin",	's' )	/* read from stdin */
-#define sflag optlist[6].val
-DEF_OPT( "xtrace",	'x' )	/* trace after expansion */
-#define xflag optlist[7].val
-DEF_OPT( "verbose",	'v' )	/* trace read input */
-#define vflag optlist[8].val
-DEF_OPTS( "vi",		'V', 'V' )	/* vi style editing */
-#define Vflag optlist[9].val
-DEF_OPTS( "emacs",	'E', 'V' )	/* emacs style editing */
-#define	Eflag optlist[10].val
-DEF_OPT( "noclobber",	'C' )	/* do not overwrite files with > */
-#define	Cflag optlist[11].val
-DEF_OPT( "allexport",	'a' )	/* export all variables */
-#define	aflag optlist[12].val
-DEF_OPT( "notify",	'b' )	/* [U] report completion of background jobs */
-#define	bflag optlist[13].val
-DEF_OPT( "nounset",	'u' )	/* error expansion of unset variables */
-#define	uflag optlist[14].val
-DEF_OPT( "quietprofile", 'q' )
-#define	qflag optlist[15].val
-DEF_OPT( "nolog",	0 )	/* [U] no functon defs in command history */
-#define	nolog optlist[16].val
-DEF_OPT( "cdprint",	0 )	/* always print result of cd */
-#define	cdprint optlist[17].val
-#ifdef DEBUG
-DEF_OPT( "debug",	0 )	/* enable debug prints */
-#define	debug optlist[18].val
-#endif
-
-#ifdef DEFINE_OPTIONS
-	{ 0, 0, 0, 0 },
-};
-#define NOPTS (sizeof optlist / sizeof optlist[0] - 1)
-int sizeof_optlist = sizeof optlist;
-#else
-extern struct optent optlist[];
-extern int sizeof_optlist;
-#endif
-
-
-extern char *minusc;		/* argument to -c option */
-extern char *arg0;		/* $0 */
-extern struct shparam shellparam;  /* $@ */
-extern char **argptr;		/* argument list for builtin commands */
-extern char *optionarg;		/* set by nextopt */
-extern char *optptr;		/* used by nextopt */
-
-void procargs(int, char **);
-void optschanged(void);
-void setparam(char **);
-void freeparam(volatile struct shparam *);
-int shiftcmd(int, char **);
-int setcmd(int, char **);
-int getoptscmd(int, char **);
-int nextopt(const char *);
-void getoptsreset(const char *);
diff --git a/ash/output.c b/ash/output.c
deleted file mode 100644
index d18dc3a..0000000
--- a/ash/output.c
+++ /dev/null
@@ -1,522 +0,0 @@
-/*	$NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)output.c	8.2 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc Exp $");
-#endif
-#endif /* not lint */
-
-/*
- * Shell output routines.  We use our own output routines because:
- *	When a builtin command is interrupted we have to discard
- *		any pending output.
- *	When a builtin command appears in back quotes, we want to
- *		save the output of the command in a region obtained
- *		via malloc, rather than doing a fork and reading the
- *		output of the command via a pipe.
- *	Our output routines may be smaller than the stdio routines.
- */
-
-#include <sys/types.h>		/* quad_t */
-#include <sys/param.h>		/* BSD4_4 */
-#include <sys/ioctl.h>
-
-#include <stdio.h>	/* defines BUFSIZ */
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#include "shell.h"
-#include "syntax.h"
-#include "output.h"
-#include "memalloc.h"
-#include "error.h"
-
-#define OUTBUFSIZ BUFSIZ
-#define BLOCK_OUT -2		/* output to a fixed block of memory */
-#define MEM_OUT -3		/* output to dynamically allocated memory */
-#define OUTPUT_ERR 01		/* error occurred on output */
-
-
-struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
-struct output errout = {NULL, 0, NULL, 100, 2, 0};
-struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
-struct output *out1 = &output;
-struct output *out2 = &errout;
-
-
-
-#ifdef mkinit
-
-INCLUDE "output.h"
-INCLUDE "memalloc.h"
-
-RESET {
-	out1 = &output;
-	out2 = &errout;
-	if (memout.buf != NULL) {
-		ckfree(memout.buf);
-		memout.buf = NULL;
-	}
-}
-
-#endif
-
-
-#ifdef notdef	/* no longer used */
-/*
- * Set up an output file to write to memory rather than a file.
- */
-
-void
-open_mem(char *block, int length, struct output *file)
-{
-	file->nextc = block;
-	file->nleft = --length;
-	file->fd = BLOCK_OUT;
-	file->flags = 0;
-}
-#endif
-
-
-void
-out1str(const char *p)
-{
-	outstr(p, out1);
-}
-
-
-void
-out2str(const char *p)
-{
-	outstr(p, out2);
-}
-
-
-void
-outstr(const char *p, struct output *file)
-{
-	while (*p)
-		outc(*p++, file);
-	if (file == out2)
-		flushout(file);
-}
-
-
-char out_junk[16];
-
-
-void
-emptyoutbuf(struct output *dest)
-{
-	int offset;
-
-	if (dest->fd == BLOCK_OUT) {
-		dest->nextc = out_junk;
-		dest->nleft = sizeof out_junk;
-		dest->flags |= OUTPUT_ERR;
-	} else if (dest->buf == NULL) {
-		INTOFF;
-		dest->buf = ckmalloc(dest->bufsize);
-		dest->nextc = dest->buf;
-		dest->nleft = dest->bufsize;
-		INTON;
-	} else if (dest->fd == MEM_OUT) {
-		offset = dest->bufsize;
-		INTOFF;
-		dest->bufsize <<= 1;
-		dest->buf = ckrealloc(dest->buf, dest->bufsize);
-		dest->nleft = dest->bufsize - offset;
-		dest->nextc = dest->buf + offset;
-		INTON;
-	} else {
-		flushout(dest);
-	}
-	dest->nleft--;
-}
-
-
-void
-flushall(void)
-{
-	flushout(&output);
-	flushout(&errout);
-}
-
-
-void
-flushout(struct output *dest)
-{
-
-	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
-		return;
-	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
-		dest->flags |= OUTPUT_ERR;
-	dest->nextc = dest->buf;
-	dest->nleft = dest->bufsize;
-}
-
-
-void
-freestdout(void)
-{
-	INTOFF;
-	if (output.buf) {
-		ckfree(output.buf);
-		output.buf = NULL;
-		output.nleft = 0;
-	}
-	INTON;
-}
-
-
-void
-outfmt(struct output *file, const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	doformat(file, fmt, ap);
-	va_end(ap);
-}
-
-
-void
-out1fmt(const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	doformat(out1, fmt, ap);
-	va_end(ap);
-}
-
-#ifndef __linux__
-void
-dprintf(const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	doformat(out2, fmt, ap);
-	va_end(ap);
-	flushout(out2);
-}
-#endif
-
-void
-fmtstr(char *outbuf, size_t length, const char *fmt, ...)
-{
-	va_list ap;
-	struct output strout;
-
-	va_start(ap, fmt);
-	strout.nextc = outbuf;
-	strout.nleft = length;
-	strout.fd = BLOCK_OUT;
-	strout.flags = 0;
-	doformat(&strout, fmt, ap);
-	outc('\0', &strout);
-	if (strout.flags & OUTPUT_ERR)
-		outbuf[length - 1] = '\0';
-	va_end(ap);
-}
-
-/*
- * Formatted output.  This routine handles a subset of the printf formats:
- * - Formats supported: d, u, o, p, X, s, and c.
- * - The x format is also accepted but is treated like X.
- * - The l, ll and q modifiers are accepted.
- * - The - and # flags are accepted; # only works with the o format.
- * - Width and precision may be specified with any format except c.
- * - An * may be given for the width or precision.
- * - The obsolete practice of preceding the width with a zero to get
- *   zero padding is not supported; use the precision field.
- * - A % may be printed by writing %% in the format string.
- */
-
-#define TEMPSIZE 24
-
-#ifdef BSD4_4
-#define HAVE_VASPRINTF 1
-#endif
-
-void
-doformat(struct output *dest, const char *f, va_list ap)
-{
-#if	HAVE_VASPRINTF
-	char *s;
-
-	vasprintf(&s, f, ap);
-	outstr(s, dest);
-	free(s);     
-#else	/* !HAVE_VASPRINTF */
-	static const char digit[] = "0123456789ABCDEF";
-	char c;
-	char temp[TEMPSIZE];
-	int flushleft;
-	int sharp;
-	int width;
-	int prec;
-	int islong;
-	int isquad;
-	char *p;
-	int sign;
-#ifdef BSD4_4
-	quad_t l;
-	u_quad_t num;
-#else
-	long l;
-	u_long num;
-#endif
-	unsigned base;
-	int len;
-	int size;
-	int pad;
-
-	while ((c = *f++) != '\0') {
-		if (c != '%') {
-			outc(c, dest);
-			continue;
-		}
-		flushleft = 0;
-		sharp = 0;
-		width = 0;
-		prec = -1;
-		islong = 0;
-		isquad = 0;
-		for (;;) {
-			if (*f == '-')
-				flushleft++;
-			else if (*f == '#')
-				sharp++;
-			else
-				break;
-			f++;
-		}
-		if (*f == '*') {
-			width = va_arg(ap, int);
-			f++;
-		} else {
-			while (is_digit(*f)) {
-				width = 10 * width + digit_val(*f++);
-			}
-		}
-		if (*f == '.') {
-			if (*++f == '*') {
-				prec = va_arg(ap, int);
-				f++;
-			} else {
-				prec = 0;
-				while (is_digit(*f)) {
-					prec = 10 * prec + digit_val(*f++);
-				}
-			}
-		}
-		if (*f == 'l') {
-			f++;
-			if (*f == 'l') {
-				isquad++;
-				f++;
-			} else
-				islong++;
-		} else if (*f == 'q') {
-			isquad++;
-			f++;
-		}
-		switch (*f) {
-		case 'd':
-#ifdef BSD4_4
-			if (isquad)
-				l = va_arg(ap, quad_t);
-			else
-#endif
-			if (islong)
-				l = va_arg(ap, long);
-			else
-				l = va_arg(ap, int);
-			sign = 0;
-			num = l;
-			if (l < 0) {
-				num = -l;
-				sign = 1;
-			}
-			base = 10;
-			goto number;
-		case 'u':
-			base = 10;
-			goto uns_number;
-		case 'o':
-			base = 8;
-			goto uns_number;
-		case 'p':
-			outc('0', dest);
-			outc('x', dest);
-			/*FALLTHROUGH*/
-		case 'x':
-			/* we don't implement 'x'; treat like 'X' */
-		case 'X':
-			base = 16;
-uns_number:	  /* an unsigned number */
-			sign = 0;
-#ifdef BSD4_4
-			if (isquad)
-				num = va_arg(ap, u_quad_t);
-			else
-#endif
-			if (islong)
-				num = va_arg(ap, unsigned long);
-			else
-				num = va_arg(ap, unsigned int);
-number:		  /* process a number */
-			p = temp + TEMPSIZE - 1;
-			*p = '\0';
-			while (num) {
-				*--p = digit[num % base];
-				num /= base;
-			}
-			len = (temp + TEMPSIZE - 1) - p;
-			if (prec < 0)
-				prec = 1;
-			if (sharp && *f == 'o' && prec <= len)
-				prec = len + 1;
-			pad = 0;
-			if (width) {
-				size = len;
-				if (size < prec)
-					size = prec;
-				size += sign;
-				pad = width - size;
-				if (flushleft == 0) {
-					while (--pad >= 0)
-						outc(' ', dest);
-				}
-			}
-			if (sign)
-				outc('-', dest);
-			prec -= len;
-			while (--prec >= 0)
-				outc('0', dest);
-			while (*p)
-				outc(*p++, dest);
-			while (--pad >= 0)
-				outc(' ', dest);
-			break;
-		case 's':
-			p = va_arg(ap, char *);
-			pad = 0;
-			if (width) {
-				len = strlen(p);
-				if (prec >= 0 && len > prec)
-					len = prec;
-				pad = width - len;
-				if (flushleft == 0) {
-					while (--pad >= 0)
-						outc(' ', dest);
-				}
-			}
-			prec++;
-			while (--prec != 0 && *p)
-				outc(*p++, dest);
-			while (--pad >= 0)
-				outc(' ', dest);
-			break;
-		case 'c':
-			c = va_arg(ap, int);
-			outc(c, dest);
-			break;
-		default:
-			outc(*f, dest);
-			break;
-		}
-		f++;
-	}
-#endif	/* !HAVE_VASPRINTF */
-}
-
-
-
-/*
- * Version of write which resumes after a signal is caught.
- */
-
-int
-xwrite(int fd, char *buf, int nbytes)
-{
-	int ntry;
-	int i;
-	int n;
-
-	n = nbytes;
-	ntry = 0;
-	for (;;) {
-		i = write(fd, buf, n);
-		if (i > 0) {
-			if ((n -= i) <= 0)
-				return nbytes;
-			buf += i;
-			ntry = 0;
-		} else if (i == 0) {
-			if (++ntry > 10)
-				return nbytes - n;
-		} else if (errno != EINTR) {
-			return -1;
-		}
-	}
-}
-
-
-/*
- * Version of ioctl that retries after a signal is caught.
- * XXX unused function
- */
-
-int
-xioctl(int fd, unsigned long request, char *arg)
-{
-	int i;
-
-	while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
-	return i;
-}
diff --git a/ash/redir.c b/ash/redir.c
deleted file mode 100644
index e9c085c..0000000
--- a/ash/redir.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/*	$NetBSD: redir.c,v 1.28 2003/08/07 09:05:37 agc Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)redir.c	8.2 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: redir.c,v 1.28 2003/08/07 09:05:37 agc Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/types.h>
-#include <sys/param.h>	/* PIPE_BUF */
-#include <signal.h>
-#include <string.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-/*
- * Code for dealing with input/output redirection.
- */
-
-#include "main.h"
-#include "shell.h"
-#include "nodes.h"
-#include "jobs.h"
-#include "options.h"
-#include "expand.h"
-#include "redir.h"
-#include "output.h"
-#include "memalloc.h"
-#include "error.h"
-
-
-#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
-
-
-MKINIT
-struct redirtab {
-	struct redirtab *next;
-	short renamed[10];
-};
-
-
-MKINIT struct redirtab *redirlist;
-
-/*
- * We keep track of whether or not fd0 has been redirected.  This is for
- * background commands, where we want to redirect fd0 to /dev/null only
- * if it hasn't already been redirected.
-*/
-int fd0_redirected = 0;
-
-STATIC void openredirect(union node *, char[10 ]);
-STATIC int openhere(union node *);
-
-
-/*
- * 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.
- */
-
-void
-redirect(union node *redir, int flags)
-{
-	union node *n;
-	struct redirtab *sv = NULL;
-	int i;
-	int fd;
-	int try;
-	char memory[10];	/* file descriptors to write to memory */
-
-	for (i = 10 ; --i >= 0 ; )
-		memory[i] = 0;
-	memory[1] = flags & REDIR_BACKQ;
-	if (flags & REDIR_PUSH) {
-		/* We don't have to worry about REDIR_VFORK here, as
-		 * flags & REDIR_PUSH is never true if REDIR_VFORK is set.
-		 */
-		sv = ckmalloc(sizeof (struct redirtab));
-		for (i = 0 ; i < 10 ; i++)
-			sv->renamed[i] = EMPTY;
-		sv->next = redirlist;
-		redirlist = sv;
-	}
-	for (n = redir ; n ; n = n->nfile.next) {
-		fd = n->nfile.fd;
-		try = 0;
-		if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
-		    n->ndup.dupfd == fd)
-			continue; /* redirect from/to same file descriptor */
-
-		if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
-			INTOFF;
-again:
-			if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
-				switch (errno) {
-				case EBADF:
-					if (!try) {
-						openredirect(n, memory);
-						try++;
-						goto again;
-					}
-					/* FALLTHROUGH*/
-				default:
-					INTON;
-					error("%d: %s", fd, strerror(errno));
-					/* NOTREACHED */
-				}
-			}
-			if (!try) {
-				sv->renamed[fd] = i;
-				close(fd);
-			}
-			INTON;
-		} else {
-			close(fd);
-		}
-                if (fd == 0)
-                        fd0_redirected++;
-		if (!try)
-			openredirect(n, memory);
-	}
-	if (memory[1])
-		out1 = &memout;
-	if (memory[2])
-		out2 = &memout;
-}
-
-
-STATIC void
-openredirect(union node *redir, char memory[10])
-{
-	int fd = redir->nfile.fd;
-	char *fname;
-	int f;
-	int flags = O_WRONLY|O_CREAT|O_TRUNC;
-
-	/*
-	 * We suppress interrupts so that we won't leave open file
-	 * descriptors around.  This may not be such a good idea because
-	 * an open of a device or a fifo can block indefinitely.
-	 */
-	INTOFF;
-	memory[fd] = 0;
-	switch (redir->nfile.type) {
-	case NFROM:
-		fname = redir->nfile.expfname;
-		if ((f = open(fname, O_RDONLY)) < 0)
-			goto eopen;
-		break;
-	case NFROMTO:
-		fname = redir->nfile.expfname;
-		if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
-			goto ecreate;
-		break;
-	case NTO:
-		if (Cflag)
-			flags |= O_EXCL;
-		/* FALLTHROUGH */
-	case NCLOBBER:
-		fname = redir->nfile.expfname;
-		if ((f = open(fname, flags, 0666)) < 0)
-			goto ecreate;
-		break;
-	case NAPPEND:
-		fname = redir->nfile.expfname;
-		if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
-			goto ecreate;
-		break;
-	case NTOFD:
-	case NFROMFD:
-		if (redir->ndup.dupfd >= 0) {	/* if not ">&-" */
-			if (memory[redir->ndup.dupfd])
-				memory[fd] = 1;
-			else
-				copyfd(redir->ndup.dupfd, fd);
-		}
-		INTON;
-		return;
-	case NHERE:
-	case NXHERE:
-		f = openhere(redir);
-		break;
-	default:
-		abort();
-	}
-
-	if (f != fd) {
-		copyfd(f, fd);
-		close(f);
-	}
-	INTON;
-	return;
-ecreate:
-	error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
-eopen:
-	error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
-}
-
-
-/*
- * 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];
-	int len = 0;
-
-	if (pipe(pip) < 0)
-		error("Pipe call failed");
-	if (redir->type == NHERE) {
-		len = strlen(redir->nhere.doc->narg.text);
-		if (len <= PIPESIZE) {
-			xwrite(pip[1], redir->nhere.doc->narg.text, len);
-			goto out;
-		}
-	}
-	if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
-		close(pip[0]);
-		bsd_signal(SIGINT, SIG_IGN);
-		bsd_signal(SIGQUIT, SIG_IGN);
-		bsd_signal(SIGHUP, SIG_IGN);
-#ifdef SIGTSTP
-		bsd_signal(SIGTSTP, SIG_IGN);
-#endif
-		bsd_signal(SIGPIPE, SIG_DFL);
-		if (redir->type == NHERE)
-			xwrite(pip[1], redir->nhere.doc->narg.text, len);
-		else
-			expandhere(redir->nhere.doc, pip[1]);
-		_exit(0);
-	}
-out:
-	close(pip[1]);
-	return pip[0];
-}
-
-
-
-/*
- * Undo the effects of the last redirection.
- */
-
-void
-popredir(void)
-{
-	struct redirtab *rp = redirlist;
-	int i;
-
-	for (i = 0 ; i < 10 ; i++) {
-		if (rp->renamed[i] != EMPTY) {
-                        if (i == 0)
-                                fd0_redirected--;
-			close(i);
-			if (rp->renamed[i] >= 0) {
-				copyfd(rp->renamed[i], i);
-				close(rp->renamed[i]);
-			}
-		}
-	}
-	INTOFF;
-	redirlist = rp->next;
-	ckfree(rp);
-	INTON;
-}
-
-/*
- * Undo all redirections.  Called on error or interrupt.
- */
-
-#ifdef mkinit
-
-INCLUDE "redir.h"
-
-RESET {
-	while (redirlist)
-		popredir();
-}
-
-SHELLPROC {
-	clearredir(0);
-}
-
-#endif
-
-/* Return true if fd 0 has already been redirected at least once.  */
-int
-fd0_redirected_p () {
-        return fd0_redirected != 0;
-}
-
-/*
- * Discard all saved file descriptors.
- */
-
-void
-clearredir(vforked)
-	int vforked;
-{
-	struct redirtab *rp;
-	int i;
-
-	for (rp = redirlist ; rp ; rp = rp->next) {
-		for (i = 0 ; i < 10 ; i++) {
-			if (rp->renamed[i] >= 0) {
-				close(rp->renamed[i]);
-			}
-			if (!vforked)
-				rp->renamed[i] = EMPTY;
-		}
-	}
-}
-
-
-
-/*
- * 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.
- */
-
-int
-copyfd(int from, int to)
-{
-	int newfd;
-	
-	newfd = dup2(from, to);
-	if (newfd < 0) {
-		if (errno == EMFILE)
-			return EMPTY;
-		else
-			error("%d: %s", from, strerror(errno));
-	}
-	return newfd;
-}
diff --git a/ash/syntax.c b/ash/syntax.c
deleted file mode 100644
index 7024f16..0000000
--- a/ash/syntax.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*	$NetBSD: syntax.c,v 1.1 2004/01/17 17:38:12 dsl Exp $	*/
-
-#include "shell.h"
-#include "syntax.h"
-#include "parser.h"
-
-#if CWORD != 0
-#error initialisation assumes 'CWORD' is zero
-#endif
-
-#define ndx(ch) (ch + 1 - CHAR_MIN)
-#define set(ch, val) [ndx(ch)] = val,
-#define set_range(s, e, val) [ndx(s) ... ndx(e)] = val,
-
-/* syntax table used when not in quotes */
-const char basesyntax[257] = { CEOF,
-    set_range(CTL_FIRST, CTL_LAST, CCTL)
-    set('\n', CNL)
-    set('\\', CBACK)
-    set('\'', CSQUOTE)
-    set('"', CDQUOTE)
-    set('`', CBQUOTE)
-    set('$', CVAR)
-    set('}', CENDVAR)
-    set('<', CSPCL)
-    set('>', CSPCL)
-    set('(', CSPCL)
-    set(')', CSPCL)
-    set(';', CSPCL)
-    set('&', CSPCL)
-    set('|', CSPCL)
-    set(' ', CSPCL)
-    set('\t', CSPCL)
-};
-
-/* syntax table used when in double quotes */
-const char dqsyntax[257] = { CEOF,
-    set_range(CTL_FIRST, CTL_LAST, CCTL)
-    set('\n', CNL)
-    set('\\', CBACK)
-    set('"', CDQUOTE)
-    set('`', CBQUOTE)
-    set('$', CVAR)
-    set('}', CENDVAR)
-    /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */
-    set('!', CCTL)
-    set('*', CCTL)
-    set('?', CCTL)
-    set('[', CCTL)
-    set('=', CCTL)
-    set('~', CCTL)
-    set(':', CCTL)
-    set('/', CCTL)
-    set('-', CCTL)
-};
-
-/* syntax table used when in single quotes */
-const char sqsyntax[257] = { CEOF,
-    set_range(CTL_FIRST, CTL_LAST, CCTL)
-    set('\n', CNL)
-    set('\'', CSQUOTE)
-    /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */
-    set('!', CCTL)
-    set('*', CCTL)
-    set('?', CCTL)
-    set('[', CCTL)
-    set('=', CCTL)
-    set('~', CCTL)
-    set(':', CCTL)
-    set('/', CCTL)
-    set('-', CCTL)
-};
-
-/* syntax table used when in arithmetic */
-const char arisyntax[257] = { CEOF,
-    set_range(CTL_FIRST, CTL_LAST, CCTL)
-    set('\n', CNL)
-    set('\\', CBACK)
-    set('`', CBQUOTE)
-    set('\'', CSQUOTE)
-    set('"', CDQUOTE)
-    set('$', CVAR)
-    set('}', CENDVAR)
-    set('(', CLP)
-    set(')', CRP)
-};
-
-/* character classification table */
-const char is_type[257] = { 0,
-    set_range('0', '9', ISDIGIT)
-    set_range('a', 'z', ISLOWER)
-    set_range('A', 'Z', ISUPPER)
-    set('_', ISUNDER)
-    set('#', ISSPECL)
-    set('?', ISSPECL)
-    set('$', ISSPECL)
-    set('!', ISSPECL)
-    set('-', ISSPECL)
-    set('*', ISSPECL)
-    set('@', ISSPECL)
-};
diff --git a/ash/syntax.h b/ash/syntax.h
deleted file mode 100644
index 1930592..0000000
--- a/ash/syntax.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*	$NetBSD: syntax.h,v 1.2 2004/01/17 17:38:12 dsl Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#include <ctype.h>
-
-/* Syntax classes */
-#define CWORD 0			/* character is nothing special */
-#define CNL 1			/* newline character */
-#define CBACK 2			/* a backslash character */
-#define CSQUOTE 3		/* single quote */
-#define CDQUOTE 4		/* double quote */
-#define CBQUOTE 5		/* backwards single quote */
-#define CVAR 6			/* a dollar sign */
-#define CENDVAR 7		/* a '}' character */
-#define CLP 8			/* a left paren in arithmetic */
-#define CRP 9			/* a right paren in arithmetic */
-#define CEOF 10			/* end of file */
-#define CCTL 11			/* like CWORD, except it must be escaped */
-#define CSPCL 12		/* these terminate a word */
-
-/* Syntax classes for is_ functions */
-#define ISDIGIT 01		/* a digit */
-#define ISUPPER 02		/* an upper case letter */
-#define ISLOWER 04		/* a lower case letter */
-#define ISUNDER 010		/* an underscore */
-#define ISSPECL 020		/* the name of a special parameter */
-
-#define PEOF (CHAR_MIN - 1)
-#define SYNBASE (-PEOF)
-/* XXX UPEOF is CHAR_MAX, so is a valid 'char' value... */
-#define UPEOF ((char)PEOF)
-
-
-#define BASESYNTAX (basesyntax + SYNBASE)
-#define DQSYNTAX (dqsyntax + SYNBASE)
-#define SQSYNTAX (sqsyntax + SYNBASE)
-#define ARISYNTAX (arisyntax + SYNBASE)
-
-/* These defines assume that the digits are contiguous */
-#define is_digit(c)	((unsigned)((c) - '0') <= 9)
-#define is_alpha(c)	(((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && isalpha((unsigned char)(c)))
-#define is_name(c)	(((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && ((c) == '_' || isalpha((unsigned char)(c))))
-#define is_in_name(c)	(((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && ((c) == '_' || isalnum((unsigned char)(c))))
-#define is_special(c)	((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))
-#define digit_val(c)	((c) - '0')
-
-extern const char basesyntax[];
-extern const char dqsyntax[];
-extern const char sqsyntax[];
-extern const char arisyntax[];
-extern const char is_type[];
diff --git a/ash/token.def b/ash/token.def
deleted file mode 100644
index 7644cb9..0000000
--- a/ash/token.def
+++ /dev/null
@@ -1,108 +0,0 @@
-#define TEOF 0
-#define TNL 1
-#define TSEMI 2
-#define TBACKGND 3
-#define TAND 4
-#define TOR 5
-#define TPIPE 6
-#define TLP 7
-#define TRP 8
-#define TENDCASE 9
-#define TENDBQUOTE 10
-#define TREDIR 11
-#define TWORD 12
-#define TIF 13
-#define TTHEN 14
-#define TELSE 15
-#define TELIF 16
-#define TFI 17
-#define TWHILE 18
-#define TUNTIL 19
-#define TFOR 20
-#define TDO 21
-#define TDONE 22
-#define TBEGIN 23
-#define TEND 24
-#define TCASE 25
-#define TESAC 26
-
-/* Array indicating which tokens mark the end of a list */
-const char tokendlist[] = {
-	1,
-	0,
-	0,
-	0,
-	0,
-	0,
-	0,
-	0,
-	1,
-	1,
-	1,
-	0,
-	0,
-	0,
-	1,
-	1,
-	1,
-	1,
-	0,
-	0,
-	0,
-	1,
-	1,
-	0,
-	1,
-	0,
-	1,
-};
-
-char *const tokname[] = {
-	"end of file",
-	"newline",
-	"\";\"",
-	"\"&\"",
-	"\"&&\"",
-	"\"||\"",
-	"\"|\"",
-	"\"(\"",
-	"\")\"",
-	"\";;\"",
-	"\"`\"",
-	"redirection",
-	"word",
-	"\"if\"",
-	"\"then\"",
-	"\"else\"",
-	"\"elif\"",
-	"\"fi\"",
-	"\"while\"",
-	"\"until\"",
-	"\"for\"",
-	"\"do\"",
-	"\"done\"",
-	"\"{\"",
-	"\"}\"",
-	"\"case\"",
-	"\"esac\"",
-};
-
-#define KWDOFFSET 13
-
-char *const parsekwd[] = {
-	"if",
-	"then",
-	"else",
-	"elif",
-	"fi",
-	"while",
-	"until",
-	"for",
-	"do",
-	"done",
-	"{",
-	"}",
-	"case",
-	"esac",
-	0
-};
diff --git a/ash/trap.c b/ash/trap.c
deleted file mode 100644
index bf254af..0000000
--- a/ash/trap.c
+++ /dev/null
@@ -1,488 +0,0 @@
-/*	$NetBSD: trap.c,v 1.30 2003/08/26 18:13:25 jmmv Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)trap.c	8.5 (Berkeley) 6/5/95";
-#else
-__RCSID("$NetBSD: trap.c,v 1.30 2003/08/26 18:13:25 jmmv Exp $");
-#endif
-#endif /* not lint */
-
-#include <signal.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#include "shell.h"
-#include "main.h"
-#include "nodes.h"	/* for other headers */
-#include "eval.h"
-#include "jobs.h"
-#include "show.h"
-#include "options.h"
-#include "syntax.h"
-#include "output.h"
-#include "memalloc.h"
-#include "error.h"
-#include "trap.h"
-#include "mystring.h"
-
-#ifdef __KLIBC__
-typedef __sighandler_t sig_t;
-#define POSIX_SIGNALS
-#undef _GNU_SOURCE
-#define sys_signame sys_siglist
-#endif
-
-/*
- * Sigmode records the current value of the signal handlers for the various
- * modes.  A value of zero means that the current handler is not known.
- * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
- */
-
-#define S_DFL 1			/* default signal handling (SIG_DFL) */
-#define S_CATCH 2		/* signal is caught */
-#define S_IGN 3			/* signal is ignored (SIG_IGN) */
-#define S_HARD_IGN 4		/* signal is ignored permenantly */
-#define S_RESET 5		/* temporary - to reset a hard ignored sig */
-
-
-char *trap[NSIG+1];		/* trap handler commands */
-MKINIT char sigmode[NSIG];	/* current value of signal */
-char gotsig[NSIG];		/* indicates specified signal received */
-int pendingsigs;		/* indicates some signal received */
-
-static int getsigaction(int, sig_t *);
-
-/*
- * return the signal number described by `p' (as a number or a name)
- * or -1 if it isn't one
- */
-
-static int
-signame_to_signum(const char *p)
-{
-	int i;
-
-	if (is_number(p))
-		return number(p);
-
-	if (strcasecmp(p, "exit") == 0 )
-		return 0;
-	
-	if (strncasecmp(p, "sig", 3) == 0)
-		p += 3;
-
-	for (i = 0; i < NSIG; ++i)
-#ifdef _GNU_SOURCE
-		if (strcasecmp (p, strsignal(i)) == 0)
-#else
-		if (strcasecmp (p, sys_signame[i]) == 0)
-#endif
-			return i;
-	return -1;
-}
-
-/*
- * Print a list of valid signal names
- */
-static void
-printsignals(void)
-{
-	int n;
-
-	out1str("EXIT ");
-
-	for (n = 1; n < NSIG; n++) {
-#ifdef _GNU_SOURCE
-		out1fmt("%s", strsignal(n));
-#else
-		out1fmt("%s", sys_signame[n]);
-#endif
-		if ((n == NSIG/2) ||  n == (NSIG - 1))
-			out1str("\n");
-		else
-			out1c(' ');
-	}
-}
-
-/*
- * The trap builtin.
- */
-
-int
-trapcmd(int argc, char **argv)
-{
-	char *action;
-	char **ap;
-	int signo;
-
-	if (argc <= 1) {
-		for (signo = 0 ; signo <= NSIG ; signo++) {
-			if (trap[signo] != NULL)
-#ifdef _GNU_SOURCE
-				out1fmt("trap -- '%s' %s\n", trap[signo],
-				    (signo) ? strsignal(signo) : "EXIT");
-#else
-				out1fmt("trap -- '%s' %s\n", trap[signo],
-				    (signo) ? sys_signame[signo] : "EXIT");
-#endif
-		}
-		return 0;
-	}
-	ap = argv + 1;
-
-	action = NULL;
-
-	if (strcmp(*ap, "--") == 0)
-		if (*++ap == NULL)
-			return 0;
-
-	if (signame_to_signum(*ap) == -1) {
-		if ((*ap)[0] == '-') {
-			if ((*ap)[1] == '\0')
-				ap++;
-			else if ((*ap)[1] == 'l' && (*ap)[2] == '\0') {
-				printsignals();
-				return 0;
-			}
-			else
-				error("bad option %s\n", *ap);
-		}
-		else
-			action = *ap++;
-	}
-
-	while (*ap) {
-		if (is_number(*ap))
-			signo = number(*ap);
-		else
-			signo = signame_to_signum(*ap);
-
-		if (signo < 0 || signo > NSIG)
-			error("%s: bad trap", *ap);
-
-		INTOFF;
-		if (action)
-			action = savestr(action);
-
-		if (trap[signo])
-			ckfree(trap[signo]);
-
-		trap[signo] = action;
-
-		if (signo != 0)
-			setsignal(signo, 0);
-		INTON;
-		ap++;
-	}
-	return 0;
-}
-
-
-
-/*
- * Clear traps on a fork or vfork.
- * Takes one arg vfork, to tell it to not be destructive of
- * the parents variables.
- */
-
-void
-clear_traps(int vforked)
-{
-	char **tp;
-
-	for (tp = trap ; tp <= &trap[NSIG] ; tp++) {
-		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
-			INTOFF;
-			if (!vforked) {
-				ckfree(*tp);
-				*tp = NULL;
-			}
-			if (tp != &trap[0])
-				setsignal(tp - trap, vforked);
-			INTON;
-		}
-	}
-}
-
-
-
-/*
- * Set the signal handler for the specified signal.  The routine figures
- * out what it should be set to.
- */
-
-long
-setsignal(int signo, int vforked)
-{
-	int action;
-	sig_t sigact = SIG_DFL;
-#ifdef POSIX_SIGNALS
-	struct sigaction act, oact;
-#endif
-	char *t, tsig;
-
-	if ((t = trap[signo]) == NULL)
-		action = S_DFL;
-	else if (*t != '\0')
-		action = S_CATCH;
-	else
-		action = S_IGN;
-	if (rootshell && !vforked && action == S_DFL) {
-		switch (signo) {
-		case SIGINT:
-			if (iflag || minusc || sflag == 0)
-				action = S_CATCH;
-			break;
-		case SIGQUIT:
-#ifdef DEBUG
-			if (debug)
-				break;
-#endif
-			/* FALLTHROUGH */
-		case SIGTERM:
-			if (iflag)
-				action = S_IGN;
-			break;
-#if JOBS
-		case SIGTSTP:
-		case SIGTTOU:
-			if (mflag)
-				action = S_IGN;
-			break;
-#endif
-		}
-	}
-
-	t = &sigmode[signo - 1];
-	tsig = *t;
-	if (tsig == 0) {
-		/*
-		 * current setting unknown
-		 */
-		if (!getsigaction(signo, &sigact)) {
-			/*
-			 * Pretend it worked; maybe we should give a warning
-			 * here, but other shells don't. We don't alter
-			 * sigmode, so that we retry every time.
-			 */
-			return 0;
-		}
-		if (sigact == SIG_IGN) {
-			if (mflag && (signo == SIGTSTP ||
-			     signo == SIGTTIN || signo == SIGTTOU)) {
-				tsig = S_IGN;	/* don't hard ignore these */
-			} else
-				tsig = S_HARD_IGN;
-		} else {
-			tsig = S_RESET;	/* force to be set */
-		}
-	}
-	if (tsig == S_HARD_IGN || tsig == action)
-		return 0;
-	switch (action) {
-		case S_DFL:	sigact = SIG_DFL;	break;
-		case S_CATCH:  	sigact = onsig;		break;
-		case S_IGN:	sigact = SIG_IGN;	break;
-	}
-	if (!vforked)
-		*t = action;
-#ifdef POSIX_SIGNALS
-	act.sa_handler = sigact;
-	sigemptyset(&act.sa_mask);
-	act.sa_flags = 0;
-#ifdef SA_INTERRUPT
-	act.sa_flags |= SA_INTERRUPT;
-#endif
-	if (sigaction(signo, &act, &oact) < 0) 
-		return (long)SIG_ERR;
-	return (long)oact.sa_handler;
-#else
-	siginterrupt(signo, 1);
-	return (long)signal(signo, sigact);
-#endif
-}
-
-/*
- * Return the current setting for sig w/o changing it.
- */
-static int
-getsigaction(int signo, sig_t *sigact)
-{
-	struct sigaction sa;
-
-	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
-		return 0;
-	*sigact = (sig_t) sa.sa_handler;
-	return 1;
-}
-
-/*
- * Ignore a signal.
- */
-
-void
-ignoresig(int signo, int vforked)
-{
-	if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
-		bsd_signal(signo, SIG_IGN);
-	}
-	if (!vforked)
-		sigmode[signo - 1] = S_HARD_IGN;
-}
-
-
-#ifdef mkinit
-INCLUDE <signal.h>
-INCLUDE "trap.h"
-
-SHELLPROC {
-	char *sm;
-
-	clear_traps(0);
-	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
-		if (*sm == S_IGN)
-			*sm = S_HARD_IGN;
-	}
-}
-#endif
-
-
-
-/*
- * Signal handler.
- *
- * The __cdecl is to work around the fact that the Linux/i386 kernel prior
- * to 2.6.9-rc2 didn't pass the proper arguments to regparm'd signal handlers.
- */
-__cdecl void
-onsig(int signo)
-{
-	bsd_signal(signo, onsig);
-	if (signo == SIGINT && trap[SIGINT] == NULL) {
-		onint();
-		return;
-	}
-	gotsig[signo - 1] = 1;
-	pendingsigs++;
-}
-
-
-
-/*
- * Called to execute a trap.  Perhaps we should avoid entering new trap
- * handlers while we are executing a trap handler.
- */
-
-void
-dotrap(void)
-{
-	int i;
-	int savestatus;
-
-	for (;;) {
-		for (i = 1 ; ; i++) {
-			if (gotsig[i - 1])
-				break;
-			if (i >= NSIG)
-				goto done;
-		}
-		gotsig[i - 1] = 0;
-		savestatus=exitstatus;
-		evalstring(trap[i], 0);
-		exitstatus=savestatus;
-	}
-done:
-	pendingsigs = 0;
-}
-
-
-
-/*
- * Controls whether the shell is interactive or not.
- */
-
-
-void
-setinteractive(int on)
-{
-	static int is_interactive;
-
-	if (on == is_interactive)
-		return;
-	setsignal(SIGINT, 0);
-	setsignal(SIGQUIT, 0);
-	setsignal(SIGTERM, 0);
-	is_interactive = on;
-}
-
-
-
-/*
- * Called to exit the shell.
- */
-
-void
-exitshell(int status)
-{
-	struct jmploc loc1, loc2;
-	char *p;
-
-	TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
-	if (setjmp(loc1.loc)) {
-		goto l1;
-	}
-	if (setjmp(loc2.loc)) {
-		goto l2;
-	}
-	handler = &loc1;
-	if ((p = trap[0]) != NULL && *p != '\0') {
-		trap[0] = NULL;
-		evalstring(p, 0);
-	}
-l1:   handler = &loc2;			/* probably unnecessary */
-	flushall();
-#if JOBS
-	setjobctl(0);
-#endif
-l2:   _exit(status);
-	/* NOTREACHED */
-}
diff --git a/ash/var.c b/ash/var.c
deleted file mode 100644
index 428eae9..0000000
--- a/ash/var.c
+++ /dev/null
@@ -1,851 +0,0 @@
-/*	$NetBSD: var.c,v 1.34 2003/08/26 18:14:24 jmmv Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: var.c,v 1.34 2003/08/26 18:14:24 jmmv Exp $");
-#endif
-#endif /* not lint */
-
-#include <unistd.h>
-#include <stdlib.h>
-#ifndef __KLIBC__
-#include <strings.h>
-#include <paths.h>
-#else
-#define index(s,c) strchr(s,c)
-#define _PATH_DEFPATH "/bin:/usr/bin"
-#endif
-
-/*
- * Shell variables.
- */
-
-#include "shell.h"
-#include "output.h"
-#include "expand.h"
-#include "nodes.h"	/* for other headers */
-#include "eval.h"	/* defines cmdenviron */
-#include "exec.h"
-#include "syntax.h"
-#include "options.h"
-#include "mail.h"
-#include "var.h"
-#include "memalloc.h"
-#include "error.h"
-#include "mystring.h"
-#include "parser.h"
-#include "show.h"
-#ifdef KLIBC_SH_HISTORY
-#include "myhistedit.h"
-#endif
-
-
-#define VTABSIZE 39
-
-
-struct varinit {
-	struct var *var;
-	int flags;
-	const char *text;
-	void (*func)(const char *);
-};
-
-
-#if ATTY
-struct var vatty;
-#endif
-#ifdef KLIBC_SH_HISTORY
-struct var vhistsize;
-struct var vterm;
-#endif
-struct var vifs;
-#ifdef KLIBC_SH_MAIL
-struct var vmail;
-struct var vmpath;
-#endif
-struct var vpath;
-struct var vps1;
-struct var vps2;
-struct var vps4;
-struct var vvers;
-struct var voptind;
-
-const struct varinit varinit[] = {
-#if ATTY
-	{ &vatty,	VSTRFIXED|VTEXTFIXED|VUNSET,	"ATTY=",
-	  NULL },
-#endif
-#ifdef KLIBC_SH_HISTORY
-	{ &vhistsize,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE=",
-	  sethistsize },
-#endif
-	{ &vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n",
-	  NULL },
-#ifdef KLIBC_SH_MAIL
-	{ &vmail,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL=",
-	  NULL },
-	{ &vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH=",
-	  NULL },
-#endif
-	{ &vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=" _PATH_DEFPATH,
-	  changepath },
-	/*
-	 * vps1 depends on uid
-	 */
-	{ &vps2,	VSTRFIXED|VTEXTFIXED,		"PS2=> ",
-	  NULL },
-	{ &vps4,	VSTRFIXED|VTEXTFIXED,		"PS4=+ ",
-	  NULL },
-#ifdef KLIBC_SH_HISTORY
-	{ &vterm,	VSTRFIXED|VTEXTFIXED|VUNSET,	"TERM=",
-	  setterm },
-#endif
-	{ &voptind,	VSTRFIXED|VTEXTFIXED|VNOFUNC,	"OPTIND=1",
-	  getoptsreset },
-	{ NULL,	0,				NULL,
-	  NULL }
-};
-
-struct var *vartab[VTABSIZE];
-
-STATIC struct var **hashvar(const char *);
-STATIC int varequal(const char *, const char *);
-
-/*
- * Initialize the varable symbol tables and import the environment
- */
-
-#ifdef mkinit
-INCLUDE "var.h"
-MKINIT char **environ;
-INIT {
-	char **envp;
-
-	initvar();
-	for (envp = environ ; *envp ; envp++) {
-		if (strchr(*envp, '=')) {
-			setvareq(*envp, VEXPORT|VTEXTFIXED);
-		}
-	}
-}
-#endif
-
-
-/*
- * This routine initializes the builtin variables.  It is called when the
- * shell is initialized and again when a shell procedure is spawned.
- */
-
-void
-initvar(void)
-{
-	const struct varinit *ip;
-	struct var *vp;
-	struct var **vpp;
-
-	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
-		if ((vp->flags & VEXPORT) == 0) {
-			vpp = hashvar(ip->text);
-			vp->next = *vpp;
-			*vpp = vp;
-			vp->text = strdup(ip->text);
-			vp->flags = ip->flags;
-			vp->func = ip->func;
-		}
-	}
-	/*
-	 * PS1 depends on uid
-	 */
-	if ((vps1.flags & VEXPORT) == 0) {
-		vpp = hashvar("PS1=");
-		vps1.next = *vpp;
-		*vpp = &vps1;
-		vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# ");
-		vps1.flags = VSTRFIXED|VTEXTFIXED;
-	}
-}
-
-/*
- * Safe version of setvar, returns 1 on success 0 on failure.
- */
-
-int
-setvarsafe(const char *name, const char *val, int flags)
-{
-	struct jmploc jmploc;
-	struct jmploc *volatile savehandler = handler;
-	int err = 0;
-#ifdef __GNUC__
-	(void) &err;
-#endif
-
-	if (setjmp(jmploc.loc))
-		err = 1;
-	else {
-		handler = &jmploc;
-		setvar(name, val, flags);
-	}
-	handler = savehandler;
-	return err;
-}
-
-/*
- * Set the value of a variable.  The flags argument is ored with the
- * flags of the variable.  If val is NULL, the variable is unset.
- */
-
-void
-setvar(const char *name, const char *val, int flags)
-{
-	const char *p;
-	const char *q;
-	char *d;
-	int len;
-	int namelen;
-	char *nameeq;
-	int isbad;
-
-	isbad = 0;
-	p = name;
-	if (! is_name(*p))
-		isbad = 1;
-	p++;
-	for (;;) {
-		if (! is_in_name(*p)) {
-			if (*p == '\0' || *p == '=')
-				break;
-			isbad = 1;
-		}
-		p++;
-	}
-	namelen = p - name;
-	if (isbad)
-		error("%.*s: bad variable name", namelen, name);
-	len = namelen + 2;		/* 2 is space for '=' and '\0' */
-	if (val == NULL) {
-		flags |= VUNSET;
-	} else {
-		len += strlen(val);
-	}
-	d = nameeq = ckmalloc(len);
-	q = name;
-	while (--namelen >= 0)
-		*d++ = *q++;
-	*d++ = '=';
-	*d = '\0';
-	if (val)
-		scopy(val, d);
-	setvareq(nameeq, flags);
-}
-
-
-
-/*
- * Same as setvar except that the variable and value are passed in
- * the first argument as name=value.  Since the first argument will
- * be actually stored in the table, it should not be a string that
- * will go away.
- */
-
-void
-setvareq(char *s, int flags)
-{
-	struct var *vp, **vpp;
-
-	if (aflag)
-		flags |= VEXPORT;
-	vpp = hashvar(s);
-	for (vp = *vpp ; vp ; vp = vp->next) {
-		if (varequal(s, vp->text)) {
-			if (vp->flags & VREADONLY) {
-				size_t len = strchr(s, '=') - s;
-				error("%.*s: is read only", len, s);
-			}
-			if (flags & VNOSET)
-				return;
-			INTOFF;
-
-			if (vp->func && (flags & VNOFUNC) == 0)
-				(*vp->func)(strchr(s, '=') + 1);
-
-			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
-				ckfree(vp->text);
-
-			vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
-			vp->flags |= flags & ~VNOFUNC;
-			vp->text = s;
-
-			/*
-			 * We could roll this to a function, to handle it as
-			 * a regular variable function callback, but why bother?
-			 */
-#ifdef KLIBC_SH_MAIL
-			if (vp == &vmpath || (vp == &vmail && ! mpathset()))
-				chkmail(1);
-#endif
-			INTON;
-			return;
-		}
-	}
-	/* not found */
-	if (flags & VNOSET)
-		return;
-	vp = ckmalloc(sizeof (*vp));
-	vp->flags = flags & ~VNOFUNC;
-	vp->text = s;
-	vp->next = *vpp;
-	vp->func = NULL;
-	*vpp = vp;
-}
-
-
-
-/*
- * Process a linked list of variable assignments.
- */
-
-void
-listsetvar(struct strlist *list, int flags)
-{
-	struct strlist *lp;
-
-	INTOFF;
-	for (lp = list ; lp ; lp = lp->next) {
-		setvareq(savestr(lp->text), flags);
-	}
-	INTON;
-}
-
-void
-listmklocal(struct strlist *list, int flags)
-{
-	struct strlist *lp;
-
-	for (lp = list ; lp ; lp = lp->next)
-		mklocal(lp->text, flags);
-}
-
-
-/*
- * Find the value of a variable.  Returns NULL if not set.
- */
-
-char *
-lookupvar(const char *name)
-{
-	struct var *v;
-
-	for (v = *hashvar(name) ; v ; v = v->next) {
-		if (varequal(v->text, name)) {
-			if (v->flags & VUNSET)
-				return NULL;
-			return strchr(v->text, '=') + 1;
-		}
-	}
-	return NULL;
-}
-
-
-
-/*
- * Search the environment of a builtin command.  If the second argument
- * is nonzero, return the value of a variable even if it hasn't been
- * exported.
- */
-
-char *
-bltinlookup(const char *name, int doall)
-{
-	struct strlist *sp;
-	struct var *v;
-
-	for (sp = cmdenviron ; sp ; sp = sp->next) {
-		if (varequal(sp->text, name))
-			return strchr(sp->text, '=') + 1;
-	}
-	for (v = *hashvar(name) ; v ; v = v->next) {
-		if (varequal(v->text, name)) {
-			if ((v->flags & VUNSET)
-			 || (!doall && (v->flags & VEXPORT) == 0))
-				return NULL;
-			return strchr(v->text, '=') + 1;
-		}
-	}
-	return NULL;
-}
-
-
-
-/*
- * Generate a list of exported variables.  This routine is used to construct
- * the third argument to execve when executing a program.
- */
-
-char **
-environment(void)
-{
-	int nenv;
-	struct var **vpp;
-	struct var *vp;
-	char **env;
-	char **ep;
-
-	nenv = 0;
-	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
-		for (vp = *vpp ; vp ; vp = vp->next)
-			if (vp->flags & VEXPORT)
-				nenv++;
-	}
-	ep = env = stalloc((nenv + 1) * sizeof *env);
-	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
-		for (vp = *vpp ; vp ; vp = vp->next)
-			if (vp->flags & VEXPORT)
-				*ep++ = vp->text;
-	}
-	*ep = NULL;
-	return env;
-}
-
-
-/*
- * Called when a shell procedure is invoked to clear out nonexported
- * variables.  It is also necessary to reallocate variables of with
- * VSTACK set since these are currently allocated on the stack.
- */
-
-#ifdef mkinit
-void shprocvar(void);
-
-SHELLPROC {
-	shprocvar();
-}
-#endif
-
-void
-shprocvar(void)
-{
-	struct var **vpp;
-	struct var *vp, **prev;
-
-	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
-		for (prev = vpp ; (vp = *prev) != NULL ; ) {
-			if ((vp->flags & VEXPORT) == 0) {
-				*prev = vp->next;
-				if ((vp->flags & VTEXTFIXED) == 0)
-					ckfree(vp->text);
-				if ((vp->flags & VSTRFIXED) == 0)
-					ckfree(vp);
-			} else {
-				if (vp->flags & VSTACK) {
-					vp->text = savestr(vp->text);
-					vp->flags &=~ VSTACK;
-				}
-				prev = &vp->next;
-			}
-		}
-	}
-	initvar();
-}
-
-
-
-/*
- * Command to list all variables which are set.  Currently this command
- * is invoked from the set command when the set command is called without
- * any variables.
- */
-
-void
-print_quoted(const char *p)
-{
-	const char *q;
-
-	if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) {
-		out1fmt("%s", p);
-		return;
-	}
-	while (*p) {
-		if (*p == '\'') {
-			out1fmt("\\'");
-			p++;
-			continue;
-		}
-		q = index(p, '\'');
-		if (!q) {
-			out1fmt("'%s'", p );
-			return;
-		}
-		out1fmt("'%.*s'", (int)(q - p), p );
-		p = q;
-	}
-}
-
-static int
-sort_var(const void *v_v1, const void *v_v2)
-{
-	const struct var * const *v1 = v_v1;
-	const struct var * const *v2 = v_v2;
-
-	/* XXX Will anyone notice we include the '=' of the shorter name? */
-#ifdef HAVE_LOCALE_H
-	return strcoll((*v1)->text, (*v2)->text);
-#else
-	return strcmp((*v1)->text, (*v2)->text);
-#endif
-}
-
-/*
- * POSIX requires that 'set' (but not export or readonly) output the
- * variables in lexicographic order - by the locale's collating order (sigh).
- * Maybe we could keep them in an ordered balanced binary tree
- * instead of hashed lists.
- * For now just roll 'em through qsort for printing...
- */
-
-int
-showvars(const char *name, int flag, int show_value)
-{
-	struct var **vpp;
-	struct var *vp;
-	const char *p;
-
-	static struct var **list;	/* static in case we are interrupted */
-	static int list_len;
-	int count = 0;
-
-	if (!list) {
-		list_len = 32;
-		list = ckmalloc(list_len * sizeof *list);
-	}
-
-	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
-		for (vp = *vpp ; vp ; vp = vp->next) {
-			if (flag && !(vp->flags & flag))
-				continue;
-			if (vp->flags & VUNSET && !(show_value & 2))
-				continue;
-			if (count >= list_len) {
-				list = ckrealloc(list,
-					(list_len << 1) * sizeof *list);
-				list_len <<= 1;
-			}
-			list[count++] = vp;
-		}
-	}
-
-	qsort(list, count, sizeof *list, sort_var);
-
-	for (vpp = list; count--; vpp++) {
-		vp = *vpp;
-		if (name)
-			out1fmt("%s ", name);
-		for (p = vp->text ; *p != '=' ; p++)
-			out1c(*p);
-		if (!(vp->flags & VUNSET) && show_value) {
-			out1fmt("=");
-			print_quoted(++p);
-		}
-		out1c('\n');
-	}
-	return 0;
-}
-
-
-
-/*
- * The export and readonly commands.
- */
-
-int
-exportcmd(int argc, char **argv)
-{
-	struct var **vpp;
-	struct var *vp;
-	char *name;
-	const char *p;
-	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
-	int pflag;
-
-	pflag = nextopt("p") == 'p' ? 3 : 0;
-	if (argc <= 1 || pflag) {
-		showvars( pflag ? argv[0] : 0, flag, pflag );
-		return 0;
-	}
-
-	while ((name = *argptr++) != NULL) {
-		if ((p = strchr(name, '=')) != NULL) {
-			p++;
-		} else {
-			vpp = hashvar(name);
-			for (vp = *vpp ; vp ; vp = vp->next) {
-				if (varequal(vp->text, name)) {
-					vp->flags |= flag;
-					goto found;
-				}
-			}
-		}
-		setvar(name, p, flag);
-found:;
-	}
-	return 0;
-}
-
-
-/*
- * The "local" command.
- */
-
-int
-localcmd(int argc, char **argv)
-{
-	char *name;
-
-	if (! in_function())
-		error("Not in a function");
-	while ((name = *argptr++) != NULL) {
-		mklocal(name, 0);
-	}
-	return 0;
-}
-
-
-/*
- * Make a variable a local variable.  When a variable is made local, it's
- * value and flags are saved in a localvar structure.  The saved values
- * will be restored when the shell function returns.  We handle the name
- * "-" as a special case.
- */
-
-void
-mklocal(const char *name, int flags)
-{
-	struct localvar *lvp;
-	struct var **vpp;
-	struct var *vp;
-
-	INTOFF;
-	lvp = ckmalloc(sizeof (struct localvar));
-	if (name[0] == '-' && name[1] == '\0') {
-		char *p;
-		p = ckmalloc(sizeof_optlist);
-		lvp->text = memcpy(p, optlist, sizeof_optlist);
-		vp = NULL;
-	} else {
-		vpp = hashvar(name);
-		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
-		if (vp == NULL) {
-			if (strchr(name, '='))
-				setvareq(savestr(name), VSTRFIXED|flags);
-			else
-				setvar(name, NULL, VSTRFIXED|flags);
-			vp = *vpp;	/* the new variable */
-			lvp->text = NULL;
-			lvp->flags = VUNSET;
-		} else {
-			lvp->text = vp->text;
-			lvp->flags = vp->flags;
-			vp->flags |= VSTRFIXED|VTEXTFIXED;
-			if (strchr(name, '='))
-				setvareq(savestr(name), flags);
-		}
-	}
-	lvp->vp = vp;
-	lvp->next = localvars;
-	localvars = lvp;
-	INTON;
-}
-
-
-/*
- * Called after a function returns.
- */
-
-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);
-			ckfree(lvp->text);
-		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
-			(void)unsetvar(vp->text, 0);
-		} else {
-			if (vp->func && (vp->flags & VNOFUNC) == 0)
-				(*vp->func)(strchr(lvp->text, '=') + 1);
-			if ((vp->flags & VTEXTFIXED) == 0)
-				ckfree(vp->text);
-			vp->flags = lvp->flags;
-			vp->text = lvp->text;
-		}
-		ckfree(lvp);
-	}
-}
-
-
-int
-setvarcmd(int argc, char **argv)
-{
-	if (argc <= 2)
-		return unsetcmd(argc, argv);
-	else if (argc == 3)
-		setvar(argv[1], argv[2], 0);
-	else
-		error("List assignment not implemented");
-	return 0;
-}
-
-
-/*
- * The unset builtin command.  We unset the function before we unset the
- * variable to allow a function to be unset when there is a readonly variable
- * with the same name.
- */
-
-int
-unsetcmd(int argc, char **argv)
-{
-	char **ap;
-	int i;
-	int flg_func = 0;
-	int flg_var = 0;
-	int ret = 0;
-
-	while ((i = nextopt("evf")) != '\0') {
-		if (i == 'f')
-			flg_func = 1;
-		else
-			flg_var = i;
-	}
-	if (flg_func == 0 && flg_var == 0)
-		flg_var = 1;
-
-	for (ap = argptr; *ap ; ap++) {
-		if (flg_func)
-			ret |= unsetfunc(*ap);
-		if (flg_var)
-			ret |= unsetvar(*ap, flg_var == 'e');
-	}
-	return ret;
-}
-
-
-/*
- * Unset the specified variable.
- */
-
-int
-unsetvar(const char *s, int unexport)
-{
-	struct var **vpp;
-	struct var *vp;
-
-	vpp = hashvar(s);
-	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
-		if (varequal(vp->text, s)) {
-			if (vp->flags & VREADONLY)
-				return (1);
-			INTOFF;
-			if (unexport) {
-				vp->flags &= ~VEXPORT;
-			} else {
-				if (*(strchr(vp->text, '=') + 1) != '\0')
-					setvar(s, nullstr, 0);
-				vp->flags &= ~VEXPORT;
-				vp->flags |= VUNSET;
-				if ((vp->flags & VSTRFIXED) == 0) {
-					if ((vp->flags & VTEXTFIXED) == 0)
-						ckfree(vp->text);
-					*vpp = vp->next;
-					ckfree(vp);
-				}
-			}
-			INTON;
-			return (0);
-		}
-	}
-
-	return (1);
-}
-
-
-
-/*
- * Find the appropriate entry in the hash table from the name.
- */
-
-STATIC struct var **
-hashvar(const char *p)
-{
-	unsigned int hashval;
-
-	hashval = ((unsigned char) *p) << 4;
-	while (*p && *p != '=')
-		hashval += (unsigned char) *p++;
-	return &vartab[hashval % VTABSIZE];
-}
-
-
-
-/*
- * Returns true if the two strings specify the same varable.  The first
- * variable name is terminated by '='; the second may be terminated by
- * either '=' or '\0'.
- */
-
-STATIC int
-varequal(const char *p, const char *q)
-{
-	while (*p == *q++) {
-		if (*p++ == '=')
-			return 1;
-	}
-	if (*p == '=' && *(q - 1) == '\0')
-		return 1;
-	return 0;
-}
diff --git a/dash/Makefile b/dash/Makefile
new file mode 100644
index 0000000..899e657
--- /dev/null
+++ b/dash/Makefile
@@ -0,0 +1,155 @@
+#	Makefile.linux
+
+YACC = bison -y
+
+COMMON_CFLAGS = -Wall $(OPTFLAGS) $(REQFLAGS)
+COMMON_CPPFLAGS = \
+	-include config.h \
+	-DBSD=1 -DSMALL -DSHELL \
+	-DGLOB_BROKEN -DFNMATCH_BROKEN -DIFS_BROKEN \
+	-DJOBS=0
+
+CFLAGS = $(COMMON_CFLAGS)
+CPPFLAGS = $(COMMON_CPPFLAGS)
+CFLAGS_FOR_BUILD = $(HOST_CFLAGS) $(COMMON_CFLAGS) 
+CPPFLAGS_FOR_BUILD = $(COMMON_CPPFLAGS)
+YFLAGS = -d
+
+DEFS = -DHAVE_CONFIG_H
+DEFAULT_INCLUDES = -I.
+DEPDIR = .deps
+
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(CPPFLAGS) $(CFLAGS)
+COMPILE_FOR_BUILD = \
+	$(HOST_CC) $(CPPFLAGS_FOR_BUILD) \
+	$(CFLAGS_FOR_BUILD) 
+YACCCOMPILE = $(YACC) $(YFLAGS)
+
+PROG =	sh
+SRCS =	alias.c arith_yylex.c cd.c error.c eval.c exec.c expand.c \
+	histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \
+	mystring.c options.c parser.c redir.c show.c trap.c output.c \
+	bltin/printf.c system.c bltin/test.c bltin/times.c var.c
+
+OBJ1 =	builtins.o init.o nodes.o signames.o syntax.o
+OBJ2 =	alias.o arith.o arith_yylex.o cd.o \
+	error.o eval.o exec.o expand.o \
+	histedit.o input.o jobs.o \
+	mail.o main.o memalloc.o \
+	miscbltin.o mystring.o options.o \
+	parser.o redir.o show.o trap.o \
+	output.o printf.o system.o \
+	test.o times.o var.o
+
+OBJS =	$(OBJ1) $(OBJ2)
+
+SRCROOT  = ..
+include ../MCONFIG
+
+LIBS = $(KLIBC) $(LIBGCC)
+
+HELPERS = mkinit mksyntax mknodes mksignames
+BUILT_SOURCES = arith.h builtins.h nodes.h syntax.h token.h
+CLEANFILES = \
+	$(BUILT_SOURCES) $(patsubst %.o,%.c,$(OBJ1)) \
+	arith.c $(HELPERS) builtins.def
+
+all:	$(PROG) $(PROG).shared
+
+$(OBJS): $(BUILT_SOURCES)
+
+$(PROG): $(OBJS) $(LIBS) $(CRT0)
+	$(LD) $(LDFLAGS) -o $@ $(CRT0) $(OBJS) $(LIBS)
+	cp -f $@ $@.g
+	$(STRIPCMD) $@
+
+$(PROG).shared: $(OBJS) $(CRTSHARED) $(LIBSHARED) $(LIBGCC)
+	$(LD) $(LDFLAGS) -o $(PROG).shared $(EMAIN) $(CRTSHARED) $(OBJS) -R $(LIBSHARED) $(LIBGCC)
+	cp -f $@ $@.g
+	$(STRIPCMD) $@
+
+$(CRT0) $(LIBS):
+	@echo '*** error: $@ not up to date' || exit 1
+
+.c.o:
+	if $(COMPILE) -MT $@ -MD -MP -MF ".$*.Td" -c -o $@ $<; \
+	then mv -f ".$*.Td" ".$*.d"; else rm -f ".$*.Td"; exit 1; fi
+
+printf.o: bltin/printf.c
+	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -MT printf.o -MD -MP -MF ".printf.Td" -c -o printf.o `test -f 'bltin/printf.c' || echo '$(srcdir)/'`bltin/printf.c; \
+	then mv -f ".printf.Td" ".printf.d"; else rm -f ".printf.Td"; exit 1; fi
+
+test.o: bltin/test.c
+	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -MT test.o -MD -MP -MF ".test.Td" -c -o test.o `test -f 'bltin/test.c' || echo '$(srcdir)/'`bltin/test.c; \
+	then mv -f ".test.Td" ".test.d"; else rm -f ".test.Td"; exit 1; fi
+
+times.o: bltin/times.c
+	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -MT times.o -MD -MP -MF ".times.Td" -c -o times.o `test -f 'bltin/times.c' || echo '$(srcdir)/'`bltin/times.c; \
+	then mv -f ".times.Td" ".times.d"; else rm -f ".times.Td"; exit 1; fi
+
+.y.c:
+	$(YACCCOMPILE) $<
+	if test -f y.tab.h; then \
+	  to=`echo "$*_H" | sed \
+                -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \
+                -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`; \
+	  sed -e "/^#/!b" -e "s/Y_TAB_H/$$to/g" -e "s|y\.tab\.h|$*.h|" \
+            y.tab.h >$*.ht; \
+	  rm -f y.tab.h; \
+	  if cmp -s $*.ht $*.h; then \
+	    rm -f $*.ht ;\
+	  else \
+	    mv $*.ht $*.h; \
+	  fi; \
+	fi
+	if test -f y.output; then \
+	  mv y.output $*.output; \
+	fi
+	sed '/^#/ s|y\.tab\.c|$@|' y.tab.c >$@t && mv $@t $@
+	rm -f y.tab.c
+
+token.h: mktokens
+	sh $^
+
+builtins.def: builtins.def.in config.h
+	$(COMPILE) -E -x c -o $@ $<
+
+builtins.c builtins.h: mkbuiltins builtins.def
+	sh $^
+
+init.c: mkinit $(SRCS)
+	./$^
+
+nodes.c nodes.h: mknodes nodetypes nodes.c.pat
+	./$^
+
+syntax.c syntax.h: mksyntax
+	./$^
+
+signames.c: mksignames
+	./$^
+
+$(HELPERS): %: %.c
+	$(COMPILE_FOR_BUILD) -o $@ $(CRT0) $< $(LIBS)
+
+arith.h: arith.c
+	@if test ! -f $@; then \
+	  rm -f arith.c; \
+	  $(MAKE) arith.c; \
+	else :; fi
+
+clean:
+	rm -f core $(CLEANFILES) $(OBJS)
+	rm -f $(PROG) $(PROG).g $(PROG).shared $(PROG).shared.g
+
+spotless: clean
+	find . \( -name \*~ -o -name \*.orig -o -name \*.rej -o -name \*.o \) -not -type d -print0 | xargs -0rt rm -f
+	rm -f tags .*.d .*.Td
+
+install: all
+	$(INSTALL_EXEC) sh.shared $(INSTALLROOT)$(INSTALLDIR)/$(CROSS)bin/sh
+
+ifneq ($(wildcard .*.d),)
+include $(wildcard .*.d)
+endif
diff --git a/dash/README.klibc b/dash/README.klibc
new file mode 100644
index 0000000..0394fb0
--- /dev/null
+++ b/dash/README.klibc
@@ -0,0 +1,7 @@
+This version of dash was obtained from
+
+http://gondor.apana.org.au/~herbert/dash/dash.git/
+
+It corresponds to changeset 3c98399cdf8d376b2c1ebd9cd32ca5d8c84f3ac9.
+
+The only changes made are the addition of config.h and a new Makefile.
diff --git a/ash/TOUR b/dash/TOUR
similarity index 97%
rename from ash/TOUR
rename to dash/TOUR
index 06e46e2..0c60e2a 100644
--- a/ash/TOUR
+++ b/dash/TOUR
@@ -1,4 +1,3 @@
-#	$NetBSD: TOUR,v 1.8 1996/10/16 14:24:56 christos Exp $
 #	@(#)TOUR	8.1 (Berkeley) 5/31/93
 
 NOTE -- This is the original TOUR paper distributed with ash and
@@ -347,11 +346,9 @@
 the current line in this variable.
 
 DEBUGGING:  If DEBUG is defined in shell.h, then the shell will
-write debugging information to the file /tmp/trace. Or, if an
-enviroment variable KLIBC_ASH_DEBUGTRACE is set, this file will
-be used for debugging outout. Most of this is done using the
-TRACE macro, which takes a set of printf arguments inside two
-sets of parenthesis.  Example:
+write debugging information to the file $HOME/trace.  Most of
+this is done using the TRACE macro, which takes a set of printf
+arguments inside two sets of parenthesis.  Example:
 "TRACE(("n=%d0, n))".  The double parenthesis are necessary be-
 cause the preprocessor can't handle functions with a variable
 number of arguments.  Defining DEBUG also causes the shell to
diff --git a/dash/alias.c b/dash/alias.c
new file mode 100644
index 0000000..daeacbb
--- /dev/null
+++ b/dash/alias.c
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include "shell.h"
+#include "input.h"
+#include "output.h"
+#include "error.h"
+#include "memalloc.h"
+#include "mystring.h"
+#include "alias.h"
+#include "options.h"	/* XXX for argptr (should remove?) */
+
+#define ATABSIZE 39
+
+struct alias *atab[ATABSIZE];
+
+STATIC void setalias(const char *, const char *);
+STATIC struct alias *freealias(struct alias *);
+STATIC struct alias **__lookupalias(const char *);
+
+STATIC
+void
+setalias(const char *name, const char *val)
+{
+	struct alias *ap, **app;
+
+	app = __lookupalias(name);
+	ap = *app;
+	INTOFF;
+	if (ap) {
+		if (!(ap->flag & ALIASINUSE)) {
+			ckfree(ap->val);
+		}
+		ap->val	= savestr(val);
+		ap->flag &= ~ALIASDEAD;
+	} else {
+		/* not found */
+		ap = ckmalloc(sizeof (struct alias));
+		ap->name = savestr(name);
+		ap->val = savestr(val);
+		ap->flag = 0;
+		ap->next = 0;
+		*app = ap;
+	}
+	INTON;
+}
+
+int
+unalias(const char *name)
+{
+	struct alias **app;
+
+	app = __lookupalias(name);
+
+	if (*app) {
+		INTOFF;
+		*app = freealias(*app);
+		INTON;
+		return (0);
+	}
+
+	return (1);
+}
+
+void
+rmaliases(void)
+{
+	struct alias *ap, **app;
+	int i;
+
+	INTOFF;
+	for (i = 0; i < ATABSIZE; i++) {
+		app = &atab[i];
+		for (ap = *app; ap; ap = *app) {
+			*app = freealias(*app);
+			if (ap == *app) {
+				app = &ap->next;
+			}
+		}
+	}
+	INTON;
+}
+
+struct alias *
+lookupalias(const char *name, int check)
+{
+	struct alias *ap = *__lookupalias(name);
+
+	if (check && ap && (ap->flag & ALIASINUSE))
+		return (NULL);
+	return (ap);
+}
+
+/*
+ * TODO - sort output
+ */
+int
+aliascmd(int argc, char **argv)
+{
+	char *n, *v;
+	int ret = 0;
+	struct alias *ap;
+
+	if (argc == 1) {
+		int i;
+
+		for (i = 0; i < ATABSIZE; i++)
+			for (ap = atab[i]; ap; ap = ap->next) {
+				printalias(ap);
+			}
+		return (0);
+	}
+	while ((n = *++argv) != NULL) {
+		if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
+			if ((ap = *__lookupalias(n)) == NULL) {
+				outfmt(out2, "%s: %s not found\n", "alias", n);
+				ret = 1;
+			} else
+				printalias(ap);
+		} else {
+			*v++ = '\0';
+			setalias(n, v);
+		}
+	}
+
+	return (ret);
+}
+
+int
+unaliascmd(int argc, char **argv)
+{
+	int i;
+
+	while ((i = nextopt("a")) != '\0') {
+		if (i == 'a') {
+			rmaliases();
+			return (0);
+		}
+	}
+	for (i = 0; *argptr; argptr++) {
+		if (unalias(*argptr)) {
+			outfmt(out2, "%s: %s not found\n", "unalias", *argptr);
+			i = 1;
+		}
+	}
+
+	return (i);
+}
+
+STATIC struct alias *
+freealias(struct alias *ap) {
+	struct alias *next;
+
+	if (ap->flag & ALIASINUSE) {
+		ap->flag |= ALIASDEAD;
+		return ap;
+	}
+
+	next = ap->next;
+	ckfree(ap->name);
+	ckfree(ap->val);
+	ckfree(ap);
+	return next;
+}
+
+void
+printalias(const struct alias *ap) {
+	out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
+}
+
+STATIC struct alias **
+__lookupalias(const char *name) {
+	unsigned int hashval;
+	struct alias **app;
+	const char *p;
+	unsigned int ch;
+
+	p = name;
+
+	ch = (unsigned char)*p;
+	hashval = ch << 4;
+	while (ch) {
+		hashval += ch;
+		ch = (unsigned char)*++p;
+	}
+	app = &atab[hashval % ATABSIZE];
+
+	for (; *app; app = &(*app)->next) {
+		if (equal(name, (*app)->name)) {
+			break;
+		}
+	}
+
+	return app;
+}
diff --git a/ash/alias.h b/dash/alias.h
similarity index 89%
rename from ash/alias.h
rename to dash/alias.h
index 7ce25f4..fb841d6 100644
--- a/ash/alias.h
+++ b/dash/alias.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: alias.h,v 1.6 2003/08/07 09:05:29 agc Exp $	*/
-
 /*-
  * Copyright (c) 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -35,6 +35,7 @@
  */
 
 #define ALIASINUSE	1
+#define ALIASDEAD	2
 
 struct alias {
 	struct alias *next;
@@ -43,8 +44,9 @@
 	int flag;
 };
 
-struct alias *lookupalias(char *, int);
-char *get_alias_text(char *);
+struct alias *lookupalias(const char *, int);
 int aliascmd(int, char **);
 int unaliascmd(int, char **);
 void rmaliases(void);
+int unalias(const char *);
+void printalias(const struct alias *);
diff --git a/ash/arith.y b/dash/arith.y
similarity index 78%
rename from ash/arith.y
rename to dash/arith.y
index ef19459..07b0b39 100644
--- a/ash/arith.y
+++ b/dash/arith.y
@@ -1,9 +1,9 @@
 %{
-/*	$NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $	*/
-
 /*-
  * Copyright (c) 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -33,20 +33,6 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)arith.y	8.3 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $");
-#endif
-#endif /* not lint */
-
 #include <stdlib.h>
 #include "expand.h"
 #include "shell.h"
@@ -56,10 +42,13 @@
 
 const char *arith_buf, *arith_startbuf;
 
+#ifndef YYBISON
+int yyparse(void);
+#endif
 void yyerror(const char *);
 #ifdef TESTARITH
 int main(int , char *[]);
-int error(char *);
+int sh_error(char *);
 #endif
 
 %}
@@ -80,13 +69,13 @@
 
 exp:	expr {
 			return ($1);
-		}
+	}
 	;
 
 
 expr:	ARITH_LPAREN expr ARITH_RPAREN { $$ = $2; }
-	| expr ARITH_OR expr	{ $$ = $1 ? $1 : $3 ? $3 : 0; }
-	| expr ARITH_AND expr	{ $$ = $1 ? ( $3 ? $3 : 0 ) : 0; }
+	| expr ARITH_OR expr	{ $$ = $1 || $3; }
+	| expr ARITH_AND expr	{ $$ = $1 && $3; }
 	| expr ARITH_BOR expr	{ $$ = $1 | $3; }
 	| expr ARITH_BXOR expr	{ $$ = $1 ^ $3; }
 	| expr ARITH_BAND expr	{ $$ = $1 & $3; }
@@ -105,12 +94,12 @@
 			if ($3 == 0)
 				yyerror("division by zero");
 			$$ = $1 / $3;
-			}
+		}
 	| expr ARITH_REM expr   {
 			if ($3 == 0)
 				yyerror("division by zero");
 			$$ = $1 % $3;
-			}
+		}
 	| ARITH_NOT expr	{ $$ = !($2); }
 	| ARITH_BNOT expr	{ $$ = ~($2); }
 	| ARITH_SUB expr %prec ARITH_UNARYMINUS { $$ = -($2); }
@@ -135,46 +124,6 @@
 }
 
 
-/*
- *  The exp(1) builtin.
- */
-int
-expcmd(argc, argv)
-	int argc;
-	char **argv;
-{
-	const char *p;
-	char *concat;
-	char **ap;
-	long i;
-
-	if (argc > 1) {
-		p = argv[1];
-		if (argc > 2) {
-			/*
-			 * concatenate arguments
-			 */
-			STARTSTACKSTR(concat);
-			ap = argv + 2;
-			for (;;) {
-				while (*p)
-					STPUTC(*p++, concat);
-				if ((p = *ap++) == NULL)
-					break;
-				STPUTC(' ', concat);
-			}
-			STPUTC('\0', concat);
-			p = grabstackstr(concat);
-		}
-	} else
-		p = "";
-
-	i = arith(p);
-
-	out1fmt("%ld\n", i);
-	return (! i);
-}
-
 /*************************/
 #ifdef TEST_ARITH
 #include <stdio.h>
@@ -183,7 +132,7 @@
 {
 	printf("%d\n", exp(argv[1]));
 }
-error(s)
+sh_error(s)
 	char *s;
 {
 	fprintf(stderr, "exp: %s\n", s);
@@ -196,8 +145,11 @@
 	const char *s;
 {
 
+#ifndef YYBISON
+	yyerrok;
+#endif
 	yyclearin;
 	arith_lex_reset();	/* reprime lex */
-	error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
+	sh_error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
 	/* NOTREACHED */
 }
diff --git a/ash/arith_lex.l b/dash/arith_lex.l
similarity index 79%
rename from ash/arith_lex.l
rename to dash/arith_lex.l
index fd222da..85e170c 100644
--- a/ash/arith_lex.l
+++ b/dash/arith_lex.l
@@ -1,9 +1,9 @@
 %{
-/*	$NetBSD: arith_lex.l,v 1.12 2003/08/07 09:05:30 agc Exp $	*/
-
 /*-
  * Copyright (c) 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -33,39 +33,23 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)arith_lex.l	8.3 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: arith_lex.l,v 1.12 2003/08/07 09:05:30 agc Exp $");
-#endif
-#endif /* not lint */
-
+#include <stdlib.h>
 #include <unistd.h>
 #include "arith.h"
 #include "error.h"
 #include "expand.h"
 
 extern int yylval;
-extern char *arith_buf, *arith_startbuf;
+extern const char *arith_buf, *arith_startbuf;
 #undef YY_INPUT
 #define YY_INPUT(buf,result,max) \
 	result = (*buf = *arith_buf++) ? 1 : YY_NULL;
 #define YY_NO_UNPUT
 %}
-%option noyywrap
 
 %%
 [ \t\n]	{ ; }
-0x[0-9a-fA-F]+	{ yylval = strtol(yytext, 0, 16); return(ARITH_NUM); }
-0[0-7]*		{ yylval = strtol(yytext, 0, 8); return(ARITH_NUM); }
-[1-9][0-9]*	{ yylval = strtol(yytext, 0, 10); return(ARITH_NUM); }
+[0-9]+	{ yylval = strtoll(yytext, 0, 0); return(ARITH_NUM); }
 "("	{ return(ARITH_LPAREN); }
 ")"	{ return(ARITH_RPAREN); }
 "||"	{ return(ARITH_OR); }
@@ -88,7 +72,7 @@
 "-"	{ return(ARITH_SUB); }
 "~"	{ return(ARITH_BNOT); }
 "!"	{ return(ARITH_NOT); }
-.	{ error("arith: syntax error: \"%s\"\n", arith_startbuf); }
+.	{ error("arith: syntax error: \"%s\"", arith_startbuf); }
 %%
 
 void
diff --git a/dash/arith_yylex.c b/dash/arith_yylex.c
new file mode 100644
index 0000000..4fa2051
--- /dev/null
+++ b/dash/arith_yylex.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 2002
+ *	Herbert Xu.
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include "arith.h"
+#include "expand.h"
+#include "error.h"
+
+extern int yylval;
+extern const char *arith_buf, *arith_startbuf;
+
+int
+yylex()
+{
+	int value;
+	const char *buf = arith_buf;
+
+	for (;;) {
+		switch (*buf) {
+		case ' ':
+		case '\t':
+		case '\n':
+			buf++;
+			continue;
+		default:
+err:
+			sh_error("arith: syntax error: \"%s\"", arith_startbuf);
+			/* NOTREACHED */
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			yylval = strtoll(buf, (char **) &arith_buf, 0);
+			return ARITH_NUM;
+		case '=':
+			if (*++buf != '=') {
+				goto err;
+			}
+			value = ARITH_EQ;
+			break;
+		case '>':
+			switch (*++buf) {
+			case '=':
+				value = ARITH_GE;
+				break;
+			case '>':
+				value = ARITH_RSHIFT;
+				break;
+			default:
+				value = ARITH_GT;
+				goto out;
+			}
+			break;
+		case '<':
+			switch (*++buf) {
+			case '=':
+				value = ARITH_LE;
+				break;
+			case '<':
+				value = ARITH_LSHIFT;
+				break;
+			default:
+				value = ARITH_LT;
+				goto out;
+			}
+			break;
+		case '|':
+			if (*++buf != '|') {
+				value = ARITH_BOR;
+				goto out;
+			}
+			value = ARITH_OR;
+			break;
+		case '&':
+			if (*++buf != '&') {
+				value = ARITH_BAND;
+				goto out;
+			}
+			value = ARITH_AND;
+			break;
+		case '!':
+			if (*++buf != '=') {
+				value = ARITH_NOT;
+				goto out;
+			}
+			value = ARITH_NE;
+			break;
+		case 0:
+			value = 0;
+			goto out;
+		case '(':
+			value = ARITH_LPAREN;
+			break;
+		case ')':
+			value = ARITH_RPAREN;
+			break;
+		case '*':
+			value = ARITH_MUL;
+			break;
+		case '/':
+			value = ARITH_DIV;
+			break;
+		case '%':
+			value = ARITH_REM;
+			break;
+		case '+':
+			value = ARITH_ADD;
+			break;
+		case '-':
+			value = ARITH_SUB;
+			break;
+		case '~':
+			value = ARITH_BNOT;
+			break;
+		case '^':
+			value = ARITH_BXOR;
+			break;
+		}
+		break;
+	}
+
+	buf++;
+out:
+	arith_buf = buf;
+	return value;
+}
diff --git a/ash/bltin/bltin.h b/dash/bltin/bltin.h
similarity index 89%
rename from ash/bltin/bltin.h
rename to dash/bltin/bltin.h
index b8f9d75..f5ac06f 100644
--- a/ash/bltin/bltin.h
+++ b/dash/bltin/bltin.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: bltin.h,v 1.11 2003/08/07 09:05:40 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -42,9 +42,12 @@
 
 #include "../shell.h"
 #include "../mystring.h"
+#include "../options.h"
 #ifdef SHELL
+#include "../memalloc.h"
 #include "../output.h"
 #include "../error.h"
+#ifndef USE_GLIBC_STDIO
 #undef stdout
 #undef stderr
 #undef putc
@@ -60,15 +63,12 @@
 #define fputs outstr
 #define fflush flushout
 #define fileno(f) ((f)->fd)
+#define ferror outerr
+#endif
 #define INITARGS(argv)
-#define	err sh_err
-#define	verr sh_verr
-#define	errx sh_errx
-#define	verrx sh_verrx
+#define	error sh_error
 #define	warn sh_warn
-#define	vwarn sh_vwarn
 #define	warnx sh_warnx
-#define	vwarnx sh_vwarnx
 #define exit sh_exit
 #define setprogname(s)
 #define getprogname() commandname
@@ -83,11 +83,6 @@
 #define INITARGS(argv)	if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
 #endif
 
-pointer stalloc(int);
-void error(const char *, ...);
-void sh_warnx(const char *, ...);
-void sh_exit(int) __attribute__((__noreturn__));
-
 int echocmd(int, char **);
 
 
diff --git a/ash/bltin/echo.1 b/dash/bltin/echo.1
similarity index 97%
rename from ash/bltin/echo.1
rename to dash/bltin/echo.1
index 7e71fa3..fbc7fb4 100644
--- a/ash/bltin/echo.1
+++ b/dash/bltin/echo.1
@@ -1,7 +1,7 @@
-.\"	$NetBSD: echo.1,v 1.13 2003/08/07 09:05:40 agc Exp $
-.\"
 .\" Copyright (c) 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
+.\" Copyright (c) 1997-2005
+.\"	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 .\"
 .\" This code is derived from software contributed to Berkeley by
 .\" Kenneth Almquist.
diff --git a/dash/bltin/printf.1 b/dash/bltin/printf.1
new file mode 100644
index 0000000..3873173
--- /dev/null
+++ b/dash/bltin/printf.1
@@ -0,0 +1,354 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\"	The Regents of the University of California.  All rights reserved.
+.\" Copyright (c) 1997-2005
+.\"	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"	from: @(#)printf.1	8.1 (Berkeley) 6/6/93
+.\"
+.Dd November 5, 1993
+.Dt PRINTF 1
+.Os
+.Sh NAME
+.Nm printf
+.Nd formatted output
+.Sh SYNOPSIS
+.Nm
+.Ar format
+.Op Ar arguments  ...
+.Sh DESCRIPTION
+.Nm
+formats and prints its arguments, after the first, under control
+of the
+.Ar format  .
+The
+.Ar format
+is a character string which contains three types of objects: plain characters,
+which are simply copied to standard output, character escape sequences which
+are converted and copied to the standard output, and format specifications,
+each of which causes printing of the next successive
+.Ar argument  .
+.Pp
+The
+.Ar arguments
+after the first are treated as strings if the corresponding format is
+either
+.Cm b ,
+.Cm B ,
+.Cm c
+or
+.Cm s ;
+otherwise it is evaluated as a C constant, with the following extensions:
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+A leading plus or minus sign is allowed.
+.It
+If the leading character is a single or double quote, the value is the
+.Tn ASCII
+code of the next character.
+.El
+.Pp
+The format string is reused as often as necessary to satisfy the
+.Ar arguments  .
+Any extra format specifications are evaluated with zero or the null
+string.
+.Pp
+Character escape sequences are in backslash notation as defined in
+.St -ansiC .
+The characters and their meanings are as follows:
+.Bl -tag -width Ds -offset indent
+.It Cm \ee
+Write an \*[Lt]escape\*[Gt] character.
+.It Cm \ea
+Write a \*[Lt]bell\*[Gt] character.
+.It Cm \eb
+Write a \*[Lt]backspace\*[Gt] character.
+.It Cm \ef
+Write a \*[Lt]form-feed\*[Gt] character.
+.It Cm \en
+Write a \*[Lt]new-line\*[Gt] character.
+.It Cm \er
+Write a \*[Lt]carriage return\*[Gt] character.
+.It Cm \et
+Write a \*[Lt]tab\*[Gt] character.
+.It Cm \ev
+Write a \*[Lt]vertical tab\*[Gt] character.
+.It Cm \e\'
+Write a \*[Lt]single quote\*[Gt] character.
+.It Cm \e"
+Write a \*[Lt]double quote\*[Gt] character.
+.It Cm \e\e
+Write a backslash character.
+.It Cm \e Ns Ar num
+Write an 8\-bit character whose
+.Tn ASCII
+value is the 1\-, 2\-, or 3\-digit octal number
+.Ar num .
+.It Cm \ex Ns Ar xx
+Write an 8\-bit character whose
+.Tn ASCII
+value is the 1\- or 2\-digit hexadecimal number
+.Ar xx .
+.El
+.Pp
+Each format specification is introduced by the percent character
+(``%'').
+The remainder of the format specification includes,
+in the following order:
+.Bl -tag -width Ds
+.It "Zero or more of the following flags:"
+.Bl -tag -width Ds
+.It Cm #
+A `#' character
+specifying that the value should be printed in an ``alternative form''.
+For
+.Cm b ,
+.Cm c ,
+.Cm d ,
+and
+.Cm s
+formats, this option has no effect.
+For the
+.Cm o
+format the precision of the number is increased to force the first
+character of the output string to a zero.
+For the
+.Cm x
+.Pq Cm X
+format, a non-zero result has the string
+.Li 0x
+.Pq Li 0X
+prepended to it.
+For
+.Cm e  ,
+.Cm E ,
+.Cm f  ,
+.Cm g ,
+and
+.Cm G
+formats, the result will always contain a decimal point, even if no
+digits follow the point (normally, a decimal point only appears in the
+results of those formats if a digit follows the decimal point).
+For
+.Cm g
+and
+.Cm G
+formats, trailing zeros are not removed from the result as they
+would otherwise be.
+.\" I turned this off - decided it isn't a valid use of '#'
+.\" For the
+.\" .Cm B
+.\" format, backslash-escape sequences are expanded first;
+.It Cm \&\-
+A minus sign `\-' which specifies
+.Em left adjustment
+of the output in the indicated field;
+.It Cm \&+
+A `+' character specifying that there should always be
+a sign placed before the number when using signed formats.
+.It Sq \&\ \&
+A space specifying that a blank should be left before a positive number
+for a signed format.
+A `+' overrides a space if both are used;
+.It Cm \&0
+A zero `0' character indicating that zero-padding should be used
+rather than blank-padding.
+A `\-' overrides a `0' if both are used;
+.El
+.It "Field Width:"
+An optional digit string specifying a
+.Em field width ;
+if the output string has fewer characters than the field width it will
+be blank-padded on the left (or right, if the left-adjustment indicator
+has been given) to make up the field width (note that a leading zero
+is a flag, but an embedded zero is part of a field width);
+.It Precision :
+An optional period,
+.Sq Cm \&.\& ,
+followed by an optional digit string giving a
+.Em precision
+which specifies the number of digits to appear after the decimal point,
+for
+.Cm e
+and
+.Cm f
+formats, or the maximum number of characters to be printed
+from a string
+.Sm off
+.Pf ( Cm b No ,
+.Sm on
+.Cm B
+and
+.Cm s
+formats); if the digit string is missing, the precision is treated
+as zero;
+.It Format :
+A character which indicates the type of format to use (one of
+.Cm diouxXfwEgGbBcs ) .
+.El
+.Pp
+A field width or precision may be
+.Sq Cm \&*
+instead of a digit string.
+In this case an
+.Ar argument
+supplies the field width or precision.
+.Pp
+The format characters and their meanings are:
+.Bl -tag -width Fl
+.It Cm diouXx
+The
+.Ar argument
+is printed as a signed decimal (d or i), unsigned octal, unsigned decimal,
+or unsigned hexadecimal (X or x), respectively.
+.It Cm f
+The
+.Ar argument
+is printed in the style
+.Sm off
+.Pf [\-]ddd Cm \&. No ddd
+.Sm on
+where the number of d's
+after the decimal point is equal to the precision specification for
+the argument.
+If the precision is missing, 6 digits are given; if the precision
+is explicitly 0, no digits and no decimal point are printed.
+.It Cm eE
+The
+.Ar argument
+is printed in the style
+.Sm off
+.Pf [\-]d Cm \&. No ddd Cm e No \\*(Pmdd
+.Sm on
+where there
+is one digit before the decimal point and the number after is equal to
+the precision specification for the argument; when the precision is
+missing, 6 digits are produced.
+An upper-case E is used for an `E' format.
+.It Cm gG
+The
+.Ar argument
+is printed in style
+.Cm f
+or in style
+.Cm e
+.Pq Cm E
+whichever gives full precision in minimum space.
+.It Cm b
+Characters from the string
+.Ar argument
+are printed with backslash-escape sequences expanded.
+.br
+The following additional backslash-escape sequences are supported:
+.Bl -tag -width Ds
+.It Cm \ec
+Causes
+.Nm
+to ignore any remaining characters in the string operand containing it,
+any remaining string operands, and any additional characters in
+the format operand.
+.It Cm \e0 Ns Ar num
+Write an 8\-bit character whose
+.Tn ASCII
+value is the 1\-, 2\-, or 3\-digit
+octal number
+.Ar num .
+.It Cm \e^ Ns Ar c
+Write the control character 
+.Ar c .
+Generates characters `\e000' through `\e037`, and `\e177' (from `\e^?').
+.It Cm \eM\- Ns Ar c
+Write the character 
+.Ar c
+with the 8th bit set.
+Generates characters `\e241' through `\e376`.
+.It Cm \eM^ Ns Ar c
+Write the control character 
+.Ar c
+with the 8th bit set.
+Generates characters `\e000' through `\e037`, and `\e177' (from `\eM^?').
+.El
+.It Cm B
+Characters from the string
+.Ar argument
+are printed with unprintable characters backslash-escaped using the
+.Sm off
+.Pf ` Cm \e Ar c No ',
+.Pf ` Cm \e^ Ar c No ',
+.Pf ` Cm \eM\- Ar c No '
+or
+.Pf ` Cm \eM^ Ar c No ',
+.Sm on
+formats described above.
+.It Cm c
+The first character of
+.Ar argument
+is printed.
+.It Cm s
+Characters from the string
+.Ar argument
+are printed until the end is reached or until the number of characters
+indicated by the precision specification is reached; if the
+precision is omitted, all characters in the string are printed.
+.It Cm \&%
+Print a `%'; no argument is used.
+.El
+.Pp
+In no case does a non-existent or small field width cause truncation of
+a field; padding takes place only if the specified field width exceeds
+the actual width.
+.Sh EXIT STATUS
+.Nm
+exits 0 on success, 1 on failure.
+.Sh SEE ALSO
+.Xr echo 1 ,
+.Xr printf 3 ,
+.Xr printf 9
+.Xr vis 3
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.1-2001 .
+.Pp
+Support for the floating point formats and `*' as a field width and precision
+are optional in POSIX.
+.Pp
+The behaviour of the %B format and the \e', \e", \exxx, \ee and
+\e[M][\-|^]c escape sequences are undefined in POSIX.
+.Sh BUGS
+Since the floating point numbers are translated from
+.Tn ASCII
+to floating-point and
+then back again, floating-point precision may be lost.
+.Pp
+Hexadecimal character constants are restricted to, and should be specified
+as, two character constants.  This is contrary to the ISO C standard but
+does guarantee detection of the end of the constant.
diff --git a/dash/bltin/printf.c b/dash/bltin/printf.c
new file mode 100644
index 0000000..fc73dfb
--- /dev/null
+++ b/dash/bltin/printf.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static char	*conv_escape_str(char *);
+static char	*conv_escape(char *, int *);
+static int	 getchr(void);
+static double	 getdouble(void);
+static intmax_t	 getintmax(void);
+static uintmax_t getuintmax(void);
+static char	*getstr(void);
+static char	*mklong(const char *, const char *);
+static void      check_conversion(const char *, const char *);
+
+static int	rval;
+static char  **gargv;
+
+#define isodigit(c)	((c) >= '0' && (c) <= '7')
+#define octtobin(c)	((c) - '0')
+
+#include "bltin.h"
+#include "system.h"
+
+#define PF(f, func) { \
+	switch ((char *)param - (char *)array) { \
+	default: \
+		(void)printf(f, array[0], array[1], func); \
+		break; \
+	case sizeof(*param): \
+		(void)printf(f, array[0], func); \
+		break; \
+	case 0: \
+		(void)printf(f, func); \
+		break; \
+	} \
+}
+
+int printfcmd(int argc, char *argv[])
+{
+	char *fmt;
+	char *format;
+	int ch;
+
+	rval = 0;
+
+	nextopt(nullstr);
+
+	argv = argptr;
+	format = *argv;
+
+	if (!format) {
+		warnx("usage: printf format [arg ...]");
+		goto err;
+	}
+
+	gargv = ++argv;
+
+#define SKIP1	"#-+ 0"
+#define SKIP2	"*0123456789"
+	do {
+		/*
+		 * Basic algorithm is to scan the format string for conversion
+		 * specifications -- once one is found, find out if the field
+		 * width or precision is a '*'; if it is, gather up value. 
+		 * Note, format strings are reused as necessary to use up the
+		 * provided arguments, arguments of zero/null string are 
+		 * provided to use up the format string.
+		 */
+
+		/* find next format specification */
+		for (fmt = format; (ch = *fmt++) ;) {
+			char *start;
+			char nextch;
+			int array[2];
+			int *param;
+
+			if (ch == '\\') {
+				int c_ch;
+				fmt = conv_escape(fmt, &c_ch);
+				ch = c_ch;
+				goto pc;
+			}
+			if (ch != '%' || (*fmt == '%' && (++fmt || 1))) {
+pc:
+				putchar(ch);
+				continue;
+			}
+
+			/* Ok - we've found a format specification,
+			   Save its address for a later printf(). */
+			start = fmt - 1;
+			param = array;
+
+			/* skip to field width */
+			fmt += strspn(fmt, SKIP1);
+			if (*fmt == '*')
+				*param++ = getintmax();
+
+			/* skip to possible '.', get following precision */
+			fmt += strspn(fmt, SKIP2);
+			if (*fmt == '.')
+				++fmt;
+			if (*fmt == '*')
+				*param++ = getintmax();
+
+			fmt += strspn(fmt, SKIP2);
+
+			ch = *fmt;
+			if (!ch) {
+				warnx("missing format character");
+				goto err;
+			}
+			/* null terminate format string to we can use it
+			   as an argument to printf. */
+			nextch = fmt[1];
+			fmt[1] = 0;
+			switch (ch) {
+
+			case 'b': {
+				char *p = conv_escape_str(getstr());
+				*fmt = 's';
+				PF(start, p);
+				/* escape if a \c was encountered */
+				if (rval & 0x100)
+					goto out;
+				*fmt = 'b';
+				break;
+			}
+			case 'c': {
+				int p = getchr();
+				PF(start, p);
+				break;
+			}
+			case 's': {
+				char *p = getstr();
+				PF(start, p);
+				break;
+			}
+			case 'd':
+			case 'i': {
+				intmax_t p = getintmax();
+				char *f = mklong(start, fmt);
+				PF(f, p);
+				break;
+			}
+			case 'o':
+			case 'u':
+			case 'x':
+			case 'X': {
+				uintmax_t p = getuintmax();
+				char *f = mklong(start, fmt);
+				PF(f, p);
+				break;
+			}
+			case 'e':
+			case 'E':
+			case 'f':
+			case 'g':
+			case 'G': {
+				double p = getdouble();
+				PF(start, p);
+				break;
+			}
+			default:
+				warnx("%s: invalid directive", start);
+				goto err;
+			}
+			*++fmt = nextch;
+		}
+	} while (gargv != argv && *gargv);
+
+out:
+	return (rval & ~0x100);
+err:
+	return 1;
+}
+
+
+/*
+ * Print SysV echo(1) style escape string 
+ *	Halts processing string if a \c escape is encountered.
+ */
+static char *
+conv_escape_str(char *str)
+{
+	int ch;
+	char *cp;
+
+	/* convert string into a temporary buffer... */
+	STARTSTACKSTR(cp);
+
+	do {
+		int c;
+
+		ch = *str++;
+		if (ch != '\\')
+			continue;
+
+		ch = *str++;
+		if (ch == 'c') {
+			/* \c as in SYSV echo - abort all processing.... */
+			rval |= 0x100;
+			ch = 0;
+			continue;
+		}
+
+		/* 
+		 * %b string octal constants are not like those in C.
+		 * They start with a \0, and are followed by 0, 1, 2, 
+		 * or 3 octal digits. 
+		 */
+		if (ch == '0') {
+			unsigned char i;
+			i = 3;
+			ch = 0;
+			do {
+				unsigned k = octtobin(*str);
+				if (k > 7)
+					break;
+				str++;
+				ch <<= 3;
+				ch += k;
+			} while (--i);
+			continue;
+		}
+
+		/* Finally test for sequences valid in the format string */
+		str = conv_escape(str - 1, &c);
+		ch = c;
+	} while (STPUTC(ch, cp), ch);
+
+	return stackblock();
+}
+
+/*
+ * Print "standard" escape characters 
+ */
+static char *
+conv_escape(char *str, int *conv_ch)
+{
+	int value;
+	int ch;
+
+	ch = *str;
+
+	switch (ch) {
+	default:
+	case 0:
+		value = '\\';
+		goto out;
+
+	case '0': case '1': case '2': case '3':
+	case '4': case '5': case '6': case '7':
+		ch = 3;
+		value = 0;
+		do {
+			value <<= 3;
+			value += octtobin(*str++);
+		} while (isodigit(*str) && --ch);
+		goto out;
+
+	case '\\':	value = '\\';	break;	/* backslash */
+	case 'a':	value = '\a';	break;	/* alert */
+	case 'b':	value = '\b';	break;	/* backspace */
+	case 'f':	value = '\f';	break;	/* form-feed */
+	case 'n':	value = '\n';	break;	/* newline */
+	case 'r':	value = '\r';	break;	/* carriage-return */
+	case 't':	value = '\t';	break;	/* tab */
+	case 'v':	value = '\v';	break;	/* vertical-tab */
+	}
+
+	str++;
+out:
+	*conv_ch = value;
+	return str;
+}
+
+static char *
+mklong(const char *str, const char *ch)
+{
+	char *copy;
+	size_t len;	
+
+	len = ch - str + 3;
+	STARTSTACKSTR(copy);
+	copy = makestrspace(len, copy);
+	memcpy(copy, str, len - 3);
+	copy[len - 3] = 'j';
+	copy[len - 2] = *ch;
+	copy[len - 1] = '\0';
+	return (copy);	
+}
+
+static int
+getchr(void)
+{
+	int val = 0;
+
+	if (*gargv)
+		val = **gargv++;
+	return val;
+}
+
+static char *
+getstr(void)
+{
+	char *val = nullstr;
+
+	if (*gargv)
+		val = *gargv++;
+	return val;
+}
+
+static intmax_t
+getintmax(void)
+{
+	intmax_t val = 0;
+	char *cp, *ep;
+
+	cp = *gargv;
+	if (cp == NULL)
+		goto out;
+	gargv++;
+
+	val = (unsigned char) cp[1];
+	if (*cp == '\"' || *cp == '\'')
+		goto out;
+
+	errno = 0;
+	val = strtoimax(cp, &ep, 0);
+	check_conversion(cp, ep);
+out:
+	return val;
+}
+
+static uintmax_t
+getuintmax(void)
+{
+	uintmax_t val = 0;
+	char *cp, *ep;
+
+	cp = *gargv;
+	if (cp == NULL)
+		goto out;
+	gargv++;
+
+	val = (unsigned char) cp[1];
+	if (*cp == '\"' || *cp == '\'')
+		goto out;
+
+	errno = 0;
+	val = strtoumax(cp, &ep, 0);
+	check_conversion(cp, ep);
+out:
+	return val;
+}
+
+static double
+getdouble(void)
+{
+	double val;
+	char *cp, *ep;
+
+	cp = *gargv;
+	if (cp == NULL)
+		return 0;
+	gargv++;
+
+	if (*cp == '\"' || *cp == '\'')
+		return (unsigned char) cp[1];
+
+	errno = 0;
+	val = strtod(cp, &ep);
+	check_conversion(cp, ep);
+	return val;
+}
+
+static void
+check_conversion(const char *s, const char *ep)
+{
+	if (*ep) {
+		if (ep == s)
+			warnx("%s: expected numeric value", s);
+		else
+			warnx("%s: not completely converted", s);
+		rval = 1;
+	} else if (errno == ERANGE) {
+		warnx("%s: %s", s, strerror(ERANGE));
+		rval = 1;
+	}
+}
+
+int
+echocmd(int argc, char **argv)
+{
+	int nonl = 0;
+	struct output *outs = out1;
+
+	if (!*++argv)
+		goto end;
+	if (equal(*argv, "-n")) {
+		nonl = ~nonl;
+		if (!*++argv)
+			goto end;
+	}
+
+	do {
+		char c;
+
+		c = *(*argv)++;
+		if (!c)
+			goto next;
+		if (c != '\\')
+			goto print;
+
+		outstr(conv_escape_str(*argv - 1), outs);
+		if (rval & 0x100)
+			break;
+next:
+		c = ' ';
+		if (!*++argv) {
+end:
+			if (nonl) {
+				break;
+			}
+			c = '\n';
+		}
+print:
+		outc(c, outs);
+	} while (*argv);
+	return 0;
+}
diff --git a/dash/bltin/test.1 b/dash/bltin/test.1
new file mode 100644
index 0000000..42435fb
--- /dev/null
+++ b/dash/bltin/test.1
@@ -0,0 +1,309 @@
+.\" Copyright (c) 1991, 1993
+.\"	The Regents of the University of California.  All rights reserved.
+.\" Copyright (c) 1997-2005
+.\"	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)test.1	8.1 (Berkeley) 5/31/93
+.\"
+.Dd May 31, 1993
+.Dt TEST 1
+.Os
+.Sh NAME
+.Nm test ,
+.Nm \&[
+.Nd condition evaluation utility
+.Sh SYNOPSIS
+.Nm test
+.Ar expression
+.Nm \&[
+.Ar expression Cm ]
+.Sh DESCRIPTION
+The
+.Nm test
+utility evaluates the expression and, if it evaluates
+to true, returns a zero (true) exit status; otherwise
+it returns 1 (false).
+If there is no expression, test also
+returns 1 (false).
+.Pp
+All operators and flags are separate arguments to the
+.Nm test
+utility.
+.Pp
+The following primaries are used to construct expression:
+.Bl -tag -width Ar
+.It Fl b Ar file
+True if
+.Ar file
+exists and is a block special
+file.
+.It Fl c Ar file
+True if
+.Ar file
+exists and is a character
+special file.
+.It Fl d Ar file
+True if
+.Ar file
+exists and is a directory.
+.It Fl e Ar file
+True if
+.Ar file
+exists (regardless of type).
+.It Fl f Ar file
+True if
+.Ar file
+exists and is a regular file.
+.It Fl g Ar file
+True if
+.Ar file
+exists and its set group ID flag
+is set.
+.It Fl h Ar file
+True if
+.Ar file
+exists and is a symbolic link.
+.It Fl k Ar file
+True if
+.Ar file
+exists and its sticky bit is set.
+.It Fl n Ar string
+True if the length of
+.Ar string
+is nonzero.
+.It Fl p Ar file
+True if
+.Ar file
+is a named pipe
+.Po Tn FIFO Pc .
+.It Fl r Ar file
+True if
+.Ar file
+exists and is readable.
+.It Fl s Ar file
+True if
+.Ar file
+exists and has a size greater
+than zero.
+.It Fl t Ar file_descriptor
+True if the file whose file descriptor number
+is
+.Ar file_descriptor
+is open and is associated with a terminal.
+.It Fl u Ar file
+True if
+.Ar file
+exists and its set user ID flag
+is set.
+.It Fl w Ar file
+True if
+.Ar file
+exists and is writable.
+True
+indicates only that the write flag is on.
+The file is not writable on a read-only file
+system even if this test indicates true.
+.It Fl x Ar file
+True if
+.Ar file
+exists and is executable.
+True
+indicates only that the execute flag is on.
+If
+.Ar file
+is a directory, true indicates that
+.Ar file
+can be searched.
+.It Fl z Ar string
+True if the length of
+.Ar string
+is zero.
+.It Fl L Ar file
+True if
+.Ar file
+exists and is a symbolic link.
+This operator is retained for compatibility with previous versions of
+this program.
+Do not rely on its existence; use
+.Fl h
+instead.
+.It Fl O Ar file
+True if
+.Ar file
+exists and its owner matches the effective user id of this process.
+.It Fl G Ar file
+True if
+.Ar file
+exists and its group matches the effective group id of this process.
+.It Fl S Ar file
+True if
+.Ar file
+exists and is a socket.
+.It Ar file1 Fl nt Ar file2
+True if
+.Ar file1
+exists and is newer than
+.Ar file2 .
+.It Ar file1 Fl ot Ar file2
+True if
+.Ar file1
+exists and is older than
+.Ar file2 .
+.It Ar file1 Fl ef Ar file2
+True if
+.Ar file1
+and
+.Ar file2
+exist and refer to the same file.
+.It Ar string
+True if
+.Ar string
+is not the null
+string.
+.It Ar \&s\&1 Cm \&= Ar \&s\&2
+True if the strings
+.Ar \&s\&1
+and
+.Ar \&s\&2
+are identical.
+.It Ar \&s\&1 Cm \&!= Ar \&s\&2
+True if the strings
+.Ar \&s\&1
+and
+.Ar \&s\&2
+are not identical.
+.It Ar \&s\&1 Cm \&\*[Lt] Ar \&s\&2
+True if string
+.Ar \&s\&1
+comes before
+.Ar \&s\&2
+based on the ASCII value of their characters.
+.It Ar \&s\&1 Cm \&\*[Gt] Ar \&s\&2
+True if string
+.Ar \&s\&1
+comes after
+.Ar \&s\&2
+based on the ASCII value of their characters.
+.It Ar \&n\&1 Fl \&eq Ar \&n\&2
+True if the integers
+.Ar \&n\&1
+and
+.Ar \&n\&2
+are algebraically
+equal.
+.It Ar \&n\&1 Fl \&ne Ar \&n\&2
+True if the integers
+.Ar \&n\&1
+and
+.Ar \&n\&2
+are not
+algebraically equal.
+.It Ar \&n\&1 Fl \&gt Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically
+greater than the integer
+.Ar \&n\&2 .
+.It Ar \&n\&1 Fl \&ge Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically
+greater than or equal to the integer
+.Ar \&n\&2 .
+.It Ar \&n\&1 Fl \&lt Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically less
+than the integer
+.Ar \&n\&2 .
+.It Ar \&n\&1 Fl \&le Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically less
+than or equal to the integer
+.Ar \&n\&2 .
+.El
+.Pp
+These primaries can be combined with the following operators:
+.Bl -tag -width Ar
+.It Cm \&! Ar expression
+True if
+.Ar expression
+is false.
+.It Ar expression1 Fl a Ar expression2
+True if both
+.Ar expression1
+and
+.Ar expression2
+are true.
+.It Ar expression1 Fl o Ar expression2
+True if either
+.Ar expression1
+or
+.Ar expression2
+are true.
+.It Cm \&( Ns Ar expression Ns Cm \&)
+True if expression is true.
+.El
+.Pp
+The
+.Fl a
+operator has higher precedence than the
+.Fl o
+operator.
+.Sh GRAMMAR AMBIGUITY
+The
+.Nm test
+grammar is inherently ambiguous.
+In order to assure a degree of consistency, the cases described in
+.St -p1003.2
+section 4.62.4,
+are evaluated consistently according to the rules specified in the
+standards document.
+All other cases are subject to the ambiguity in the command semantics.
+.Sh EXIT STATUS
+The
+.Nm test
+utility exits with one of the following values:
+.Bl -tag -width Ds
+.It 0
+expression evaluated to true.
+.It 1
+expression evaluated to false or expression was
+missing.
+.It \*[Gt]1
+An error occurred.
+.El
+.Sh STANDARDS
+The
+.Nm test
+utility implements a superset of the
+.St -p1003.2
+specification.
diff --git a/ash/bltin/test.c b/dash/bltin/test.c
similarity index 83%
rename from ash/bltin/test.c
rename to dash/bltin/test.c
index 65bd7ff..9b09094 100644
--- a/ash/bltin/test.c
+++ b/dash/bltin/test.c
@@ -1,5 +1,3 @@
-/* $NetBSD: test.c,v 1.24 2001/09/16 19:03:26 wiz Exp $ */
-
 /*
  * test(1); version 7-like  --  author Erik Baalbergen
  * modified by Eric Gisin to be used as built-in.
@@ -10,38 +8,16 @@
  * This program is in the Public Domain.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-__RCSID("$NetBSD: test.c,v 1.24 2001/09/16 19:03:26 wiz Exp $");
-#endif
-
 #include <sys/stat.h>
 #include <sys/types.h>
 
 #include <ctype.h>
-#ifndef __KLIBC__
-#include <err.h>
-#endif
 #include <errno.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdarg.h>
-
-#ifdef __KLIBC__
-/* Values for the second argument to access.
-   These may be OR'd together.  */
-#define	R_OK	4		/* Test for read permission.  */
-#define	W_OK	2		/* Test for write permission.  */
-#define	X_OK	1		/* Test for execute permission.  */
-#define	F_OK	0		/* Test for existence.  */
-#endif
+#include "bltin.h"
 
 /* test(1) accepts the following grammar:
 	oexpr	::= aexpr | aexpr "-o" oexpr ;
@@ -173,41 +149,14 @@
 static int newerf(const char *, const char *);
 static int olderf(const char *, const char *);
 static int equalf(const char *, const char *);
-
-#if defined(SHELL)
-extern void error(const char *, ...) __attribute__((__noreturn__));
-#else
-static void error(const char *, ...) __attribute__((__noreturn__));
-
-static void
-error(const char *msg, ...)
-{
-	va_list ap;
-
-	va_start(ap, msg);
-	verrx(2, msg, ap);
-	/*NOTREACHED*/
-	va_end(ap);
-}
-#endif
-
-#ifdef SHELL
-int testcmd(int, char **);
+static int test_st_mode(const struct stat64 *, int);
+static int bash_group_member(gid_t);
 
 int
 testcmd(int argc, char **argv)
-#else
-int main(int, char *[]);
-
-int
-main(int argc, char *argv[])
-#endif
 {
 	int res;
 
-#ifndef SHELL
-	setprogname(argv[0]);
-#endif
 	if (strcmp(argv[0], "[") == 0) {
 		if (strcmp(argv[--argc], "]"))
 			error("missing ]");
@@ -320,6 +269,11 @@
 		syntax(op->op_text, "argument expected");
 		
 	switch (op->op_num) {
+	default:
+#ifdef DEBUG
+		abort();
+		/* NOTREACHED */
+#endif
 	case STREQ:
 		return strcmp(opnd1, opnd2) == 0;
 	case STRNE:
@@ -346,29 +300,26 @@
 		return olderf (opnd1, opnd2);
 	case FILEQ:
 		return equalf (opnd1, opnd2);
-	default:
-		abort();
-		/* NOTREACHED */
 	}
 }
 
 static int
 filstat(char *nm, enum token mode)
 {
-	struct stat s;
+	struct stat64 s;
 
-	if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
+	if (mode == FILSYM ? lstat64(nm, &s) : stat64(nm, &s))
 		return 0;
 
 	switch (mode) {
 	case FILRD:
-		return access(nm, R_OK) == 0;
+		return test_st_mode(&s, R_OK);
 	case FILWR:
-		return access(nm, W_OK) == 0;
+		return test_st_mode(&s, W_OK);
 	case FILEX:
-		return access(nm, X_OK) == 0;
+		return test_st_mode(&s, X_OK);
 	case FILEXIST:
-		return access(nm, F_OK) == 0;
+		return 1;
 	case FILREG:
 		return S_ISREG(s.st_mode);
 	case FILDIR:
@@ -390,7 +341,7 @@
 	case FILSTCK:
 		return (s.st_mode & S_ISVTX) != 0;
 	case FILGZ:
-		return s.st_size > (off_t)0;
+		return !!s.st_size;
 	case FILUID:
 		return s.st_uid == geteuid();
 	case FILGID:
@@ -497,3 +448,53 @@
 		b1.st_dev == b2.st_dev &&
 		b1.st_ino == b2.st_ino);
 }
+
+/*
+ * Similar to what access(2) does, but uses the effective uid and gid.
+ * Doesn't make the mistake of telling root that any file is executable.
+ * Returns non-zero if the file is accessible.
+ */
+static int
+test_st_mode(const struct stat64 *st, int mode)
+{
+	int euid = geteuid();
+
+	if (euid == 0) {
+		/* Root can read or write any file. */
+		if (mode != X_OK)
+			return 1;
+
+		/* Root can execute any file that has any one of the execute
+		   bits set. */
+		mode = S_IXUSR | S_IXGRP | S_IXOTH;
+	} else if (st->st_uid == euid)
+		mode <<= 6;
+	else if (bash_group_member(st->st_gid))
+		mode <<= 3;
+
+	return st->st_mode & mode;
+}
+
+/* Return non-zero if GID is one that we have in our groups list. */
+static int
+bash_group_member(gid_t gid)
+{
+	register int i;
+	gid_t *group_array;
+	int ngroups;
+
+	/* Short-circuit if possible, maybe saving a call to getgroups(). */
+	if (gid == getgid() || gid == getegid())
+		return (1);
+
+	ngroups = getgroups(0, NULL);
+	group_array = stalloc(ngroups * sizeof(gid_t));
+	getgroups(ngroups, group_array);
+
+	/* Search through the list looking for GID. */
+	for (i = 0; i < ngroups; i++)
+		if (gid == group_array[i])
+			return (1);
+
+	return (0);
+}
diff --git a/dash/bltin/times.c b/dash/bltin/times.c
new file mode 100644
index 0000000..8eabc1f
--- /dev/null
+++ b/dash/bltin/times.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 1999 Herbert Xu <herbert@gondor.apana.org.au>
+ * This file contains code for the times builtin.
+ */
+
+#include <sys/times.h>
+#include <unistd.h>
+#ifdef USE_GLIBC_STDIO
+#include <stdio.h>
+#else
+#include "bltin.h"
+#endif
+#include "system.h"
+
+int timescmd() {
+	struct tms buf;
+	long int clk_tck = sysconf(_SC_CLK_TCK);
+
+	times(&buf);
+	printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
+	       (int) (buf.tms_utime / clk_tck / 60),
+	       ((double) buf.tms_utime) / clk_tck,
+	       (int) (buf.tms_stime / clk_tck / 60),
+	       ((double) buf.tms_stime) / clk_tck,
+	       (int) (buf.tms_cutime / clk_tck / 60),
+	       ((double) buf.tms_cutime) / clk_tck,
+	       (int) (buf.tms_cstime / clk_tck / 60),
+	       ((double) buf.tms_cstime) / clk_tck);
+	return 0;
+}
diff --git a/dash/builtins.def.in b/dash/builtins.def.in
new file mode 100644
index 0000000..362ff3f
--- /dev/null
+++ b/dash/builtins.def.in
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)builtins.def	8.4 (Berkeley) 5/4/95
+ */
+
+/*
+ * This file lists all the builtin commands.  The first column is the name
+ * of a C routine.
+ * The -a flag specifies that this is a posix 'assignment builtin' command.
+ * The -s flag specifies that this is a posix 'special builtin' command.
+ * The -u flag specifies that this is a posix 'standard utility'.
+ * The rest of the line specifies the command name or names used to run
+ * the command.
+ */
+
+#ifndef JOBS
+#define JOBS 1
+#endif
+
+#if JOBS
+bgcmd		-u bg
+fgcmd		-u fg
+#endif
+
+#ifndef SMALL
+histcmd		-u fc
+#endif
+
+breakcmd	-s break -s continue
+cdcmd		-u cd chdir
+commandcmd	-u command
+dotcmd		-s .
+echocmd		echo
+evalcmd		-s eval
+execcmd		-s exec
+exitcmd		-s exit
+exportcmd	-as export -as readonly
+falsecmd	-u false
+getoptscmd	-u getopts
+hashcmd		hash
+jobscmd		-u jobs
+localcmd	-a local
+printfcmd	printf
+pwdcmd		pwd
+readcmd		-u read
+returncmd	-s return
+setcmd		-s set
+shiftcmd	-s shift
+timescmd	-s times
+trapcmd		-s trap
+truecmd		-s : -u true
+typecmd		type
+umaskcmd	-u umask
+unaliascmd	-u unalias
+unsetcmd	-s unset
+waitcmd		-u wait
+aliascmd	-au alias
+#ifdef HAVE_GETRLIMIT
+ulimitcmd	ulimit
+#endif
+testcmd		test [
+killcmd		-u kill
diff --git a/dash/cd.c b/dash/cd.c
new file mode 100644
index 0000000..1849c69
--- /dev/null
+++ b/dash/cd.c
@@ -0,0 +1,303 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * The cd and pwd commands.
+ */
+
+#include "shell.h"
+#include "var.h"
+#include "nodes.h"	/* for jobs.h */
+#include "jobs.h"
+#include "options.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "exec.h"
+#include "redir.h"
+#include "mystring.h"
+#include "show.h"
+#include "cd.h"
+
+#define CD_PHYSICAL 1
+#define CD_PRINT 2
+
+STATIC int docd(const char *, int);
+STATIC const char *updatepwd(const char *);
+STATIC char *getpwd(void);
+STATIC int cdopt(void);
+
+STATIC char *curdir = nullstr;		/* current working directory */
+STATIC char *physdir = nullstr;		/* physical working directory */
+
+STATIC int
+cdopt()
+{
+	int flags = 0;
+	int i, j;
+
+	j = 'L';
+	while ((i = nextopt("LP"))) {
+		if (i != j) {
+			flags ^= CD_PHYSICAL;
+			j = i;
+		}
+	}
+
+	return flags;
+}
+
+int
+cdcmd(int argc, char **argv)
+{
+	const char *dest;
+	const char *path;
+	const char *p;
+	char c;
+	struct stat statb;
+	int flags;
+
+	flags = cdopt();
+	dest = *argptr;
+	if (!dest)
+		dest = bltinlookup(homestr);
+	else if (dest[0] == '-' && dest[1] == '\0') {
+		dest = bltinlookup("OLDPWD");
+		flags |= CD_PRINT;
+	}
+	if (!dest)
+		dest = nullstr;
+	if (*dest == '/')
+		goto step7;
+	if (*dest == '.') {
+		c = dest[1];
+dotdot:
+		switch (c) {
+		case '\0':
+		case '/':
+			goto step6;
+		case '.':
+			c = dest[2];
+			if (c != '.')
+				goto dotdot;
+		}
+	}
+	if (!*dest)
+		dest = ".";
+	if (!(path = bltinlookup("CDPATH"))) {
+step6:
+step7:
+		p = dest;
+		goto docd;
+	}
+	do {
+		c = *path;
+		p = padvance(&path, dest);
+		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
+			if (c && c != ':')
+				flags |= CD_PRINT;
+docd:
+			if (!docd(p, flags))
+				goto out;
+			break;
+		}
+	} while (path);
+	sh_error("can't cd to %s", dest);
+	/* NOTREACHED */
+out:
+	if (flags & CD_PRINT)
+		out1fmt(snlfmt, curdir);
+	return 0;
+}
+
+
+/*
+ * Actually do the chdir.  We also call hashcd to let the routines in exec.c
+ * know that the current directory has changed.
+ */
+
+STATIC int
+docd(const char *dest, int flags)
+{
+	const char *dir = 0;
+	int err;
+
+	TRACE(("docd(\"%s\", %d) called\n", dest, flags));
+
+	INTOFF;
+	if (!(flags & CD_PHYSICAL)) {
+		dir = updatepwd(dest);
+		if (dir)
+			dest = dir;
+	}
+	err = chdir(dest);
+	if (err)
+		goto out;
+	setpwd(dir, 1);
+	hashcd();
+out:
+	INTON;
+	return err;
+}
+
+
+/*
+ * Update curdir (the name of the current directory) in response to a
+ * cd command.
+ */
+
+STATIC const char *
+updatepwd(const char *dir)
+{
+	char *new;
+	char *p;
+	char *cdcomppath;
+	const char *lim;
+
+	cdcomppath = sstrdup(dir);
+	STARTSTACKSTR(new);
+	if (*dir != '/') {
+		if (curdir == nullstr)
+			return 0;
+		new = stputs(curdir, new);
+	}
+	new = makestrspace(strlen(dir) + 2, new);
+	lim = stackblock() + 1;
+	if (*dir != '/') {
+		if (new[-1] != '/')
+			USTPUTC('/', new);
+		if (new > lim && *lim == '/')
+			lim++;
+	} else {
+		USTPUTC('/', new);
+		cdcomppath++;
+		if (dir[1] == '/' && dir[2] != '/') {
+			USTPUTC('/', new);
+			cdcomppath++;
+			lim++;
+		}
+	}
+	p = strtok(cdcomppath, "/");
+	while (p) {
+		switch(*p) {
+		case '.':
+			if (p[1] == '.' && p[2] == '\0') {
+				while (new > lim) {
+					STUNPUTC(new);
+					if (new[-1] == '/')
+						break;
+				}
+				break;
+			} else if (p[1] == '\0')
+				break;
+			/* fall through */
+		default:
+			new = stputs(p, new);
+			USTPUTC('/', new);
+		}
+		p = strtok(0, "/");
+	}
+	if (new > lim)
+		STUNPUTC(new);
+	*new = 0;
+	return stackblock();
+}
+
+
+#define MAXPWD 256
+
+/*
+ * Find out what the current directory is. If we already know the current
+ * directory, this routine returns immediately.
+ */
+inline
+STATIC char *
+getpwd()
+{
+	char *dir = getcwd(0, 0);
+	return dir ? dir : nullstr;
+}
+
+int
+pwdcmd(int argc, char **argv)
+{
+	int flags;
+	const char *dir = curdir;
+
+	flags = cdopt();
+	if (flags) {
+		if (physdir == nullstr)
+			setpwd(dir, 0);
+		dir = physdir;
+	}
+	out1fmt(snlfmt, dir);
+	return 0;
+}
+
+void
+setpwd(const char *val, int setold)
+{
+	char *oldcur, *dir;
+
+	oldcur = dir = curdir;
+
+	if (setold) {
+		setvar("OLDPWD", oldcur, VEXPORT);
+	}
+	INTOFF;
+	if (physdir != nullstr) {
+		if (physdir != oldcur)
+			free(physdir);
+		physdir = nullstr;
+	}
+	if (oldcur == val || !val) {
+		char *s = getpwd();
+		physdir = s;
+		if (!val)
+			dir = s;
+	} else
+		dir = savestr(val);
+	if (oldcur != dir && oldcur != nullstr) {
+		free(oldcur);
+	}
+	curdir = dir;
+	INTON;
+	setvar("PWD", dir, VEXPORT);
+}
diff --git a/ash/cd.h b/dash/cd.h
similarity index 92%
rename from ash/cd.h
rename to dash/cd.h
index a4dcc01..8763161 100644
--- a/ash/cd.h
+++ b/dash/cd.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: cd.h,v 1.4 2003/08/07 09:05:30 agc Exp $	*/
-
 /*-
  * Copyright (c) 1995
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,6 +30,6 @@
  *
  */
 
-void	getpwd(int);
 int	cdcmd(int, char **);
 int	pwdcmd(int, char **);
+void	setpwd(const char *, int);
diff --git a/dash/config.h b/dash/config.h
new file mode 100644
index 0000000..b53f6f3
--- /dev/null
+++ b/dash/config.h
@@ -0,0 +1,85 @@
+/* config.h.  Generated by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the `bsearch' function. */
+/* #undef HAVE_BSEARCH */
+
+/* Define to 1 if you have the `getpwnam' function. */
+/* #undef HAVE_GETPWNAM */
+
+/* Define to 1 if you have the `getrlimit' function. */
+/* #undef HAVE_GETRLIMIT */
+
+/* Define to 1 if you have the `isalpha' function. */
+/* #undef HAVE_ISALPHA */
+
+/* Define to 1 if you have the `killpg' function. */
+/* #undef HAVE_KILLPG */
+
+/* Define to 1 if you have the `mempcpy' function. */
+/* #undef HAVE_MEMPCPY */
+
+/* Define to 1 if you have the `sigsetmask' function. */
+/* #undef HAVE_SIGSETMASK */
+
+/* Define to 1 if you have the `stpcpy' function. */
+/* #undef HAVE_STPCPY */
+
+/* Define to 1 if you have the `strchrnul' function. */
+/* #undef HAVE_STRCHRNUL */
+
+/* Define to 1 if you have the `strsignal' function. */
+/* #undef HAVE_STRSIGNAL */
+
+/* Define to 1 if you have the `strtod' function. */
+/* #undef HAVE_STRTOD */
+
+/* Define to 1 if you have the `strtoimax' function. */
+#define HAVE_STRTOIMAX 1
+
+/* Define to 1 if you have the `strtoumax' function. */
+#define HAVE_STRTOUMAX 1
+
+/* Define to 1 if you have the `sysconf' function. */
+/* #undef HAVE_SYSCONF */
+
+/* Name of package */
+#define PACKAGE "dash"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "dash"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "dash 0.5.2"
+
+/* 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"
+
+/* Version number of package */
+#define VERSION "0.5.2"
+
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+
+/* 64-bit operations are the same as 32-bit */
+#define fstat64 fstat
+
+/* 64-bit operations are the same as 32-bit */
+#define lstat64 lstat
+
+/* 64-bit operations are the same as 32-bit */
+#define open64 open
+
+/* klibc has bsd_signal instead of signal */
+#define signal bsd_signal
+
+/* 64-bit operations are the same as 32-bit */
+#define stat64 stat
diff --git a/dash/error.c b/dash/error.c
new file mode 100644
index 0000000..338243d
--- /dev/null
+++ b/dash/error.c
@@ -0,0 +1,233 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Errors and exceptions.
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "shell.h"
+#include "main.h"
+#include "options.h"
+#include "output.h"
+#include "error.h"
+#include "show.h"
+#include "eval.h"
+#include "parser.h"
+#include "trap.h"
+#include "system.h"
+
+
+/*
+ * Code to handle exceptions in C.
+ */
+
+struct jmploc *handler;
+int exception;
+int suppressint;
+volatile sig_atomic_t intpending;
+
+
+static void exverror(int, const char *, va_list)
+    __attribute__((__noreturn__));
+
+/*
+ * Called to raise an exception.  Since C doesn't include exceptions, we
+ * just do a longjmp to the exception handler.  The type of exception is
+ * stored in the global variable "exception".
+ */
+
+void
+exraise(int e)
+{
+#ifdef DEBUG
+	if (handler == NULL)
+		abort();
+#endif
+	INTOFF;
+
+	exception = e;
+	longjmp(handler->loc, 1);
+}
+
+
+/*
+ * Called from trap.c when a SIGINT is received.  (If the user specifies
+ * that SIGINT is to be trapped or ignored using the trap builtin, then
+ * this routine is not called.)  Suppressint is nonzero when interrupts
+ * are held using the INTOFF macro.  (The test for iflag is just
+ * defensive programming.)
+ */
+
+void
+onint(void) {
+	int i;
+
+	intpending = 0;
+	sigclearmask();
+	i = EXSIG;
+	if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
+		if (!(rootshell && iflag)) {
+			signal(SIGINT, SIG_DFL);
+			raise(SIGINT);
+		}
+		i = EXINT;
+	}
+	exraise(i);
+	/* NOTREACHED */
+}
+
+static void
+exvwarning2(const char *msg, va_list ap)
+{
+	struct output *errs;
+	const char *name;
+	const char *fmt;
+
+	errs = out2;
+	name = arg0;
+	fmt = "%s: ";
+	if (commandname) {
+		name = commandname;
+		fmt = "%s: %d: ";
+	}
+	outfmt(errs, fmt, name, startlinno);
+	doformat(errs, msg, ap);
+#if FLUSHERR
+	outc('\n', errs);
+#else
+	outcslow('\n', errs);
+#endif
+}
+
+#define exvwarning(a, b, c) exvwarning2(b, c)
+
+/*
+ * Exverror is called to raise the error exception.  If the second argument
+ * is not NULL then error prints an error message using printf style
+ * formatting.  It then raises the error exception.
+ */
+static void
+exverror(int cond, const char *msg, va_list ap)
+{
+#ifdef DEBUG
+	if (msg) {
+		TRACE(("exverror(%d, \"", cond));
+		TRACEV((msg, ap));
+		TRACE(("\") pid=%d\n", getpid()));
+	} else
+		TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
+	if (msg)
+#endif
+		exvwarning(-1, msg, ap);
+
+	flushall();
+	exraise(cond);
+	/* NOTREACHED */
+}
+
+
+void
+sh_error(const char *msg, ...)
+{
+	va_list ap;
+
+	va_start(ap, msg);
+	exverror(EXERROR, msg, ap);
+	/* NOTREACHED */
+	va_end(ap);
+}
+
+
+void
+exerror(int cond, const char *msg, ...)
+{
+	va_list ap;
+
+	va_start(ap, msg);
+	exverror(cond, msg, ap);
+	/* NOTREACHED */
+	va_end(ap);
+}
+
+/*
+ * error/warning routines for external builtins
+ */
+
+void
+sh_warnx(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	exvwarning(-1, fmt, ap);
+	va_end(ap);
+}
+
+
+/*
+ * Return a string describing an error.  The returned string may be a
+ * pointer to a static buffer that will be overwritten on the next call.
+ * Action describes the operation that got the error.
+ */
+
+const char *
+errmsg(int e, int action)
+{
+	if (e != ENOENT && e != ENOTDIR)
+		return strerror(e);
+
+	if (action & E_OPEN)
+		return "No such file";
+	else if (action & E_CREAT)
+		return "Directory nonexistent";
+	else
+		return "not found";
+}
+
+
+#ifdef REALLY_SMALL
+void
+__inton() {
+	if (--suppressint == 0 && intpending) {
+		onint();
+	}
+}
+#endif
diff --git a/ash/error.h b/dash/error.h
similarity index 74%
rename from ash/error.h
rename to dash/error.h
index 8e70ca4..dd1fc3f 100644
--- a/ash/error.h
+++ b/dash/error.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: error.h,v 1.16 2003/08/07 09:05:30 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -34,7 +34,8 @@
  *	@(#)error.h	8.2 (Berkeley) 5/4/95
  */
 
-#include <stdarg.h>
+#include <setjmp.h>
+#include <signal.h>
 
 /*
  * Types of operations (passed to the errmsg routine).
@@ -55,21 +56,20 @@
  * inner scope, and restore handler on exit from the scope.
  */
 
-#include <setjmp.h>
-
 struct jmploc {
 	jmp_buf loc;
 };
 
 extern struct jmploc *handler;
 extern int exception;
-extern int exerrno;	/* error for EXEXEC */
 
 /* exceptions */
 #define EXINT 0		/* SIGINT received */
 #define EXERROR 1	/* a generic error */
 #define EXSHELLPROC 2	/* execute a shell procedure */
 #define EXEXEC 3	/* command execution failed */
+#define EXEXIT 4	/* exit the shell */
+#define EXSIG 5		/* trapped signal in wait(1) */
 
 
 /*
@@ -79,39 +79,62 @@
  * more fun than worrying about efficiency and portability. :-))
  */
 
-extern volatile int suppressint;
-extern volatile int intpending;
+extern int suppressint;
+extern volatile sig_atomic_t intpending;
+extern int exsig;
 
-#define INTOFF suppressint++
-#define INTON { if (--suppressint == 0 && intpending) onint(); }
-#define FORCEINTON {suppressint = 0; if (intpending) onint();}
+#define barrier() ({ __asm__ __volatile__ ("": : :"memory"); })
+#define INTOFF \
+	({ \
+		suppressint++; \
+		barrier(); \
+		0; \
+	})
+#ifdef REALLY_SMALL
+void __inton(void);
+#define INTON __inton()
+#else
+#define INTON \
+	({ \
+		barrier(); \
+		if (--suppressint == 0 && intpending) onint(); \
+		0; \
+	})
+#endif
+#define FORCEINTON \
+	({ \
+		barrier(); \
+		suppressint = 0; \
+		if (intpending) onint(); \
+		0; \
+	})
+#define SAVEINT(v) ((v) = suppressint)
+#define RESTOREINT(v) \
+	({ \
+		barrier(); \
+		if ((suppressint = (v)) == 0 && intpending) onint(); \
+		0; \
+	})
 #define CLEAR_PENDING_INT intpending = 0
 #define int_pending() intpending
+#define EXSIGON() \
+	({ \
+		exsig++; \
+		barrier(); \
+		if (pendingsigs) \
+			exraise(EXSIG); \
+		0; \
+	})
+/* EXSIG is turned off by evalbltin(). */
 
 void exraise(int) __attribute__((__noreturn__));
+#ifdef USE_NORETURN
+void onint(void) __attribute__((__noreturn__));
+#else
 void onint(void);
-void error(const char *, ...) __attribute__((__noreturn__));
+#endif
+void sh_error(const char *, ...) __attribute__((__noreturn__));
 void exerror(int, const char *, ...) __attribute__((__noreturn__));
 const char *errmsg(int, int);
 
-void sh_err(int, const char *, ...) __attribute__((__noreturn__));
-void sh_verr(int, const char *, va_list) __attribute__((__noreturn__));
-void sh_errx(int, const char *, ...) __attribute__((__noreturn__));
-void sh_verrx(int, const char *, va_list) __attribute__((__noreturn__));
-void sh_warn(const char *, ...);
-void sh_vwarn(const char *, va_list);
 void sh_warnx(const char *, ...);
-void sh_vwarnx(const char *, va_list);
-
-void sh_exit(int) __attribute__((__noreturn__));
-
-
-/*
- * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
- * so we use _setjmp instead.
- */
-
-#if defined(BSD) && !defined(__SVR4) && !defined(__linux__)
-#define setjmp(jmploc)	_setjmp(jmploc)
-#define longjmp(jmploc, val)	_longjmp(jmploc, val)
-#endif
diff --git a/dash/eval.c b/dash/eval.c
new file mode 100644
index 0000000..f8f6f0a
--- /dev/null
+++ b/dash/eval.c
@@ -0,0 +1,1100 @@
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+/*
+ * Evaluate a command.
+ */
+
+#include "shell.h"
+#include "nodes.h"
+#include "syntax.h"
+#include "expand.h"
+#include "parser.h"
+#include "jobs.h"
+#include "eval.h"
+#include "builtins.h"
+#include "options.h"
+#include "exec.h"
+#include "redir.h"
+#include "input.h"
+#include "output.h"
+#include "trap.h"
+#include "var.h"
+#include "memalloc.h"
+#include "error.h"
+#include "show.h"
+#include "mystring.h"
+#ifndef SMALL
+#include "myhistedit.h"
+#endif
+
+
+/* flags in argument to evaltree */
+#define EV_EXIT 01		/* exit after evaluating tree */
+#define EV_TESTED 02		/* exit status is checked; ignore -e flag */
+#define EV_BACKCMD 04		/* command executing within back quotes */
+
+int evalskip;			/* set if we are skipping commands */
+STATIC int skipcount;		/* number of levels to skip */
+MKINIT int loopnest;		/* current loop nesting level */
+static int funcnest;		/* depth of function calls */
+
+
+char *commandname;
+struct strlist *cmdenviron;
+int exitstatus;			/* exit status of last command */
+int back_exitstatus;		/* exit status of backquoted command */
+
+
+#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
+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 void expredir(union node *);
+STATIC void evalpipe(union node *, int);
+#ifdef notyet
+STATIC void evalcommand(union node *, int, struct backcmd *);
+#else
+STATIC void evalcommand(union node *, int);
+#endif
+STATIC int evalbltin(const struct builtincmd *, int, char **);
+STATIC int evalfun(struct funcnode *, int, char **, int);
+STATIC void prehash(union node *);
+STATIC int eprintlist(struct output *, struct strlist *, int);
+STATIC int bltincmd(int, char **);
+
+
+STATIC const struct builtincmd bltin = {
+	name: nullstr,
+	builtin: bltincmd
+};
+
+
+/*
+ * Called to reset things after an exception.
+ */
+
+#ifdef mkinit
+INCLUDE "eval.h"
+
+RESET {
+	evalskip = 0;
+	loopnest = 0;
+}
+#endif
+
+
+
+/*
+ * The eval commmand.
+ */
+
+int
+evalcmd(int argc, char **argv)
+{
+        char *p;
+        char *concat;
+        char **ap;
+
+        if (argc > 1) {
+                p = argv[1];
+                if (argc > 2) {
+                        STARTSTACKSTR(concat);
+                        ap = argv + 2;
+                        for (;;) {
+                        	concat = stputs(p, concat);
+                                if ((p = *ap++) == NULL)
+                                        break;
+                                STPUTC(' ', concat);
+                        }
+                        STPUTC('\0', concat);
+                        p = grabstackstr(concat);
+                }
+                evalstring(p, ~SKIPEVAL);
+                
+        }
+        return exitstatus;
+}
+
+
+/*
+ * Execute a command or commands contained in a string.
+ */
+
+int
+evalstring(char *s, int mask)
+{
+	union node *n;
+	struct stackmark smark;
+	int skip;
+
+	setinputstring(s);
+	setstackmark(&smark);
+
+	skip = 0;
+	while ((n = parsecmd(0)) != NEOF) {
+		evaltree(n, 0);
+		popstackmark(&smark);
+		skip = evalskip;
+		if (skip)
+			break;
+	}
+	popfile();
+
+	skip &= mask;
+	evalskip = skip;
+	return skip;
+}
+
+
+
+/*
+ * Evaluate a parse tree.  The value is left in the global variable
+ * exitstatus.
+ */
+
+void
+evaltree(union node *n, int flags)
+{
+	int checkexit = 0;
+	void (*evalfn)(union node *, int);
+	unsigned isor;
+	int status;
+	if (n == NULL) {
+		TRACE(("evaltree(NULL) called\n"));
+		goto out;
+	}
+#ifndef SMALL
+	displayhist = 1;	/* show history substitutions done with fc */
+#endif
+	TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
+	    getpid(), n, n->type, flags));
+	switch (n->type) {
+	default:
+#ifdef DEBUG
+		out1fmt("Node type = %d\n", n->type);
+#ifndef USE_GLIBC_STDIO
+		flushout(out1);
+#endif
+		break;
+#endif
+	case NNOT:
+		evaltree(n->nnot.com, EV_TESTED);
+		status = !exitstatus;
+		goto setstatus;
+	case NREDIR:
+		expredir(n->nredir.redirect);
+		status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
+		if (!status) {
+			evaltree(n->nredir.n, flags & EV_TESTED);
+			status = exitstatus;
+		}
+		popredir(0);
+		goto setstatus;
+	case NCMD:
+#ifdef notyet
+		if (eflag && !(flags & EV_TESTED))
+			checkexit = ~0;
+		evalcommand(n, flags, (struct backcmd *)NULL);
+		break;
+#else
+		evalfn = evalcommand;
+checkexit:
+		if (eflag && !(flags & EV_TESTED))
+			checkexit = ~0;
+		goto calleval;
+#endif
+	case NFOR:
+		evalfn = evalfor;
+		goto calleval;
+	case NWHILE:
+	case NUNTIL:
+		evalfn = evalloop;
+		goto calleval;
+	case NSUBSHELL:
+	case NBACKGND:
+		evalfn = evalsubshell;
+		goto calleval;
+	case NPIPE:
+		evalfn = evalpipe;
+#ifdef notyet
+		if (eflag && !(flags & EV_TESTED))
+			checkexit = ~0;
+		goto calleval;
+#else
+		goto checkexit;
+#endif
+	case NCASE:
+		evalfn = evalcase;
+		goto calleval;
+	case NAND:
+	case NOR:
+	case NSEMI:
+#if NAND + 1 != NOR
+#error NAND + 1 != NOR
+#endif
+#if NOR + 1 != NSEMI
+#error NOR + 1 != NSEMI
+#endif
+		isor = n->type - NAND;
+		evaltree(
+			n->nbinary.ch1,
+			(flags | ((isor >> 1) - 1)) & EV_TESTED
+		);
+		if (!exitstatus == isor)
+			break;
+		if (!evalskip) {
+			n = n->nbinary.ch2;
+evaln:
+			evalfn = evaltree;
+calleval:
+			evalfn(n, flags);
+			break;
+		}
+		break;
+	case NIF:
+		evaltree(n->nif.test, EV_TESTED);
+		if (evalskip)
+			break;
+		if (exitstatus == 0) {
+			n = n->nif.ifpart;
+			goto evaln;
+		} else if (n->nif.elsepart) {
+			n = n->nif.elsepart;
+			goto evaln;
+		}
+		goto success;
+	case NDEFUN:
+		defun(n->narg.text, n->narg.next);
+success:
+		status = 0;
+setstatus:
+		exitstatus = status;
+		break;
+	}
+out:
+	if ((checkexit & exitstatus))
+		evalskip |= SKIPEVAL;
+	else if (pendingsigs && dotrap())
+		goto exexit;
+
+	if (flags & EV_EXIT) {
+exexit:
+		exraise(EXEXIT);
+	}
+}
+
+
+#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
+STATIC
+#endif
+void evaltreenr(union node *, int) __attribute__ ((alias("evaltree")));
+
+
+STATIC void
+evalloop(union node *n, int flags)
+{
+	int status;
+
+	loopnest++;
+	status = 0;
+	flags &= EV_TESTED;
+	for (;;) {
+		int i;
+
+		evaltree(n->nbinary.ch1, EV_TESTED);
+		if (evalskip) {
+skipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
+				evalskip = 0;
+				continue;
+			}
+			if (evalskip == SKIPBREAK && --skipcount <= 0)
+				evalskip = 0;
+			break;
+		}
+		i = exitstatus;
+		if (n->type != NWHILE)
+			i = !i;
+		if (i != 0)
+			break;
+		evaltree(n->nbinary.ch2, flags);
+		status = exitstatus;
+		if (evalskip)
+			goto skipping;
+	}
+	loopnest--;
+	exitstatus = status;
+}
+
+
+
+STATIC void
+evalfor(union node *n, int flags)
+{
+	struct arglist arglist;
+	union node *argp;
+	struct strlist *sp;
+	struct stackmark smark;
+
+	setstackmark(&smark);
+	arglist.lastp = &arglist.list;
+	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
+		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
+		/* XXX */
+		if (evalskip)
+			goto out;
+	}
+	*arglist.lastp = NULL;
+
+	exitstatus = 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);
+		if (evalskip) {
+			if (evalskip == SKIPCONT && --skipcount <= 0) {
+				evalskip = 0;
+				continue;
+			}
+			if (evalskip == SKIPBREAK && --skipcount <= 0)
+				evalskip = 0;
+			break;
+		}
+	}
+	loopnest--;
+out:
+	popstackmark(&smark);
+}
+
+
+
+STATIC void
+evalcase(union node *n, int flags)
+{
+	union node *cp;
+	union node *patp;
+	struct arglist arglist;
+	struct stackmark smark;
+
+	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);
+				}
+				goto out;
+			}
+		}
+	}
+out:
+	popstackmark(&smark);
+}
+
+
+
+/*
+ * Kick off a subshell to evaluate a tree.
+ */
+
+STATIC void
+evalsubshell(union node *n, int flags)
+{
+	struct job *jp;
+	int backgnd = (n->type == NBACKGND);
+	int status;
+
+	expredir(n->nredir.redirect);
+	if (!backgnd && flags & EV_EXIT && !trap[0])
+		goto nofork;
+	INTOFF;
+	jp = makejob(n, 1);
+	if (forkshell(jp, n, backgnd) == 0) {
+		INTON;
+		flags |= EV_EXIT;
+		if (backgnd)
+			flags &=~ EV_TESTED;
+nofork:
+		redirect(n->nredir.redirect, 0);
+		evaltreenr(n->nredir.n, flags);
+		/* never returns */
+	}
+	status = 0;
+	if (! backgnd)
+		status = waitforjob(jp);
+	exitstatus = status;
+	INTON;
+}
+
+
+
+/*
+ * Compute the names of the files in a redirection list.
+ */
+
+STATIC void
+expredir(union node *n)
+{
+	union node *redir;
+
+	for (redir = n ; redir ; redir = redir->nfile.next) {
+		struct arglist fn;
+		fn.lastp = &fn.list;
+		switch (redir->type) {
+		case NFROMTO:
+		case NFROM:
+		case NTO:
+		case NCLOBBER:
+		case NAPPEND:
+			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+			redir->nfile.expfname = fn.list->text;
+			break;
+		case NFROMFD:
+		case NTOFD:
+			if (redir->ndup.vname) {
+				expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+				fixredir(redir, fn.list->text, 1);
+			}
+			break;
+		}
+	}
+}
+
+
+
+/*
+ * Evaluate a pipeline.  All the processes in the pipeline are children
+ * of the process creating the pipeline.  (This differs from some versions
+ * of the shell, which make the last process in a pipeline the parent
+ * of all the rest.)
+ */
+
+STATIC void
+evalpipe(union node *n, int flags)
+{
+	struct job *jp;
+	struct nodelist *lp;
+	int pipelen;
+	int prevfd;
+	int pip[2];
+
+	TRACE(("evalpipe(0x%lx) called\n", (long)n));
+	pipelen = 0;
+	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
+		pipelen++;
+	flags |= EV_EXIT;
+	INTOFF;
+	jp = makejob(n, pipelen);
+	prevfd = -1;
+	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+		prehash(lp->n);
+		pip[1] = -1;
+		if (lp->next) {
+			if (pipe(pip) < 0) {
+				close(prevfd);
+				sh_error("Pipe call failed");
+			}
+		}
+		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
+			INTON;
+			if (pip[1] >= 0) {
+				close(pip[0]);
+			}
+			if (prevfd > 0) {
+				dup2(prevfd, 0);
+				close(prevfd);
+			}
+			if (pip[1] > 1) {
+				dup2(pip[1], 1);
+				close(pip[1]);
+			}
+			evaltreenr(lp->n, flags);
+			/* never returns */
+		}
+		if (prevfd >= 0)
+			close(prevfd);
+		prevfd = pip[0];
+		close(pip[1]);
+	}
+	if (n->npipe.backgnd == 0) {
+		exitstatus = waitforjob(jp);
+		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
+	}
+	INTON;
+}
+
+
+
+/*
+ * 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.
+ */
+
+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;
+
+#ifdef notyet
+	/*
+	 * For now we disable executing builtins in the same
+	 * context as the shell, because we are not keeping
+	 * enough state to recover from changes that are
+	 * supposed only to affect subshells. eg. echo "`cd /`"
+	 */
+	if (n->type == NCMD) {
+		struct ifsregion saveifs;
+		struct ifsregion *savelastp;
+		struct nodelist *saveargbackq;
+
+		saveifs = ifsfirst;
+		savelastp = ifslastp;
+		saveargbackq = argbackq;
+
+		exitstatus = oexitstatus;
+		evalcommand(n, EV_BACKCMD, result);
+
+		ifsfirst = saveifs;
+		ifslastp = savelastp;
+		argbackq = saveargbackq;
+	} else
+#endif
+	{
+		int pip[2];
+		struct job *jp;
+
+		if (pipe(pip) < 0)
+			sh_error("Pipe call failed");
+		jp = makejob(n, 1);
+		if (forkshell(jp, n, FORK_NOJOB) == 0) {
+			FORCEINTON;
+			close(pip[0]);
+			if (pip[1] != 1) {
+				close(1);
+				copyfd(pip[1], 1);
+				close(pip[1]);
+			}
+			eflag = 0;
+			evaltreenr(n, EV_EXIT);
+			/* 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));
+}
+
+static char **
+parse_command_args(char **argv, const char **path)
+{
+	char *cp, c;
+
+	for (;;) {
+		cp = *++argv;
+		if (!cp)
+			return 0;
+		if (*cp++ != '-')
+			break;
+		if (!(c = *cp++))
+			break;
+		if (c == '-' && !*cp) {
+			argv++;
+			break;
+		}
+		do {
+			switch (c) {
+			case 'p':
+				*path = defpath;
+				break;
+			default:
+				/* run 'typecmd' for other options */
+				return 0;
+			}
+		} while ((c = *cp++));
+	}
+	return argv;
+}
+
+
+
+/*
+ * Execute a simple command.
+ */
+
+STATIC void
+#ifdef notyet
+evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
+#else
+evalcommand(union node *cmd, int flags)
+#endif
+{
+	struct stackmark smark;
+	union node *argp;
+	struct arglist arglist;
+	struct arglist varlist;
+	char **argv;
+	int argc;
+	struct strlist *sp;
+#ifdef notyet
+	int pip[2];
+#endif
+	struct cmdentry cmdentry;
+	struct job *jp;
+	char *lastarg;
+	const char *path;
+	int spclbltin;
+	int execcmd;
+	int status;
+	char **nargv;
+
+	/* First expand the arguments. */
+	TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
+	setstackmark(&smark);
+	back_exitstatus = 0;
+
+	cmdentry.cmdtype = CMDBUILTIN;
+	cmdentry.u.cmd = &bltin;
+	varlist.lastp = &varlist.list;
+	*varlist.lastp = NULL;
+	arglist.lastp = &arglist.list;
+	*arglist.lastp = NULL;
+
+	argc = 0;
+	for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
+		struct strlist **spp;
+
+		spp = arglist.lastp;
+		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+		for (sp = *spp; sp; sp = sp->next)
+			argc++;
+	}
+
+	argv = nargv = stalloc(sizeof (char *) * (argc + 1));
+	for (sp = arglist.list ; sp ; sp = sp->next) {
+		TRACE(("evalcommand arg: %s\n", sp->text));
+		*nargv++ = sp->text;
+	}
+	*nargv = NULL;
+
+	lastarg = NULL;
+	if (iflag && funcnest == 0 && argc > 0)
+		lastarg = nargv[-1];
+
+	preverrout.fd = 2;
+	expredir(cmd->ncmd.redirect);
+	status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
+
+	path = vpath.text;
+	for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
+		struct strlist **spp;
+		char *p;
+
+		spp = varlist.lastp;
+		expandarg(argp, &varlist, EXP_VARTILDE);
+
+		/*
+		 * Modify the command lookup path, if a PATH= assignment
+		 * is present
+		 */
+		p = (*spp)->text;
+		if (varequal(p, path))
+			path = p;
+	}
+
+	/* Print the command if xflag is set. */
+	if (xflag) {
+		struct output *out;
+		int sep;
+
+		out = &preverrout;
+		outstr(expandstr(ps4val()), out);
+		sep = 0;
+		sep = eprintlist(out, varlist.list, sep);
+		eprintlist(out, arglist.list, sep);
+		outcslow('\n', out);
+#ifdef FLUSHERR
+		flushout(out);
+#endif
+	}
+
+	execcmd = 0;
+	spclbltin = -1;
+
+	/* Now locate the command. */
+	if (argc) {
+		const char *oldpath;
+		int cmd_flag = DO_ERR;
+
+		path += 5;
+		oldpath = path;
+		for (;;) {
+			find_command(argv[0], &cmdentry, cmd_flag, path);
+			if (cmdentry.cmdtype == CMDUNKNOWN) {
+				status = 127;
+#ifdef FLUSHERR
+				flushout(&errout);
+#endif
+				goto bail;
+			}
+
+			/* implement bltin and command here */
+			if (cmdentry.cmdtype != CMDBUILTIN)
+				break;
+			if (spclbltin < 0)
+				spclbltin = 
+					cmdentry.u.cmd->flags &
+					BUILTIN_SPECIAL
+				;
+			if (cmdentry.u.cmd == EXECCMD)
+				execcmd++;
+			if (cmdentry.u.cmd != COMMANDCMD)
+				break;
+
+			path = oldpath;
+			nargv = parse_command_args(argv, &path);
+			if (!nargv)
+				break;
+			argc -= nargv - argv;
+			argv = nargv;
+			cmd_flag |= DO_NOFUNC;
+		}
+	}
+
+	if (status) {
+		/* We have a redirection error. */
+		if (spclbltin > 0)
+			exraise(EXERROR);
+bail:
+		exitstatus = status;
+		goto out;
+	}
+
+	/* Execute the command. */
+	switch (cmdentry.cmdtype) {
+	default:
+		/* Fork off a child process if necessary. */
+		if (!(flags & EV_EXIT) || trap[0]) {
+			INTOFF;
+			jp = makejob(cmd, 1);
+			if (forkshell(jp, cmd, FORK_FG) != 0) {
+				exitstatus = waitforjob(jp);
+				INTON;
+				break;
+			}
+			FORCEINTON;
+		}
+		listsetvar(varlist.list, VEXPORT|VSTACK);
+		shellexec(argv, path, cmdentry.u.index);
+		/* NOTREACHED */
+
+	case CMDBUILTIN:
+		cmdenviron = varlist.list;
+		if (cmdenviron) {
+			struct strlist *list = cmdenviron;
+			int i = VNOSET;
+			if (spclbltin > 0 || argc == 0) {
+				i = 0;
+				if (execcmd && argc > 1)
+					i = VEXPORT;
+			}
+			listsetvar(list, i);
+		}
+		if (evalbltin(cmdentry.u.cmd, argc, argv)) {
+			int status;
+			int i, j;
+
+			i = exception;
+			if (i == EXEXIT)
+				goto raise;
+
+			status = 2;
+			j = 0;
+			if (i == EXINT)
+				j = SIGINT;
+			if (i == EXSIG)
+				j = pendingsigs;
+			if (j)
+				status = j + 128;
+			exitstatus = status;
+
+			if (i == EXINT || spclbltin > 0) {
+raise:
+				longjmp(handler->loc, 1);
+			}
+			FORCEINTON;
+		}
+		break;
+
+	case CMDFUNCTION:
+		listsetvar(varlist.list, 0);
+		if (evalfun(cmdentry.u.func, argc, argv, flags))
+			goto raise;
+		break;
+	}
+
+out:
+	popredir(execcmd);
+	if (lastarg)
+		/* dsl: I think this is intended to be used to support
+		 * '_' in 'vi' command mode during line editing...
+		 * However I implemented that within libedit itself.
+		 */
+		setvar("_", lastarg, 0);
+	popstackmark(&smark);
+}
+
+STATIC int
+evalbltin(const struct builtincmd *cmd, int argc, char **argv) {
+	char *volatile savecmdname;
+	struct jmploc *volatile savehandler;
+	struct jmploc jmploc;
+	int i;
+
+	savecmdname = commandname;
+	if ((i = setjmp(jmploc.loc)))
+		goto cmddone;
+	savehandler = handler;
+	handler = &jmploc;
+	commandname = argv[0];
+	argptr = argv + 1;
+	optptr = NULL;			/* initialize nextopt */
+	exitstatus = (*cmd->builtin)(argc, argv);
+	flushall();
+cmddone:
+	exitstatus |= outerr(out1);
+	freestdout();
+	commandname = savecmdname;
+	exsig = 0;
+	handler = savehandler;
+
+	return i;
+}
+
+STATIC int
+evalfun(struct funcnode *func, int argc, char **argv, int flags)
+{
+	volatile struct shparam saveparam;
+	struct localvar *volatile savelocalvars;
+	struct jmploc *volatile savehandler;
+	struct jmploc jmploc;
+	int e;
+
+	saveparam = shellparam;
+	savelocalvars = localvars;
+	if ((e = setjmp(jmploc.loc))) {
+		goto funcdone;
+	}
+	INTOFF;
+	savehandler = handler;
+	handler = &jmploc;
+	localvars = NULL;
+	shellparam.malloc = 0;
+	func->count++;
+	funcnest++;
+	INTON;
+	shellparam.nparam = argc - 1;
+	shellparam.p = argv + 1;
+	shellparam.optind = 1;
+	shellparam.optoff = -1;
+	evaltree(&func->n, flags & EV_TESTED);
+funcdone:
+	INTOFF;
+	funcnest--;
+	freefunc(func);
+	poplocalvars();
+	localvars = savelocalvars;
+	freeparam(&shellparam);
+	shellparam = saveparam;
+	handler = savehandler;
+	INTON;
+	evalskip &= ~SKIPFUNC;
+	return e;
+}
+
+
+/*
+ * Search for a command.  This is called before we fork so that the
+ * location of the command will be available in the parent as well as
+ * the child.  The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
+ */
+
+STATIC void
+prehash(union node *n)
+{
+	struct cmdentry entry;
+
+	if (n->type == NCMD && n->ncmd.args)
+		if (goodname(n->ncmd.args->narg.text))
+			find_command(n->ncmd.args->narg.text, &entry, 0,
+				     pathval());
+}
+
+
+
+/*
+ * Builtin commands.  Builtin commands whose functions are closely
+ * tied to evaluation are implemented here.
+ */
+
+/*
+ * No command given.
+ */
+
+STATIC int
+bltincmd(int argc, char **argv)
+{
+	/*
+	 * Preserve exitstatus of a previous possible redirection
+	 * as POSIX mandates
+	 */
+	return back_exitstatus;
+}
+
+
+/*
+ * Handle break and continue commands.  Break, continue, and return are
+ * all handled by setting the evalskip flag.  The evaluation routines
+ * above all check this flag, and if it is set they start skipping
+ * commands rather than executing them.  The variable skipcount is
+ * the number of loops to break/continue, or the number of function
+ * levels to return.  (The latter is always 1.)  It should probably
+ * be an error to break out of more loops than exist, but it isn't
+ * in the standard shell so we don't make it one here.
+ */
+
+int
+breakcmd(int argc, char **argv)
+{
+	int n = argc > 1 ? number(argv[1]) : 1;
+
+	if (n <= 0)
+		sh_error(illnum, argv[1]);
+	if (n > loopnest)
+		n = loopnest;
+	if (n > 0) {
+		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
+		skipcount = n;
+	}
+	return 0;
+}
+
+
+/*
+ * The return command.
+ */
+
+int
+returncmd(int argc, char **argv)
+{
+	/*
+	 * If called outside a function, do what ksh does;
+	 * skip the rest of the file.
+	 */
+	evalskip = funcnest ? SKIPFUNC : SKIPFILE;
+	return argv[1] ? number(argv[1]) : exitstatus;
+}
+
+
+int
+falsecmd(int argc, char **argv)
+{
+	return 1;
+}
+
+
+int
+truecmd(int argc, char **argv)
+{
+	return 0;
+}
+
+
+int
+execcmd(int argc, char **argv)
+{
+	if (argc > 1) {
+		iflag = 0;		/* exit on error */
+		mflag = 0;
+		optschanged();
+		shellexec(argv + 1, pathval(), 0);
+	}
+	return 0;
+}
+
+
+STATIC int
+eprintlist(struct output *out, struct strlist *sp, int sep)
+{
+	while (sp) {
+		const char *p;
+
+		p = " %s" + (1 - sep);
+		sep |= 1;
+		outfmt(out, p, sp->text);
+		sp = sp->next;
+	}
+
+	return sep;
+}
diff --git a/ash/eval.h b/dash/eval.h
similarity index 88%
rename from ash/eval.h
rename to dash/eval.h
index 155bc44..005620d 100644
--- a/ash/eval.h
+++ b/dash/eval.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: eval.h,v 1.14 2003/08/07 09:05:31 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -47,18 +47,16 @@
 	struct job *jp;		/* job structure for command */
 };
 
-void evalstring(char *, int);
+int evalstring(char *, int);
 union node;	/* BLETCH for ansi C */
 void evaltree(union node *, int);
 void evalbackcmd(union node *, struct backcmd *);
 
-/* in_function returns nonzero if we are currently evaluating a function */
-#define in_function()	funcnest
-extern int funcnest;
 extern int evalskip;
 
 /* reasons for skipping commands (see comment on breakcmd routine) */
-#define SKIPBREAK	1
-#define SKIPCONT	2
-#define SKIPFUNC	3
-#define SKIPFILE	4
+#define SKIPBREAK	(1 << 0)
+#define SKIPCONT	(1 << 1)
+#define SKIPFUNC	(1 << 2)
+#define SKIPFILE	(1 << 3)
+#define SKIPEVAL	(1 << 4)
diff --git a/dash/exec.c b/dash/exec.c
new file mode 100644
index 0000000..417ba8a
--- /dev/null
+++ b/dash/exec.c
@@ -0,0 +1,869 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <paths.h>
+
+/*
+ * When commands are first encountered, they are entered in a hash table.
+ * This ensures that a full path search will not have to be done for them
+ * on each invocation.
+ *
+ * We should investigate converting to a linear search, even though that
+ * would make the command name "hash" a misnomer.
+ */
+
+#include "shell.h"
+#include "main.h"
+#include "nodes.h"
+#include "parser.h"
+#include "redir.h"
+#include "eval.h"
+#include "exec.h"
+#include "builtins.h"
+#include "var.h"
+#include "options.h"
+#include "output.h"
+#include "syntax.h"
+#include "memalloc.h"
+#include "error.h"
+#include "init.h"
+#include "mystring.h"
+#include "show.h"
+#include "jobs.h"
+#include "alias.h"
+#include "system.h"
+
+
+#define CMDTABLESIZE 31		/* should be prime */
+#define ARB 1			/* actual size determined at run time */
+
+
+
+struct tblentry {
+	struct tblentry *next;	/* next entry in hash chain */
+	union param param;	/* definition of builtin function */
+	short cmdtype;		/* index identifying command */
+	char rehash;		/* if set, cd done since entry created */
+	char cmdname[ARB];	/* name of command */
+};
+
+
+STATIC struct tblentry *cmdtable[CMDTABLESIZE];
+STATIC int builtinloc = -1;		/* index in path of %builtin, or -1 */
+
+
+STATIC void tryexec(char *, char **, char **);
+STATIC void printentry(struct tblentry *);
+STATIC void clearcmdentry(int);
+STATIC struct tblentry *cmdlookup(const char *, int);
+STATIC void delete_cmd_entry(void);
+STATIC void addcmdentry(char *, struct cmdentry *);
+STATIC int describe_command(struct output *, char *, int);
+
+
+/*
+ * Exec a program.  Never returns.  If you change this routine, you may
+ * have to change the find_command routine as well.
+ */
+
+void
+shellexec(char **argv, const char *path, int idx)
+{
+	char *cmdname;
+	int e;
+	char **envp;
+	int exerrno;
+
+	clearredir(1);
+	envp = environment();
+	if (strchr(argv[0], '/') != NULL) {
+		tryexec(argv[0], argv, envp);
+		e = errno;
+	} else {
+		e = ENOENT;
+		while ((cmdname = padvance(&path, argv[0])) != NULL) {
+			if (--idx < 0 && pathopt == NULL) {
+				tryexec(cmdname, argv, envp);
+				if (errno != ENOENT && errno != ENOTDIR)
+					e = errno;
+			}
+			stunalloc(cmdname);
+		}
+	}
+
+	/* Map to POSIX errors */
+	switch (e) {
+	case EACCES:
+		exerrno = 126;
+		break;
+	case ENOENT:
+		exerrno = 127;
+		break;
+	default:
+		exerrno = 2;
+		break;
+	}
+	exitstatus = exerrno;
+	TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
+		argv[0], e, suppressint ));
+	exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
+	/* NOTREACHED */
+}
+
+
+STATIC void
+tryexec(char *cmd, char **argv, char **envp)
+{
+	int repeated = 0;
+#if !defined(BSD) && !defined(linux)
+	char *p;
+#endif
+
+repeat:
+#ifdef SYSV
+	do {
+		execve(cmd, argv, envp);
+	} while (errno == EINTR);
+#else
+	execve(cmd, argv, envp);
+#endif
+	if (repeated++) {
+		ckfree(argv);
+	} else if (errno == ENOEXEC) {
+		char **ap;
+		char **new;
+
+		for (ap = argv; *ap; ap++)
+			;
+		ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
+		*ap++ = cmd = _PATH_BSHELL;
+		while ((*ap++ = *argv++))
+			;
+		argv = new;
+		goto repeat;
+	}
+}
+
+
+
+/*
+ * Do a path search.  The variable path (passed by reference) should be
+ * set to the start of the path before the first call; padvance will update
+ * this value as it proceeds.  Successive calls to padvance will return
+ * the possible path expansions in sequence.  If an option (indicated by
+ * a percent sign) appears in the path entry then the global variable
+ * pathopt will be set to point to it; otherwise pathopt will be set to
+ * NULL.
+ */
+
+const char *pathopt;
+
+char *
+padvance(const char **path, const char *name)
+{
+	const char *p;
+	char *q;
+	const char *start;
+	size_t len;
+
+	if (*path == NULL)
+		return NULL;
+	start = *path;
+	for (p = start ; *p && *p != ':' && *p != '%' ; p++);
+	len = p - start + strlen(name) + 2;	/* "2" is for '/' and '\0' */
+	while (stackblocksize() < len)
+		growstackblock();
+	q = stackblock();
+	if (p != start) {
+		memcpy(q, start, p - start);
+		q += p - start;
+		*q++ = '/';
+	}
+	strcpy(q, name);
+	pathopt = NULL;
+	if (*p == '%') {
+		pathopt = ++p;
+		while (*p && *p != ':')  p++;
+	}
+	if (*p == ':')
+		*path = p + 1;
+	else
+		*path = NULL;
+	return stalloc(len);
+}
+
+
+
+/*** Command hashing code ***/
+
+
+int
+hashcmd(int argc, char **argv)
+{
+	struct tblentry **pp;
+	struct tblentry *cmdp;
+	int c;
+	struct cmdentry entry;
+	char *name;
+
+	while ((c = nextopt("r")) != '\0') {
+		clearcmdentry(0);
+		return 0;
+	}
+	if (*argptr == NULL) {
+		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+				if (cmdp->cmdtype == CMDNORMAL)
+					printentry(cmdp);
+			}
+		}
+		return 0;
+	}
+	c = 0;
+	while ((name = *argptr) != NULL) {
+		if ((cmdp = cmdlookup(name, 0)) != NULL
+		 && (cmdp->cmdtype == CMDNORMAL
+		     || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
+			delete_cmd_entry();
+		find_command(name, &entry, DO_ERR, pathval());
+		if (entry.cmdtype == CMDUNKNOWN)
+			c = 1;
+		argptr++;
+	}
+	return c;
+}
+
+
+STATIC void
+printentry(struct tblentry *cmdp)
+{
+	int idx;
+	const char *path;
+	char *name;
+
+	idx = cmdp->param.index;
+	path = pathval();
+	do {
+		name = padvance(&path, cmdp->cmdname);
+		stunalloc(name);
+	} while (--idx >= 0);
+	out1str(name);
+	out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr);
+}
+
+
+
+/*
+ * Resolve a command name.  If you change this routine, you may have to
+ * change the shellexec routine as well.
+ */
+
+void
+find_command(char *name, struct cmdentry *entry, int act, const char *path)
+{
+	struct tblentry *cmdp;
+	int idx;
+	int prev;
+	char *fullname;
+	struct stat64 statb;
+	int e;
+	int updatetbl;
+	struct builtincmd *bcmd;
+
+	/* If name contains a slash, don't use PATH or hash table */
+	if (strchr(name, '/') != NULL) {
+		entry->u.index = -1;
+		if (act & DO_ABS) {
+			while (stat64(name, &statb) < 0) {
+#ifdef SYSV
+				if (errno == EINTR)
+					continue;
+#endif
+				entry->cmdtype = CMDUNKNOWN;
+				return;
+			}
+		}
+		entry->cmdtype = CMDNORMAL;
+		return;
+	}
+
+	updatetbl = (path == pathval());
+	if (!updatetbl) {
+		act |= DO_ALTPATH;
+		if (strstr(path, "%builtin") != NULL)
+			act |= DO_ALTBLTIN;
+	}
+
+	/* If name is in the table, check answer will be ok */
+	if ((cmdp = cmdlookup(name, 0)) != NULL) {
+		int bit;
+
+		switch (cmdp->cmdtype) {
+		default:
+#if DEBUG
+			abort();
+#endif
+		case CMDNORMAL:
+			bit = DO_ALTPATH;
+			break;
+		case CMDFUNCTION:
+			bit = DO_NOFUNC;
+			break;
+		case CMDBUILTIN:
+			bit = DO_ALTBLTIN;
+			break;
+		}
+		if (act & bit) {
+			updatetbl = 0;
+			cmdp = NULL;
+		} else if (cmdp->rehash == 0)
+			/* if not invalidated by cd, we're done */
+			goto success;
+	}
+
+	/* If %builtin not in path, check for builtin next */
+	bcmd = find_builtin(name);
+	if (bcmd && (bcmd->flags & BUILTIN_REGULAR || (
+		act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
+	)))
+		goto builtin_success;
+
+	/* We have to search path. */
+	prev = -1;		/* where to start */
+	if (cmdp && cmdp->rehash) {	/* doing a rehash */
+		if (cmdp->cmdtype == CMDBUILTIN)
+			prev = builtinloc;
+		else
+			prev = cmdp->param.index;
+	}
+
+	e = ENOENT;
+	idx = -1;
+loop:
+	while ((fullname = padvance(&path, name)) != NULL) {
+		stunalloc(fullname);
+		idx++;
+		if (pathopt) {
+			if (prefix(pathopt, "builtin")) {
+				if (bcmd)
+					goto builtin_success;
+				continue;
+			} else if (!(act & DO_NOFUNC) &&
+				   prefix(pathopt, "func")) {
+				/* handled below */
+			} else {
+				/* ignore unimplemented options */
+				continue;
+			}
+		}
+		/* if rehash, don't redo absolute path names */
+		if (fullname[0] == '/' && idx <= prev) {
+			if (idx < prev)
+				continue;
+			TRACE(("searchexec \"%s\": no change\n", name));
+			goto success;
+		}
+		while (stat64(fullname, &statb) < 0) {
+#ifdef SYSV
+			if (errno == EINTR)
+				continue;
+#endif
+			if (errno != ENOENT && errno != ENOTDIR)
+				e = errno;
+			goto loop;
+		}
+		e = EACCES;	/* if we fail, this will be the error */
+		if (!S_ISREG(statb.st_mode))
+			continue;
+		if (pathopt) {		/* this is a %func directory */
+			stalloc(strlen(fullname) + 1);
+			readcmdfile(fullname);
+			if ((cmdp = cmdlookup(name, 0)) == NULL ||
+			    cmdp->cmdtype != CMDFUNCTION)
+				sh_error("%s not defined in %s", name,
+					 fullname);
+			stunalloc(fullname);
+			goto success;
+		}
+#ifdef notdef
+		/* XXX this code stops root executing stuff, and is buggy
+		   if you need a group from the group list. */
+		if (statb.st_uid == geteuid()) {
+			if ((statb.st_mode & 0100) == 0)
+				goto loop;
+		} else if (statb.st_gid == getegid()) {
+			if ((statb.st_mode & 010) == 0)
+				goto loop;
+		} else {
+			if ((statb.st_mode & 01) == 0)
+				goto loop;
+		}
+#endif
+		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
+		if (!updatetbl) {
+			entry->cmdtype = CMDNORMAL;
+			entry->u.index = idx;
+			return;
+		}
+		INTOFF;
+		cmdp = cmdlookup(name, 1);
+		cmdp->cmdtype = CMDNORMAL;
+		cmdp->param.index = idx;
+		INTON;
+		goto success;
+	}
+
+	/* We failed.  If there was an entry for this command, delete it */
+	if (cmdp && updatetbl)
+		delete_cmd_entry();
+	if (act & DO_ERR)
+		sh_warnx("%s: %s", name, errmsg(e, E_EXEC));
+	entry->cmdtype = CMDUNKNOWN;
+	return;
+
+builtin_success:
+	if (!updatetbl) {
+		entry->cmdtype = CMDBUILTIN;
+		entry->u.cmd = bcmd;
+		return;
+	}
+	INTOFF;
+	cmdp = cmdlookup(name, 1);
+	cmdp->cmdtype = CMDBUILTIN;
+	cmdp->param.cmd = bcmd;
+	INTON;
+success:
+	cmdp->rehash = 0;
+	entry->cmdtype = cmdp->cmdtype;
+	entry->u = cmdp->param;
+}
+
+
+
+/*
+ * Search the table of builtin commands.
+ */
+
+struct builtincmd *
+find_builtin(const char *name)
+{
+	struct builtincmd *bp;
+
+	bp = bsearch(
+		&name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd),
+		pstrcmp
+	);
+	return bp;
+}
+
+
+
+/*
+ * Called when a cd is done.  Marks all commands so the next time they
+ * are executed they will be rehashed.
+ */
+
+void
+hashcd(void)
+{
+	struct tblentry **pp;
+	struct tblentry *cmdp;
+
+	for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+		for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+			if (cmdp->cmdtype == CMDNORMAL || (
+				cmdp->cmdtype == CMDBUILTIN &&
+				!(cmdp->param.cmd->flags & BUILTIN_REGULAR) &&
+				builtinloc > 0
+			))
+				cmdp->rehash = 1;
+		}
+	}
+}
+
+
+
+/*
+ * Fix command hash table when PATH changed.
+ * Called before PATH is changed.  The argument is the new value of PATH;
+ * pathval() still returns the old value at this point.
+ * Called with interrupts off.
+ */
+
+void
+changepath(const char *newval)
+{
+	const char *old, *new;
+	int idx;
+	int firstchange;
+	int bltin;
+
+	old = pathval();
+	new = newval;
+	firstchange = 9999;	/* assume no change */
+	idx = 0;
+	bltin = -1;
+	for (;;) {
+		if (*old != *new) {
+			firstchange = idx;
+			if ((*old == '\0' && *new == ':')
+			 || (*old == ':' && *new == '\0'))
+				firstchange++;
+			old = new;	/* ignore subsequent differences */
+		}
+		if (*new == '\0')
+			break;
+		if (*new == '%' && bltin < 0 && prefix(new + 1, "builtin"))
+			bltin = idx;
+		if (*new == ':') {
+			idx++;
+		}
+		new++, old++;
+	}
+	if (builtinloc < 0 && bltin >= 0)
+		builtinloc = bltin;		/* zap builtins */
+	if (builtinloc >= 0 && bltin < 0)
+		firstchange = 0;
+	clearcmdentry(firstchange);
+	builtinloc = bltin;
+}
+
+
+/*
+ * Clear out command entries.  The argument specifies the first entry in
+ * PATH which has changed.
+ */
+
+STATIC void
+clearcmdentry(int firstchange)
+{
+	struct tblentry **tblp;
+	struct tblentry **pp;
+	struct tblentry *cmdp;
+
+	INTOFF;
+	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
+		pp = tblp;
+		while ((cmdp = *pp) != NULL) {
+			if ((cmdp->cmdtype == CMDNORMAL &&
+			     cmdp->param.index >= firstchange)
+			 || (cmdp->cmdtype == CMDBUILTIN &&
+			     builtinloc >= firstchange)) {
+				*pp = cmdp->next;
+				ckfree(cmdp);
+			} else {
+				pp = &cmdp->next;
+			}
+		}
+	}
+	INTON;
+}
+
+
+
+/*
+ * Locate a command in the command hash table.  If "add" is nonzero,
+ * add the command to the table if it is not already present.  The
+ * variable "lastcmdentry" is set to point to the address of the link
+ * pointing to the entry, so that delete_cmd_entry can delete the
+ * entry.
+ *
+ * Interrupts must be off if called with add != 0.
+ */
+
+struct tblentry **lastcmdentry;
+
+
+STATIC struct tblentry *
+cmdlookup(const char *name, int add)
+{
+	unsigned int hashval;
+	const char *p;
+	struct tblentry *cmdp;
+	struct tblentry **pp;
+
+	p = name;
+	hashval = (unsigned char)*p << 4;
+	while (*p)
+		hashval += (unsigned char)*p++;
+	hashval &= 0x7FFF;
+	pp = &cmdtable[hashval % CMDTABLESIZE];
+	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+		if (equal(cmdp->cmdname, name))
+			break;
+		pp = &cmdp->next;
+	}
+	if (add && cmdp == NULL) {
+		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
+					+ strlen(name) + 1);
+		cmdp->next = NULL;
+		cmdp->cmdtype = CMDUNKNOWN;
+		strcpy(cmdp->cmdname, name);
+	}
+	lastcmdentry = pp;
+	return cmdp;
+}
+
+/*
+ * Delete the command entry returned on the last lookup.
+ */
+
+STATIC void
+delete_cmd_entry(void)
+{
+	struct tblentry *cmdp;
+
+	INTOFF;
+	cmdp = *lastcmdentry;
+	*lastcmdentry = cmdp->next;
+	if (cmdp->cmdtype == CMDFUNCTION)
+		freefunc(cmdp->param.func);
+	ckfree(cmdp);
+	INTON;
+}
+
+
+
+#ifdef notdef
+void
+getcmdentry(char *name, struct cmdentry *entry)
+{
+	struct tblentry *cmdp = cmdlookup(name, 0);
+
+	if (cmdp) {
+		entry->u = cmdp->param;
+		entry->cmdtype = cmdp->cmdtype;
+	} else {
+		entry->cmdtype = CMDUNKNOWN;
+		entry->u.index = 0;
+	}
+}
+#endif
+
+
+/*
+ * Add a new command entry, replacing any existing command entry for
+ * the same name - except special builtins.
+ */
+
+STATIC void
+addcmdentry(char *name, struct cmdentry *entry)
+{
+	struct tblentry *cmdp;
+
+	cmdp = cmdlookup(name, 1);
+	if (cmdp->cmdtype == CMDFUNCTION) {
+		freefunc(cmdp->param.func);
+	}
+	cmdp->cmdtype = entry->cmdtype;
+	cmdp->param = entry->u;
+	cmdp->rehash = 0;
+}
+
+
+/*
+ * Define a shell function.
+ */
+
+void
+defun(char *name, union node *func)
+{
+	struct cmdentry entry;
+
+	INTOFF;
+	entry.cmdtype = CMDFUNCTION;
+	entry.u.func = copyfunc(func);
+	addcmdentry(name, &entry);
+	INTON;
+}
+
+
+/*
+ * Delete a function if it exists.
+ */
+
+void
+unsetfunc(const char *name)
+{
+	struct tblentry *cmdp;
+
+	if ((cmdp = cmdlookup(name, 0)) != NULL &&
+	    cmdp->cmdtype == CMDFUNCTION)
+		delete_cmd_entry();
+}
+
+/*
+ * Locate and print what a word is...
+ */
+
+int
+typecmd(int argc, char **argv)
+{
+	int i;
+	int err = 0;
+
+	for (i = 1; i < argc; i++) {
+		err |= describe_command(out1, argv[i], 1);
+	}
+	return err;
+}
+
+STATIC int
+describe_command(out, command, verbose)
+	struct output *out;
+	char *command;
+	int verbose;
+{
+	struct cmdentry entry;
+	struct tblentry *cmdp;
+	const struct alias *ap;
+	const char *path = pathval();
+
+	if (verbose) {
+		outstr(command, out);
+	}
+
+	/* First look at the keywords */
+	if (findkwd(command)) {
+		outstr(verbose ? " is a shell keyword" : command, out);
+		goto out;
+	}
+
+	/* Then look at the aliases */
+	if ((ap = lookupalias(command, 0)) != NULL) {
+		if (verbose) {
+			outfmt(out, " is an alias for %s", ap->val);
+		} else {
+			outstr("alias ", out);
+			printalias(ap);
+			return 0;
+		}
+		goto out;
+	}
+
+	/* Then check if it is a tracked alias */
+	if ((cmdp = cmdlookup(command, 0)) != NULL) {
+		entry.cmdtype = cmdp->cmdtype;
+		entry.u = cmdp->param;
+	} else {
+		/* Finally use brute force */
+		find_command(command, &entry, DO_ABS, path);
+	}
+
+	switch (entry.cmdtype) {
+	case CMDNORMAL: {
+		int j = entry.u.index;
+		char *p;
+		if (j == -1) {
+			p = command;
+		} else {
+			do {
+				p = padvance(&path, command);
+				stunalloc(p);
+			} while (--j >= 0);
+		}
+		if (verbose) {
+			outfmt(
+				out, " is%s %s",
+				cmdp ? " a tracked alias for" : nullstr, p
+			);
+		} else {
+			outstr(p, out);
+		}
+		break;
+	}
+
+	case CMDFUNCTION:
+		if (verbose) {
+			outstr(" is a shell function", out);
+		} else {
+			outstr(command, out);
+		}
+		break;
+
+	case CMDBUILTIN:
+		if (verbose) {
+			outfmt(
+				out, " is a %sshell builtin",
+				entry.u.cmd->flags & BUILTIN_SPECIAL ?
+					"special " : nullstr
+			);
+		} else {
+			outstr(command, out);
+		}
+		break;
+
+	default:
+		if (verbose) {
+			outstr(": not found\n", out);
+		}
+		return 127;
+	}
+
+out:
+	outc('\n', out);
+	return 0;
+}
+
+int
+commandcmd(argc, argv)
+	int argc;
+	char **argv;
+{
+	int c;
+	enum {
+		VERIFY_BRIEF = 1,
+		VERIFY_VERBOSE = 2,
+	} verify = 0;
+
+	while ((c = nextopt("pvV")) != '\0')
+		if (c == 'V')
+			verify |= VERIFY_VERBOSE;
+		else if (c == 'v')
+			verify |= VERIFY_BRIEF;
+#ifdef DEBUG
+		else if (c != 'p')
+			abort();
+#endif
+
+	if (verify)
+		return describe_command(out1, *argptr, verify - VERIFY_BRIEF);
+
+	return 0;
+}
diff --git a/ash/exec.h b/dash/exec.h
similarity index 85%
rename from ash/exec.h
rename to dash/exec.h
index 26fd09c..daa6f10 100644
--- a/ash/exec.h
+++ b/dash/exec.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: exec.h,v 1.21 2003/08/07 09:05:31 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -39,15 +39,14 @@
 #define CMDNORMAL	0	/* command is an executable program */
 #define CMDFUNCTION	1	/* command is a shell function */
 #define CMDBUILTIN	2	/* command is a shell builtin */
-#define CMDSPLBLTIN	3	/* command is a special shell builtin */
 
 
 struct cmdentry {
 	int cmdtype;
 	union param {
 		int index;
-		int (*bltin)(int, char**);
-		union node *func;
+		const struct builtincmd *cmd;
+		struct funcnode *func;
 	} u;
 };
 
@@ -61,19 +60,18 @@
 
 extern const char *pathopt;	/* set by padvance */
 
-void shellexec(char **, char **, const char *, int, int)
+void shellexec(char **, const char *, int)
     __attribute__((__noreturn__));
 char *padvance(const char **, const char *);
 int hashcmd(int, char **);
 void find_command(char *, struct cmdentry *, int, const char *);
-int (*find_builtin(char *))(int, char **);
-int (*find_splbltin(char *))(int, char **);
+struct builtincmd *find_builtin(const char *);
 void hashcd(void);
 void changepath(const char *);
-void deletefuncs(void);
+#ifdef notdef
 void getcmdentry(char *, struct cmdentry *);
-void addcmdentry(char *, struct cmdentry *);
+#endif
 void defun(char *, union node *);
-int unsetfunc(char *);
+void unsetfunc(const char *);
 int typecmd(int, char **);
-void hash_special_builtins(void);
+int commandcmd(int, char **);
diff --git a/dash/expand.c b/dash/expand.c
new file mode 100644
index 0000000..dafb51f
--- /dev/null
+++ b/dash/expand.c
@@ -0,0 +1,1744 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#ifdef HAVE_GETPWNAM
+#include <pwd.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#if defined(__GLIBC__)
+#if !defined(FNMATCH_BROKEN)
+#include <fnmatch.h>
+#if !defined(GLOB_BROKEN)
+#include <glob.h>
+#endif
+#else
+#include <ctype.h>
+#endif
+#endif
+
+/*
+ * Routines to expand arguments to commands.  We have to deal with
+ * backquotes, shell variables, and file metacharacters.
+ */
+
+#include "shell.h"
+#include "main.h"
+#include "nodes.h"
+#include "eval.h"
+#include "expand.h"
+#include "syntax.h"
+#include "parser.h"
+#include "jobs.h"
+#include "options.h"
+#include "var.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "mystring.h"
+#include "show.h"
+#include "system.h"
+
+/*
+ * _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 */
+
+/* Add CTLESC when necessary. */
+#define QUOTES_ESC	(EXP_FULL | EXP_CASE)
+/* Do not skip NUL characters. */
+#define QUOTES_KEEPNUL	EXP_TILDE
+
+/*
+ * Structure specifying which parts of the string should be searched
+ * for IFS characters.
+ */
+
+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 */
+};
+
+/* 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;
+
+STATIC void argstr(char *, int);
+STATIC char *exptilde(char *, char *, int);
+STATIC void expbackq(union node *, int, int);
+STATIC const char *subevalvar(char *, char *, int, int, int, int, int);
+STATIC char *evalvar(char *, int);
+STATIC size_t strtodest(const char *, const char *, int);
+STATIC void memtodest(const char *, size_t, const char *, int);
+STATIC ssize_t varvalue(char *, int, int);
+STATIC void recordregion(int, int, int);
+STATIC void removerecordregions(int); 
+STATIC void ifsbreakup(char *, struct arglist *);
+STATIC void ifsfree(void);
+STATIC void expandmeta(struct strlist *, int);
+#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
+STATIC void addglob(const glob_t *);
+#else
+STATIC void expmeta(char *, char *);
+#endif
+STATIC void addfname(char *);
+#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+STATIC struct strlist *expsort(struct strlist *);
+STATIC struct strlist *msort(struct strlist *, int);
+#endif
+STATIC int patmatch(char *, const char *);
+#if !defined(__GLIBC__) || defined(FNMATCH_BROKEN)
+STATIC int pmatch(const char *, const char *);
+#else
+#define pmatch(a, b) !fnmatch((a), (b), 0)
+#endif
+STATIC int cvtnum(long);
+STATIC size_t esclen(const char *, const char *);
+STATIC char *scanleft(char *, char *, char *, char *, int, int);
+STATIC char *scanright(char *, char *, char *, char *, int, int);
+STATIC void varunset(const char *, const char *, const char *, int)
+	__attribute__((__noreturn__));
+
+
+/*
+ * Prepare a pattern for a glob(3) call.
+ *
+ * Returns an stalloced string.
+ */
+
+STATIC inline char *
+preglob(const char *pattern, int quoted, int flag) {
+	flag |= RMESCAPE_GLOB;
+	if (quoted) {
+		flag |= RMESCAPE_QUOTED;
+	}
+	return _rmescapes((char *)pattern, flag);
+}
+
+
+STATIC size_t
+esclen(const char *start, const char *p) {
+	size_t esc = 0;
+
+	while (p > start && *--p == CTLESC) {
+		esc++;
+	}
+	return esc;
+}
+
+
+static inline const char *getpwhome(const char *name)
+{
+#ifdef HAVE_GETPWNAM
+	struct passwd *pw = getpwnam(name);
+	return pw ? pw->pw_dir : 0;
+#else
+	return 0;
+#endif
+}
+
+
+/*
+ * Expand shell variables and backquotes inside a here document.
+ */
+
+void
+expandhere(union node *arg, int fd)
+{
+	herefd = fd;
+	expandarg(arg, (struct arglist *)NULL, 0);
+	xwrite(fd, stackblock(), expdest - (char *)stackblock());
+}
+
+
+/*
+ * Perform variable substitution and command substitution on an argument,
+ * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
+ * perform splitting and file name expansion.  When arglist is NULL, perform
+ * here document expansion.
+ */
+
+void
+expandarg(union node *arg, struct arglist *arglist, int flag)
+{
+	struct strlist *sp;
+	char *p;
+
+	argbackq = arg->narg.backquote;
+	STARTSTACKSTR(expdest);
+	ifsfirst.next = NULL;
+	ifslastp = NULL;
+	argstr(arg->narg.text, flag);
+	p = _STPUTC('\0', expdest);
+	expdest = p - 1;
+	if (arglist == NULL) {
+		return;			/* here document expanded */
+	}
+	p = grabstackstr(p);
+	exparg.lastp = &exparg.list;
+	/*
+	 * TODO - EXP_REDIR
+	 */
+	if (flag & EXP_FULL) {
+		ifsbreakup(p, &exparg);
+		*exparg.lastp = NULL;
+		exparg.lastp = &exparg.list;
+		expandmeta(exparg.list, flag);
+	} else {
+		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
+			rmescapes(p);
+		sp = (struct strlist *)stalloc(sizeof (struct strlist));
+		sp->text = p;
+		*exparg.lastp = sp;
+		exparg.lastp = &sp->next;
+	}
+	if (ifsfirst.next)
+		ifsfree();
+	*exparg.lastp = NULL;
+	if (exparg.list) {
+		*arglist->lastp = exparg.list;
+		arglist->lastp = exparg.lastp;
+	}
+}
+
+
+
+/*
+ * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
+ * characters to allow for further processing.  Otherwise treat
+ * $@ like $* since no splitting will be performed.
+ */
+
+STATIC void
+argstr(char *p, int flag)
+{
+	static const char spclchars[] = {
+		'=',
+		':',
+		CTLQUOTEMARK,
+		CTLENDVAR,
+		CTLESC,
+		CTLVAR,
+		CTLBACKQ,
+		CTLBACKQ | CTLQUOTE,
+		CTLENDARI,
+		0
+	};
+	const char *reject = spclchars;
+	int c;
+	int quotes = flag & QUOTES_ESC;
+	int breakall = flag & EXP_WORD;
+	int inquotes;
+	size_t length;
+	int startloc;
+
+	if (!(flag & EXP_VARTILDE)) {
+		reject += 2;
+	} else if (flag & EXP_VARTILDE2) {
+		reject++;
+	}
+	inquotes = 0;
+	length = 0;
+	if (flag & EXP_TILDE) {
+		char *q;
+
+		flag &= ~EXP_TILDE;
+tilde:
+		q = p;
+		if (*q == CTLESC && (flag & EXP_QWORD))
+			q++;
+		if (*q == '~')
+			p = exptilde(p, q, flag);
+	}
+start:
+	startloc = expdest - (char *)stackblock();
+	for (;;) {
+		length += strcspn(p + length, reject);
+		c = p[length];
+		if (c && (!(c & 0x80) || c == CTLENDARI)) {
+			/* c == '=' || c == ':' || c == CTLENDARI */
+			length++;
+		}
+		if (length > 0) {
+			int newloc;
+			expdest = stnputs(p, length, expdest);
+			newloc = expdest - (char *)stackblock();
+			if (breakall && !inquotes && newloc > startloc) {
+				recordregion(startloc, newloc, 0);
+			}
+			startloc = newloc;
+		}
+		p += length + 1;
+		length = 0;
+
+		switch (c) {
+		case '\0':
+			goto breakloop;
+		case '=':
+			if (flag & EXP_VARTILDE2) {
+				p--;
+				continue;
+			}
+			flag |= EXP_VARTILDE2;
+			reject++;
+			/* fall through */
+		case ':':
+			/*
+			 * sort of a hack - expand tildes in variable
+			 * assignments (after the first '=' and after ':'s).
+			 */
+			if (*--p == '~') {
+				goto tilde;
+			}
+			continue;
+		}
+
+		switch (c) {
+		case CTLENDVAR: /* ??? */
+			goto breakloop;
+		case CTLQUOTEMARK:
+			/* "$@" syntax adherence hack */
+			if (
+				!inquotes &&
+				!memcmp(p, dolatstr, DOLATSTRLEN) &&
+				(p[4] == CTLQUOTEMARK || (
+					p[4] == CTLENDVAR &&
+					p[5] == CTLQUOTEMARK
+				))
+			) {
+				p = evalvar(p + 1, flag) + 1;
+				goto start;
+			}
+			inquotes = !inquotes;
+addquote:
+			if (quotes) {
+				p--;
+				length++;
+				startloc++;
+			}
+			break;
+		case CTLESC:
+			startloc++;
+			length++;
+			goto addquote;
+		case CTLVAR:
+			p = evalvar(p, flag);
+			goto start;
+		case CTLBACKQ:
+			c = 0;
+		case CTLBACKQ|CTLQUOTE:
+			expbackq(argbackq->n, c, quotes);
+			argbackq = argbackq->next;
+			goto start;
+		case CTLENDARI:
+			p--;
+			expari(quotes);
+			goto start;
+		}
+	}
+breakloop:
+	;
+}
+
+STATIC char *
+exptilde(char *startp, char *p, int flag)
+{
+	char c;
+	char *name;
+	const char *home;
+	int quotes = flag & QUOTES_ESC;
+	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 {
+		home = getpwhome(name);
+	}
+	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);
+}
+
+
+STATIC void 
+removerecordregions(int endoff)
+{
+	if (ifslastp == NULL)
+		return;
+
+	if (ifsfirst.endoff > endoff) {
+		while (ifsfirst.next != NULL) {
+			struct ifsregion *ifsp;
+			INTOFF;
+			ifsp = ifsfirst.next->next;
+			ckfree(ifsfirst.next);
+			ifsfirst.next = ifsp;
+			INTON;
+		}
+		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;
+		INTOFF;
+		ifsp = ifslastp->next->next;
+		ckfree(ifslastp->next);
+		ifslastp->next = ifsp;
+		INTON;
+	}
+	if (ifslastp->endoff > endoff)
+		ifslastp->endoff = endoff;
+}
+
+
+/*
+ * Expand arithmetic expression.  Backup to start of expression,
+ * evaluate, place result in (backed up) result, adjust string position.
+ */
+void
+expari(int quotes)
+{
+	char *p, *start;
+	int begoff;
+	int flag;
+	int len;
+
+	/*	ifsfree(); */
+
+	/*
+	 * This routine is slightly over-complicated for
+	 * efficiency.  Next we scan backwards looking for the
+	 * start of arithmetic.
+	 */
+	start = stackblock();
+	p = expdest - 1;
+	*p = '\0';
+	p--;
+	do {
+		int esc;
+
+		while (*p != CTLARI) {
+			p--;
+#ifdef DEBUG
+			if (p < start) {
+				sh_error("missing CTLARI (shouldn't happen)");
+			}
+#endif
+		}
+
+		esc = esclen(start, p);
+		if (!(esc % 2)) {
+			break;
+		}
+
+		p -= esc + 1;
+	} while (1);
+
+	begoff = p - start;
+
+	removerecordregions(begoff);
+
+	flag = p[1];
+
+	expdest = p;
+
+	if (quotes)
+		rmescapes(p + 2);
+
+	len = cvtnum(arith(p + 2));
+
+	if (flag != '"')
+		recordregion(begoff, begoff + len, 0);
+}
+
+
+/*
+ * 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;
+	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
+	struct stackmark smark;
+
+	INTOFF;
+	setstackmark(&smark);
+	dest = expdest;
+	startloc = dest - (char *)stackblock();
+	grabstackstr(dest);
+	evalbackcmd(cmd, (struct backcmd *) &in);
+	popstackmark(&smark);
+
+	p = in.buf;
+	i = in.nleft;
+	if (i == 0)
+		goto read;
+	for (;;) {
+		memtodest(p, i, syntax, quotes);
+read:
+		if (in.fd < 0)
+			break;
+		do {
+			i = read(in.fd, buf, sizeof buf);
+		} while (i < 0 && errno == EINTR);
+		TRACE(("expbackq: read returns %d\n", i));
+		if (i <= 0)
+			break;
+		p = buf;
+	}
+
+	if (in.buf)
+		ckfree(in.buf);
+	if (in.fd >= 0) {
+		close(in.fd);
+		back_exitstatus = waitforjob(in.jp);
+	}
+	INTON;
+
+	/* Eat all trailing newlines */
+	dest = expdest;
+	for (; dest > (char *)stackblock() && dest[-1] == '\n';)
+		STUNPUTC(dest);
+	expdest = dest;
+
+	if (quoted == 0)
+		recordregion(startloc, dest - (char *)stackblock(), 0);
+	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
+		(dest - (char *)stackblock()) - startloc,
+		(dest - (char *)stackblock()) - startloc,
+		stackblock() + startloc));
+}
+
+
+STATIC char *
+scanleft(
+	char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+	int zero
+) {
+	char *loc;
+	char *loc2;
+	char c;
+
+	loc = startp;
+	loc2 = rmesc;
+	do {
+		int match;
+		const char *s = loc2;
+		c = *loc2;
+		if (zero) {
+			*loc2 = '\0';
+			s = rmesc;
+		}
+		match = pmatch(str, s);
+		*loc2 = c;
+		if (match)
+			return loc;
+		if (quotes && *loc == CTLESC)
+			loc++;
+		loc++;
+		loc2++;
+	} while (c);
+	return 0;
+}
+
+
+STATIC char *
+scanright(
+	char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+	int zero
+) {
+	int esc = 0;
+	char *loc;
+	char *loc2;
+
+	for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
+		int match;
+		char c = *loc2;
+		const char *s = loc2;
+		if (zero) {
+			*loc2 = '\0';
+			s = rmesc;
+		}
+		match = pmatch(str, s);
+		*loc2 = c;
+		if (match)
+			return loc;
+		loc--;
+		if (quotes) {
+			if (--esc < 0) {
+				esc = esclen(startp, loc);
+			}
+			if (esc % 2) {
+				esc--;
+				loc--;
+			}
+		}
+	}
+	return 0;
+}
+
+STATIC const char *
+subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
+{
+	char *startp;
+	char *loc;
+	int saveherefd = herefd;
+	struct nodelist *saveargbackq = argbackq;
+	int amount;
+	char *rmesc, *rmescend;
+	int zero;
+	char *(*scan)(char *, char *, char *, char *, int , int);
+
+	herefd = -1;
+	argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
+	STPUTC('\0', expdest);
+	herefd = saveherefd;
+	argbackq = saveargbackq;
+	startp = stackblock() + startloc;
+
+	switch (subtype) {
+	case VSASSIGN:
+		setvar(str, startp, 0);
+		amount = startp - expdest;
+		STADJUST(amount, expdest);
+		return startp;
+
+	case VSQUESTION:
+		varunset(p, str, startp, varflags);
+		/* NOTREACHED */
+	}
+
+	subtype -= VSTRIMRIGHT;
+#ifdef DEBUG
+	if (subtype < 0 || subtype > 3)
+		abort();
+#endif
+
+	rmesc = startp;
+	rmescend = stackblock() + strloc;
+	if (quotes) {
+		rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
+		if (rmesc != startp) {
+			rmescend = expdest;
+			startp = stackblock() + startloc;
+		}
+	}
+	rmescend--;
+	str = stackblock() + strloc;
+	preglob(str, varflags & VSQUOTE, 0);
+
+	/* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
+	zero = subtype >> 1;
+	/* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
+	scan = (subtype & 1) ^ zero ? scanleft : scanright;
+
+	loc = scan(startp, rmesc, rmescend, str, quotes, zero);
+	if (loc) {
+		if (zero) {
+			memmove(startp, loc, str - loc);
+			loc = startp + (str - loc) - 1;
+		}
+		*loc = '\0';
+		amount = loc - expdest;
+		STADJUST(amount, expdest);
+	}
+	return loc;
+}
+
+
+/*
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
+ */
+STATIC char *
+evalvar(char *p, int flag)
+{
+	int subtype;
+	int varflags;
+	char *var;
+	int patloc;
+	int c;
+	int startloc;
+	ssize_t varlen;
+	int easy;
+	int quotes;
+	int quoted;
+
+	quotes = flag & QUOTES_ESC;
+	varflags = *p++;
+	subtype = varflags & VSTYPE;
+	quoted = varflags & VSQUOTE;
+	var = p;
+	easy = (!quoted || (*var == '@' && shellparam.nparam));
+	startloc = expdest - (char *)stackblock();
+	p = strchr(p, '=') + 1;
+
+again:
+	varlen = varvalue(var, varflags, flag);
+	if (varflags & VSNUL)
+		varlen--;
+
+	if (subtype == VSPLUS) {
+		varlen = -1 - varlen;
+		goto vsplus;
+	}
+
+	if (subtype == VSMINUS) {
+vsplus:
+		if (varlen < 0) {
+			argstr(
+				p, flag | EXP_TILDE |
+					(quoted ?  EXP_QWORD : EXP_WORD)
+			);
+			goto end;
+		}
+		if (easy)
+			goto record;
+		goto end;
+	}
+
+	if (subtype == VSASSIGN || subtype == VSQUESTION) {
+		if (varlen < 0) {
+			if (subevalvar(p, var, 0, subtype, startloc,
+				       varflags, 0)) {
+				varflags &= ~VSNUL;
+				/* 
+				 * Remove any recorded regions beyond 
+				 * start of variable 
+				 */
+				removerecordregions(startloc);
+				goto again;
+			}
+			goto end;
+		}
+		if (easy)
+			goto record;
+		goto end;
+	}
+
+	if (varlen < 0 && uflag)
+		varunset(p, var, 0, 0);
+
+	if (subtype == VSLENGTH) {
+		cvtnum(varlen > 0 ? varlen : 0);
+		goto record;
+	}
+
+	if (subtype == VSNORMAL) {
+		if (!easy)
+			goto end;
+record:
+		recordregion(startloc, expdest - (char *)stackblock(), quoted);
+		goto end;
+	}
+
+#ifdef DEBUG
+	switch (subtype) {
+	case VSTRIMLEFT:
+	case VSTRIMLEFTMAX:
+	case VSTRIMRIGHT:
+	case VSTRIMRIGHTMAX:
+		break;
+	default:
+		abort();
+	}
+#endif
+
+	if (varlen >= 0) {
+		/*
+		 * Terminate the string and start recording the pattern
+		 * right after it
+		 */
+		STPUTC('\0', expdest);
+		patloc = expdest - (char *)stackblock();
+		if (subevalvar(p, NULL, patloc, subtype,
+			       startloc, varflags, quotes) == 0) {
+			int amount = expdest - (
+				(char *)stackblock() + patloc - 1
+			);
+			STADJUST(-amount, expdest);
+		}
+		/* Remove any recorded regions beyond start of variable */
+		removerecordregions(startloc);
+		goto record;
+	}
+
+end:
+	if (subtype != VSNORMAL) {	/* skip to end of alternative */
+		int nesting = 1;
+		for (;;) {
+			if ((c = *p++) == CTLESC)
+				p++;
+			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
+				if (varlen >= 0)
+					argbackq = argbackq->next;
+			} else if (c == CTLVAR) {
+				if ((*p++ & VSTYPE) != VSNORMAL)
+					nesting++;
+			} else if (c == CTLENDVAR) {
+				if (--nesting == 0)
+					break;
+			}
+		}
+	}
+	return p;
+}
+
+
+/*
+ * Put a string on the stack.
+ */
+
+STATIC void
+memtodest(const char *p, size_t len, const char *syntax, int quotes) {
+	char *q;
+
+	if (unlikely(!len))
+		return;
+
+	q = makestrspace(len * 2, expdest);
+
+	do {
+		int c = (unsigned char)*p++;
+		if (c) {
+			if ((quotes & QUOTES_ESC) &&
+			    (syntax[c] == CCTL || syntax[c] == CBACK))
+				USTPUTC(CTLESC, q);
+		} else if (!(quotes & QUOTES_KEEPNUL))
+			continue;
+		USTPUTC(c, q);
+	} while (--len);
+
+	expdest = q;
+}
+
+
+STATIC size_t
+strtodest(p, syntax, quotes)
+	const char *p;
+	const char *syntax;
+	int quotes;
+{
+	size_t len = strlen(p);
+	memtodest(p, len, syntax, quotes);
+	return len;
+}
+
+
+
+/*
+ * Add the value of a specialized variable to the stack string.
+ */
+
+STATIC ssize_t
+varvalue(char *name, int varflags, int flags)
+{
+	int num;
+	char *p;
+	int i;
+	int sep;
+	char sepc;
+	char **ap;
+	char const *syntax;
+	int quoted = varflags & VSQUOTE;
+	int subtype = varflags & VSTYPE;
+	int discard = subtype == VSPLUS || subtype == VSLENGTH;
+	int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL;
+	ssize_t len = 0;
+
+	sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0;
+	syntax = quoted ? DQSYNTAX : BASESYNTAX;
+
+	switch (*name) {
+	case '$':
+		num = rootpid;
+		goto numvar;
+	case '?':
+		num = exitstatus;
+		goto numvar;
+	case '#':
+		num = shellparam.nparam;
+		goto numvar;
+	case '!':
+		num = backgndpid;
+		if (num == 0)
+			return -1;
+numvar:
+		len = cvtnum(num);
+		break;
+	case '-':
+		p = makestrspace(NOPTS, expdest);
+		for (i = NOPTS - 1; i >= 0; i--) {
+			if (optlist[i]) {
+				USTPUTC(optletters[i], p);
+				len++;
+			}
+		}
+		expdest = p;
+		break;
+	case '@':
+		if (sep)
+			goto param;
+		/* fall through */
+	case '*':
+		sep = ifsset() ? ifsval()[0] : ' ';
+param:
+		if (!(ap = shellparam.p))
+			return -1;
+		sepc = sep;
+		while ((p = *ap++)) {
+			len += strtodest(p, syntax, quotes);
+
+			if (*ap && sep) {
+				len++;
+				memtodest(&sepc, 1, syntax, quotes);
+			}
+		}
+		break;
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+		num = atoi(name);
+		if (num < 0 || num > shellparam.nparam)
+			return -1;
+		p = num ? shellparam.p[num - 1] : arg0;
+		goto value;
+	default:
+		p = lookupvar(name);
+value:
+		if (!p)
+			return -1;
+
+		len = strtodest(p, syntax, quotes);
+		break;
+	}
+
+	if (discard)
+		STADJUST(-len, expdest);
+	return len;
+}
+
+
+
+/*
+ * 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 {
+		INTOFF;
+		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
+		ifsp->next = NULL;
+		ifslastp->next = ifsp;
+		INTON;
+	}
+	ifslastp = ifsp;
+	ifslastp->begoff = start;
+	ifslastp->endoff = end;
+	ifslastp->nulonly = nulonly;
+}
+
+
+
+/*
+ * Break the argument string into pieces based upon IFS and add the
+ * strings to the argument list.  The regions of the string to be
+ * searched for IFS characters have been stored by recordregion.
+ */
+STATIC void
+ifsbreakup(char *string, struct arglist *arglist)
+{
+	struct ifsregion *ifsp;
+	struct strlist *sp;
+	char *start;
+	char *p;
+	char *q;
+	const char *ifs, *realifs;
+	int ifsspc;
+	int nulonly;
+
+
+	start = string;
+	if (ifslastp != NULL) {
+		ifsspc = 0;
+		nulonly = 0;
+		realifs = ifsset() ? ifsval() : defifs;
+		ifsp = &ifsfirst;
+		do {
+			p = string + ifsp->begoff;
+			nulonly = ifsp->nulonly;
+			ifs = nulonly ? nullstr : realifs;
+			ifsspc = 0;
+			while (p < string + ifsp->endoff) {
+				q = p;
+				if (*p == CTLESC)
+					p++;
+				if (strchr(ifs, *p)) {
+					if (!nulonly)
+						ifsspc = (strchr(defifs, *p) != NULL);
+					/* Ignore IFS whitespace at start */
+					if (q == start && ifsspc) {
+						p++;
+						start = p;
+						continue;
+					}
+					*q = '\0';
+					sp = (struct strlist *)stalloc(sizeof *sp);
+					sp->text = start;
+					*arglist->lastp = sp;
+					arglist->lastp = &sp->next;
+					p++;
+					if (!nulonly) {
+						for (;;) {
+							if (p >= string + ifsp->endoff) {
+								break;
+							}
+							q = p;
+							if (*p == CTLESC)
+								p++;
+							if (strchr(ifs, *p) == NULL ) {
+								p = q;
+								break;
+							} else if (strchr(defifs, *p) == NULL) {
+								if (ifsspc) {
+									p++;
+									ifsspc = 0;
+								} else {
+									p = q;
+									break;
+								}
+							} else
+								p++;
+						}
+					}
+					start = p;
+				} else
+					p++;
+			}
+		} while ((ifsp = ifsp->next) != NULL);
+		if (nulonly)
+			goto add;
+	}
+
+	if (!*start)
+		return;
+
+add:
+	sp = (struct strlist *)stalloc(sizeof *sp);
+	sp->text = start;
+	*arglist->lastp = sp;
+	arglist->lastp = &sp->next;
+}
+
+STATIC void
+ifsfree(void)
+{
+	struct ifsregion *p;
+
+	INTOFF;
+	p = ifsfirst.next;
+	do {
+		struct ifsregion *ifsp;
+		ifsp = p->next;
+		ckfree(p);
+		p = ifsp;
+	} while (p);
+	ifslastp = NULL;
+	ifsfirst.next = NULL;
+	INTON;
+}
+
+
+
+/*
+ * Expand shell metacharacters.  At this point, the only control characters
+ * should be escapes.  The results are stored in the list exparg.
+ */
+
+#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
+STATIC void
+expandmeta(str, flag)
+	struct strlist *str;
+	int flag;
+{
+	/* TODO - EXP_REDIR */
+
+	while (str) {
+		const char *p;
+		glob_t pglob;
+		int i;
+
+		if (fflag)
+			goto nometa;
+		INTOFF;
+		p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
+		i = glob(p, GLOB_NOMAGIC, 0, &pglob);
+		if (p != str->text)
+			ckfree(p);
+		switch (i) {
+		case 0:
+			if (!(pglob.gl_flags & GLOB_MAGCHAR))
+				goto nometa2;
+			addglob(&pglob);
+			globfree(&pglob);
+			INTON;
+			break;
+		case GLOB_NOMATCH:
+nometa2:
+			globfree(&pglob);
+			INTON;
+nometa:
+			*exparg.lastp = str;
+			rmescapes(str->text);
+			exparg.lastp = &str->next;
+			break;
+		default:	/* GLOB_NOSPACE */
+			sh_error("Out of space");
+		}
+		str = str->next;
+	}
+}
+
+
+/*
+ * Add the result of glob(3) to the list.
+ */
+
+STATIC void
+addglob(pglob)
+	const glob_t *pglob;
+{
+	char **p = pglob->gl_pathv;
+
+	do {
+		addfname(*p);
+	} while (*++p);
+}
+
+
+#else	/* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
+STATIC char *expdir;
+
+
+STATIC void
+expandmeta(struct strlist *str, int flag)
+{
+	static const char metachars[] = {
+		'*', '?', '[', 0
+	};
+	/* TODO - EXP_REDIR */
+
+	while (str) {
+		struct strlist **savelastp;
+		struct strlist *sp;
+		char *p;
+
+		if (fflag)
+			goto nometa;
+		if (!strpbrk(str->text, metachars))
+			goto nometa;
+		savelastp = exparg.lastp;
+
+		INTOFF;
+		p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
+		{
+			int i = strlen(str->text);
+			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
+		}
+
+		expmeta(expdir, p);
+		ckfree(expdir);
+		if (p != str->text)
+			ckfree(p);
+		INTON;
+		if (exparg.lastp == savelastp) {
+			/*
+			 * no matches
+			 */
+nometa:
+			*exparg.lastp = str;
+			rmescapes(str->text);
+			exparg.lastp = &str->next;
+		} else {
+			*exparg.lastp = NULL;
+			*savelastp = sp = expsort(*savelastp);
+			while (sp->next != NULL)
+				sp = sp->next;
+			exparg.lastp = &sp->next;
+		}
+		str = str->next;
+	}
+}
+
+
+/*
+ * Do metacharacter (i.e. *, ?, [...]) expansion.
+ */
+
+STATIC void
+expmeta(char *enddir, char *name)
+{
+	char *p;
+	const char *cp;
+	char *start;
+	char *endname;
+	int metaflag;
+	struct stat64 statb;
+	DIR *dirp;
+	struct dirent *dp;
+	int atend;
+	int matchdot;
+
+	metaflag = 0;
+	start = name;
+	for (p = name; *p; p++) {
+		if (*p == '*' || *p == '?')
+			metaflag = 1;
+		else if (*p == '[') {
+			char *q = p + 1;
+			if (*q == '!')
+				q++;
+			for (;;) {
+				if (*q == '\\')
+					q++;
+				if (*q == '/' || *q == '\0')
+					break;
+				if (*++q == ']') {
+					metaflag = 1;
+					break;
+				}
+			}
+		} else if (*p == '\\')
+			p++;
+		else if (*p == '/') {
+			if (metaflag)
+				goto out;
+			start = p + 1;
+		}
+	}
+out:
+	if (metaflag == 0) {	/* we've reached the end of the file name */
+		if (enddir != expdir)
+			metaflag++;
+		p = name;
+		do {
+			if (*p == '\\')
+				p++;
+			*enddir++ = *p;
+		} while (*p++);
+		if (metaflag == 0 || lstat64(expdir, &statb) >= 0)
+			addfname(expdir);
+		return;
+	}
+	endname = p;
+	if (name < start) {
+		p = name;
+		do {
+			if (*p == '\\')
+				p++;
+			*enddir++ = *p++;
+		} while (p < start);
+	}
+	if (enddir == expdir) {
+		cp = ".";
+	} else if (enddir == expdir + 1 && *expdir == '/') {
+		cp = "/";
+	} else {
+		cp = expdir;
+		enddir[-1] = '\0';
+	}
+	if ((dirp = opendir(cp)) == NULL)
+		return;
+	if (enddir != expdir)
+		enddir[-1] = '/';
+	if (*endname == 0) {
+		atend = 1;
+	} else {
+		atend = 0;
+		*endname++ = '\0';
+	}
+	matchdot = 0;
+	p = start;
+	if (*p == '\\')
+		p++;
+	if (*p == '.')
+		matchdot++;
+	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
+		if (dp->d_name[0] == '.' && ! matchdot)
+			continue;
+		if (pmatch(start, dp->d_name)) {
+			if (atend) {
+				scopy(dp->d_name, enddir);
+				addfname(expdir);
+			} else {
+				for (p = enddir, cp = dp->d_name;
+				     (*p++ = *cp++) != '\0';)
+					continue;
+				p[-1] = '/';
+				expmeta(p, endname);
+			}
+		}
+	}
+	closedir(dirp);
+	if (! atend)
+		endname[-1] = '/';
+}
+#endif	/* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
+
+
+/*
+ * Add a file name to the list.
+ */
+
+STATIC void
+addfname(char *name)
+{
+	struct strlist *sp;
+
+	sp = (struct strlist *)stalloc(sizeof *sp);
+	sp->text = sstrdup(name);
+	*exparg.lastp = sp;
+	exparg.lastp = &sp->next;
+}
+
+
+#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+/*
+ * Sort the results of file name expansion.  It calculates the number of
+ * strings to sort and then calls msort (short for merge sort) to do the
+ * work.
+ */
+
+STATIC struct strlist *
+expsort(struct strlist *str)
+{
+	int len;
+	struct strlist *sp;
+
+	len = 0;
+	for (sp = str ; sp ; sp = sp->next)
+		len++;
+	return msort(str, len);
+}
+
+
+STATIC struct strlist *
+msort(struct strlist *list, int len)
+{
+	struct strlist *p, *q = NULL;
+	struct strlist **lpp;
+	int half;
+	int n;
+
+	if (len <= 1)
+		return list;
+	half = len >> 1;
+	p = list;
+	for (n = half ; --n >= 0 ; ) {
+		q = p;
+		p = p->next;
+	}
+	q->next = NULL;			/* terminate first half of list */
+	q = msort(list, half);		/* sort first half of list */
+	p = msort(p, len - half);		/* sort second half */
+	lpp = &list;
+	for (;;) {
+		if (strcmp(p->text, q->text) < 0) {
+			*lpp = p;
+			lpp = &p->next;
+			if ((p = *lpp) == NULL) {
+				*lpp = q;
+				break;
+			}
+		} else {
+			*lpp = q;
+			lpp = &q->next;
+			if ((q = *lpp) == NULL) {
+				*lpp = p;
+				break;
+			}
+		}
+	}
+	return list;
+}
+#endif
+
+
+/*
+ * Returns true if the pattern matches the string.
+ */
+
+STATIC inline int
+patmatch(char *pattern, const char *string)
+{
+	return pmatch(preglob(pattern, 0, 0), string);
+}
+
+
+#if !defined(__GLIBC__) || defined(FNMATCH_BROKEN)
+STATIC int ccmatch(const char *p, int chr, const char **r)
+{
+	static const struct class {
+		char name[10];
+		int (*fn)(int);
+	} classes[] = {
+		{ .name = ":alnum:]", .fn = isalnum },
+		{ .name = ":cntrl:]", .fn = iscntrl },
+		{ .name = ":lower:]", .fn = islower },
+		{ .name = ":space:]", .fn = isspace },
+		{ .name = ":alpha:]", .fn = isalpha },
+		{ .name = ":digit:]", .fn = isdigit },
+		{ .name = ":print:]", .fn = isprint },
+		{ .name = ":upper:]", .fn = isupper },
+		{ .name = ":blank:]", .fn = isblank },
+		{ .name = ":graph:]", .fn = isgraph },
+		{ .name = ":punct:]", .fn = ispunct },
+		{ .name = ":xdigit:]", .fn = isxdigit },
+	};
+	const struct class *class, *end;
+
+	end = classes + sizeof(classes) / sizeof(classes[0]);
+	for (class = classes; class < end; class++) {
+		const char *q;
+
+		q = prefix(p, class->name);
+		if (!q)
+			continue;
+		*r = q;
+		return class->fn(chr);
+	}
+
+	*r = 0;
+	return 0;
+}
+
+STATIC int
+pmatch(const char *pattern, const char *string)
+{
+	const char *p, *q;
+	char c;
+
+	p = pattern;
+	q = string;
+	for (;;) {
+		switch (c = *p++) {
+		case '\0':
+			goto breakloop;
+		case '\\':
+			if (*p) {
+				c = *p++;
+			}
+			goto dft;
+		case '?':
+			if (*q++ == '\0')
+				return 0;
+			break;
+		case '*':
+			c = *p;
+			while (c == '*')
+				c = *++p;
+			if (c != '\\' && c != '?' && c != '*' && c != '[') {
+				while (*q != c) {
+					if (*q == '\0')
+						return 0;
+					q++;
+				}
+			}
+			do {
+				if (pmatch(p, q))
+					return 1;
+			} while (*q++ != '\0');
+			return 0;
+		case '[': {
+			const char *startp;
+			int invert, found;
+			char chr;
+
+			startp = p;
+			invert = 0;
+			if (*p == '!') {
+				invert++;
+				p++;
+			}
+			found = 0;
+			chr = *q++;
+			if (chr == '\0')
+				return 0;
+			c = *p++;
+			do {
+				if (!c) {
+					p = startp;
+					c = *p;
+					goto dft;
+				}
+				if (c == '[') {
+					const char *r;
+
+					found |= ccmatch(p, chr, &r);
+					if (r) {
+						p = r;
+						continue;
+					}
+				} else if (c == '\\')
+					c = *p++;
+				if (*p == '-' && p[1] != ']') {
+					p++;
+					if (*p == '\\')
+						p++;
+					if (chr >= c && chr <= *p)
+						found = 1;
+					p++;
+				} else {
+					if (chr == c)
+						found = 1;
+				}
+			} while ((c = *p++) != ']');
+			if (found == invert)
+				return 0;
+			break;
+		}
+dft:	        default:
+			if (*q++ != c)
+				return 0;
+			break;
+		}
+	}
+breakloop:
+	if (*q != '\0')
+		return 0;
+	return 1;
+}
+#endif
+
+
+
+/*
+ * Remove any CTLESC characters from a string.
+ */
+
+char *
+_rmescapes(char *str, int flag)
+{
+	char *p, *q, *r;
+	static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
+	unsigned inquotes;
+	int notescaped;
+	int globbing;
+
+	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 = mempcpy(q, str, 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++ = '\\';
+			}
+		}
+		notescaped = globbing;
+copy:
+		*q++ = *p++;
+	}
+	*q = '\0';
+	if (flag & RMESCAPE_GROW) {
+		expdest = r;
+		STADJUST(q - r + 1, expdest);
+	}
+	return r;
+}
+
+
+
+/*
+ * See if a pattern matches in a case statement.
+ */
+
+int
+casematch(union node *pattern, char *val)
+{
+	struct stackmark smark;
+	int result;
+
+	setstackmark(&smark);
+	argbackq = pattern->narg.backquote;
+	STARTSTACKSTR(expdest);
+	ifslastp = NULL;
+	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
+	STACKSTRNUL(expdest);
+	result = patmatch(stackblock(), val);
+	popstackmark(&smark);
+	return result;
+}
+
+/*
+ * Our own itoa().
+ */
+
+STATIC int
+cvtnum(long num)
+{
+	int len;
+
+	expdest = makestrspace(32, expdest);
+	len = fmtstr(expdest, 32, "%ld", num);
+	STADJUST(len, expdest);
+	return len;
+}
+
+STATIC void
+varunset(const char *end, const char *var, const char *umsg, int varflags)
+{
+	const char *msg;
+	const char *tail;
+
+	tail = nullstr;
+	msg = "parameter not set";
+	if (umsg) {
+		if (*end == CTLENDVAR) {
+			if (varflags & VSNUL)
+				tail = " or null";
+		} else
+			msg = umsg;
+	}
+	sh_error("%.*s: %s%s", end - var - 1, var, msg, tail);
+}
diff --git a/ash/expand.h b/dash/expand.h
similarity index 85%
rename from ash/expand.h
rename to dash/expand.h
index 44ca8c3..6b7d607 100644
--- a/ash/expand.h
+++ b/dash/expand.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: expand.h,v 1.14 2003/08/07 09:05:32 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -54,18 +54,25 @@
 #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 */
 
 
 union node;
 void expandhere(union node *, int);
 void expandarg(union node *, struct arglist *, int);
 void expari(int);
-int patmatch(char *, char *, int);
-void rmescapes(char *);
+#define rmescapes(p) _rmescapes((p), 0)
+char *_rmescapes(char *, int);
 int casematch(union node *, char *);
 
 /* From arith.y */
 int arith(const char *);
 int expcmd(int , char **);
+#ifdef USE_LEX
 void arith_lex_reset(void);
+#else
+#define arith_lex_reset()
+#endif
 int yylex(void);
diff --git a/ash/funcs/cmv b/dash/funcs/cmv
similarity index 83%
rename from ash/funcs/cmv
rename to dash/funcs/cmv
index 667f846..91a67c5 100644
--- a/ash/funcs/cmv
+++ b/dash/funcs/cmv
@@ -1,6 +1,7 @@
-#	$NetBSD: cmv,v 1.7 1995/05/11 21:31:05 christos Exp $
 # Copyright (c) 1991, 1993
 #	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 #
 # This code is derived from software contributed to Berkeley by
 # Kenneth Almquist.
@@ -13,11 +14,7 @@
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#	This product includes software developed by the University of
-#	California, Berkeley and its contributors.
-# 4. Neither the name of the University nor the names of its contributors
+# 3. Neither the name of the University nor the names of its contributors
 #    may be used to endorse or promote products derived from this software
 #    without specific prior written permission.
 #
diff --git a/ash/funcs/dirs b/dash/funcs/dirs
similarity index 86%
rename from ash/funcs/dirs
rename to dash/funcs/dirs
index 68bb317..5f6ce63 100644
--- a/ash/funcs/dirs
+++ b/dash/funcs/dirs
@@ -1,6 +1,7 @@
-#	$NetBSD: dirs,v 1.7 1995/05/11 21:31:08 christos Exp $
 # Copyright (c) 1991, 1993
 #	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 #
 # This code is derived from software contributed to Berkeley by
 # Kenneth Almquist.
@@ -13,11 +14,7 @@
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#	This product includes software developed by the University of
-#	California, Berkeley and its contributors.
-# 4. Neither the name of the University nor the names of its contributors
+# 3. Neither the name of the University nor the names of its contributors
 #    may be used to endorse or promote products derived from this software
 #    without specific prior written permission.
 #
diff --git a/ash/funcs/kill b/dash/funcs/kill
similarity index 83%
rename from ash/funcs/kill
rename to dash/funcs/kill
index 75b0180..c5df95f 100644
--- a/ash/funcs/kill
+++ b/dash/funcs/kill
@@ -1,6 +1,7 @@
-#	$NetBSD: kill,v 1.7 1995/05/11 21:31:10 christos Exp $
 # Copyright (c) 1991, 1993
 #	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 #
 # This code is derived from software contributed to Berkeley by
 # Kenneth Almquist.
@@ -13,11 +14,7 @@
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#	This product includes software developed by the University of
-#	California, Berkeley and its contributors.
-# 4. Neither the name of the University nor the names of its contributors
+# 3. Neither the name of the University nor the names of its contributors
 #    may be used to endorse or promote products derived from this software
 #    without specific prior written permission.
 #
diff --git a/ash/funcs/login b/dash/funcs/login
similarity index 82%
rename from ash/funcs/login
rename to dash/funcs/login
index 7ae08b2..215e535 100644
--- a/ash/funcs/login
+++ b/dash/funcs/login
@@ -1,6 +1,7 @@
-#	$NetBSD: login,v 1.7 1995/05/11 21:31:11 christos Exp $
 # Copyright (c) 1991, 1993
 #	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 #
 # This code is derived from software contributed to Berkeley by
 # Kenneth Almquist.
@@ -13,11 +14,7 @@
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#	This product includes software developed by the University of
-#	California, Berkeley and its contributors.
-# 4. Neither the name of the University nor the names of its contributors
+# 3. Neither the name of the University nor the names of its contributors
 #    may be used to endorse or promote products derived from this software
 #    without specific prior written permission.
 #
diff --git a/ash/funcs/newgrp b/dash/funcs/newgrp
similarity index 82%
rename from ash/funcs/newgrp
rename to dash/funcs/newgrp
index 796a4f1..ec0e7e5 100644
--- a/ash/funcs/newgrp
+++ b/dash/funcs/newgrp
@@ -1,6 +1,7 @@
-#	$NetBSD: newgrp,v 1.7 1995/05/11 21:31:12 christos Exp $
 # Copyright (c) 1991, 1993
 #	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 #
 # This code is derived from software contributed to Berkeley by
 # Kenneth Almquist.
@@ -13,11 +14,7 @@
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#	This product includes software developed by the University of
-#	California, Berkeley and its contributors.
-# 4. Neither the name of the University nor the names of its contributors
+# 3. Neither the name of the University nor the names of its contributors
 #    may be used to endorse or promote products derived from this software
 #    without specific prior written permission.
 #
diff --git a/ash/funcs/popd b/dash/funcs/popd
similarity index 86%
rename from ash/funcs/popd
rename to dash/funcs/popd
index b2b65d5..7bccf50 100644
--- a/ash/funcs/popd
+++ b/dash/funcs/popd
@@ -1,6 +1,7 @@
-#	$NetBSD: popd,v 1.7 1995/05/11 21:31:13 christos Exp $
 # Copyright (c) 1991, 1993
 #	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 #
 # This code is derived from software contributed to Berkeley by
 # Kenneth Almquist.
@@ -13,11 +14,7 @@
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#	This product includes software developed by the University of
-#	California, Berkeley and its contributors.
-# 4. Neither the name of the University nor the names of its contributors
+# 3. Neither the name of the University nor the names of its contributors
 #    may be used to endorse or promote products derived from this software
 #    without specific prior written permission.
 #
diff --git a/ash/funcs/pushd b/dash/funcs/pushd
similarity index 86%
rename from ash/funcs/pushd
rename to dash/funcs/pushd
index b393038..19ac8e0 100644
--- a/ash/funcs/pushd
+++ b/dash/funcs/pushd
@@ -1,6 +1,7 @@
-#	$NetBSD: pushd,v 1.7 1995/05/11 21:31:15 christos Exp $
 # Copyright (c) 1991, 1993
 #	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 #
 # This code is derived from software contributed to Berkeley by
 # Kenneth Almquist.
@@ -13,11 +14,7 @@
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#	This product includes software developed by the University of
-#	California, Berkeley and its contributors.
-# 4. Neither the name of the University nor the names of its contributors
+# 3. Neither the name of the University nor the names of its contributors
 #    may be used to endorse or promote products derived from this software
 #    without specific prior written permission.
 #
diff --git a/ash/funcs/suspend b/dash/funcs/suspend
similarity index 82%
rename from ash/funcs/suspend
rename to dash/funcs/suspend
index 8a4197d..4484467 100644
--- a/ash/funcs/suspend
+++ b/dash/funcs/suspend
@@ -1,6 +1,7 @@
-#	$NetBSD: suspend,v 1.7 1995/05/11 21:31:17 christos Exp $
 # Copyright (c) 1991, 1993
 #	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 #
 # This code is derived from software contributed to Berkeley by
 # Kenneth Almquist.
@@ -13,11 +14,7 @@
 # 2. Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in the
 #    documentation and/or other materials provided with the distribution.
-# 3. All advertising materials mentioning features or use of this software
-#    must display the following acknowledgement:
-#	This product includes software developed by the University of
-#	California, Berkeley and its contributors.
-# 4. Neither the name of the University nor the names of its contributors
+# 3. Neither the name of the University nor the names of its contributors
 #    may be used to endorse or promote products derived from this software
 #    without specific prior written permission.
 #
diff --git a/dash/hetio.c b/dash/hetio.c
new file mode 100644
index 0000000..f7d175f
--- /dev/null
+++ b/dash/hetio.c
@@ -0,0 +1,397 @@
+/*
+ * Termios command line History and Editting for NetBSD sh (ash)
+ * Copyright (c) 1999
+ *	Main code:	Adam Rogoyski <rogoyski@cs.utexas.edu>
+ *	Etc:		Dave Cinege <dcinege@psychosis.com>
+ *
+ * You may use this code as you wish, so long as the original author(s)
+ * are attributed in any redistributions of the source code.
+ * This code is 'as is' with no warranty.
+ * This code may safely be consumed by a BSD or GPL license.
+ *
+ * v 0.5  19990328	Initial release
+ *
+ * Future plans: Simple file and path name completion. (like BASH)
+ *
+ */
+
+/*
+Usage and Known bugs:
+	Terminal key codes are not extensive, and more will probably
+	need to be added. This version was created on Debian GNU/Linux 2.x.
+	Delete, Backspace, Home, End, and the arrow keys were tested
+	to work in an Xterm and console. Ctrl-A also works as Home.
+	Ctrl-E also works as End. Ctrl-D and Ctrl-U perform their respective
+	functions. The binary size increase is <3K.
+
+	Editting will not display correctly for lines greater then the
+	terminal width. (more then one line.) However, history will.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+
+#include "input.h"
+#include "output.h"
+
+#include "hetio.h"
+
+
+#define  MAX_HISTORY   15			/* Maximum length of the linked list for the command line history */
+
+#define ESC	27
+#define DEL	127
+
+static struct history *his_front = NULL;	/* First element in command line list */
+static struct history *his_end = NULL;		/* Last element in command line list */
+static struct termios old_term, new_term;	/* Current termio and the previous termio before starting ash */
+
+static int history_counter = 0;			/* Number of commands in history list */
+static int reset_term = 0;			/* Set to true if the terminal needs to be reset upon exit */
+static int hetio_inter = 0;
+
+struct history
+{
+   char *s;
+   struct history *p;
+   struct history *n;
+};
+
+
+void input_delete    (int);
+void input_home      (int *);
+void input_end       (int *, int);
+void input_backspace (int *, int *);
+
+
+
+void hetio_init(void)
+{
+	hetio_inter = 1;
+}
+
+
+void hetio_reset_term(void)
+{
+	if (reset_term)
+		tcsetattr(1, TCSANOW, &old_term);
+}
+
+
+void setIO(struct termios *new, struct termios *old)	/* Set terminal IO to canonical mode, and save old term settings. */
+{
+	tcgetattr(0, old);
+	memcpy(new, old, sizeof(*new));
+	new->c_cc[VMIN] = 1;
+	new->c_cc[VTIME] = 0;
+	new->c_lflag &= ~ICANON; /* unbuffered input */
+	new->c_lflag &= ~ECHO;
+	tcsetattr(0, TCSANOW, new);
+}
+
+void input_home(int *cursor)				/* Command line input routines */
+{
+ 	while (*cursor > 0) {
+		out1c('\b');
+		--*cursor;
+	}
+	flushout(out1);
+}
+
+
+void input_delete(int cursor)
+{
+	int j = 0;
+
+	memmove(parsenextc + cursor, parsenextc + cursor + 1,
+		BUFSIZ - cursor - 1);
+	for (j = cursor; j < (BUFSIZ - 1); j++) {
+		if (!*(parsenextc + j))
+			break;
+		else
+			out1c(*(parsenextc + j));
+	}
+
+	out1str(" \b");
+
+	while (j-- > cursor)
+		out1c('\b');
+	flushout(out1);
+}
+
+
+void input_end(int *cursor, int len)
+{
+	while (*cursor < len) {
+		out1str("\033[C");
+		++*cursor;
+	}
+	flushout(out1);
+}
+
+
+void
+input_backspace(int *cursor, int *len)
+{
+	int j = 0;
+
+	if (*cursor > 0) {
+		out1str("\b \b");
+		--*cursor;
+		memmove(parsenextc + *cursor, parsenextc + *cursor + 1,
+			BUFSIZ - *cursor + 1);
+
+		for (j = *cursor; j < (BUFSIZ - 1); j++) {
+			if (!*(parsenextc + j))
+				break;
+			else
+				out1c(*(parsenextc + j));
+		}
+
+		out1str(" \b");
+
+		while (j-- > *cursor)
+			out1c('\b');
+
+		--*len;
+		flushout(out1);
+	}
+}
+
+int hetio_read_input(int fd)
+{
+	int nr = 0;
+
+	/* Are we an interactive shell? */
+	if (!hetio_inter || fd) {
+		return -255;
+	} else {
+		int len = 0;
+		int j = 0;
+		int cursor = 0;
+		int break_out = 0;
+		int ret = 0;
+		char c = 0;
+		struct history *hp = his_end;
+
+		if (!reset_term) {
+			setIO(&new_term, &old_term);
+			reset_term = 1;
+		} else {
+			tcsetattr(0, TCSANOW, &new_term);
+		}
+
+		memset(parsenextc, 0, BUFSIZ);
+
+		while (1) {
+			if ((ret = read(fd, &c, 1)) < 1)
+				return ret;
+
+			switch (c) {
+   				case 1:		/* Control-A Beginning of line */
+   					input_home(&cursor);
+					break;
+				case 5:		/* Control-E EOL */
+					input_end(&cursor, len);
+					break;
+				case 4:		/* Control-D */
+					if (!len)
+						exitshell(0);
+					break;
+				case 21: 	/* Control-U */
+					/* Return to begining of line. */
+					for (; cursor > 0; cursor--)
+						out1c('\b');
+					/* Erase old command. */
+					for (j = 0; j < len; j++) {
+						/*
+						 * Clear buffer while we're at
+						 * it.
+						 */
+						parsenextc[j] = 0;
+						out1c(' ');
+					}
+					/* return to begining of line */
+					for (; len > 0; len--)
+						out1c('\b');
+					flushout(out1);
+					break;
+				case '\b':	/* Backspace */
+				case DEL:
+					input_backspace(&cursor, &len);
+					break;
+				case '\n':	/* Enter */
+					*(parsenextc + len++ + 1) = c;
+					out1c(c);
+					flushout(out1);
+					break_out = 1;
+					break;
+				case ESC:	/* escape sequence follows */
+					if ((ret = read(fd, &c, 1)) < 1)
+						return ret;
+
+					if (c == '[' ) {    /* 91 */
+						if ((ret = read(fd, &c, 1)) < 1)
+							return ret;
+
+						switch (c) {
+							case 'A':
+								if (hp && hp->p) {		/* Up */
+									hp = hp->p;
+									goto hop;
+								}
+								break;
+							case 'B':
+								if (hp && hp->n && hp->n->s) {	/* Down */
+									hp = hp->n;
+									goto hop;
+								}
+								break;
+
+hop:						/* hop */
+								len = strlen(parsenextc);
+
+								for (; cursor > 0; cursor--)		/* return to begining of line */
+									out1c('\b');
+
+		   						for (j = 0; j < len; j++)		/* erase old command */
+									out1c(' ');
+
+								for (; j > 0; j--)		/* return to begining of line */
+									out1c('\b');
+
+								strcpy (parsenextc, hp->s);		/* write new command */
+								len = strlen (hp->s);
+								out1str(parsenextc);
+								flushout(out1);
+								cursor = len;
+								break;
+							case 'C':		/* Right */
+								if (cursor < len) {
+									out1str("\033[C");
+									cursor++;
+									flushout(out1);
+						 		}
+								break;
+							case 'D':		/* Left */
+								if (cursor > 0) {
+									out1str("\033[D");
+									cursor--;
+									flushout(out1);
+								}
+								break;
+							case '3':		/* Delete */
+								if (cursor != len) {
+									input_delete(cursor);
+									len--;
+								}
+								break;
+							case '1':		/* Home (Ctrl-A) */
+								input_home(&cursor);
+								break;
+							case '4':		/* End (Ctrl-E) */
+								input_end(&cursor, len);
+								break;
+						}
+						if (c == '1' || c == '3' || c == '4')
+							if ((ret = read(fd, &c, 1)) < 1)
+								return ret;  /* read 126 (~) */
+					}
+
+					if (c == 'O') {	/* 79 */
+						if ((ret = read(fd, &c, 1)) < 1)
+							return ret;
+						switch (c) {
+							case 'H':		/* Home (xterm) */
+      								input_home(&cursor);
+								break;
+							case 'F':		/* End (xterm_ */
+								input_end(&cursor, len);
+								break;
+						}
+					}
+
+					c = 0;
+					break;
+
+				default:				/* If it's regular input, do the normal thing */
+					if (!isprint(c))		/* Skip non-printable characters */
+						break;
+
+	       				if (len >= (BUFSIZ - 2))	/* Need to leave space for enter */
+		  				break;
+
+					len++;
+
+					if (cursor == (len - 1)) {	/* Append if at the end of the line */
+						*(parsenextc + cursor) = c;
+					} else {			/* Insert otherwise */
+						memmove(parsenextc + cursor + 1, parsenextc + cursor,
+							len - cursor - 1);
+
+						*(parsenextc + cursor) = c;
+
+						for (j = cursor; j < len; j++)
+							out1c(*(parsenextc + j));
+						for (; j > cursor; j--)
+							out1str("\033[D");
+					}
+
+					cursor++;
+					out1c(c);
+					flushout(out1);
+					break;
+			}
+
+			if (break_out)		/* Enter is the command terminator, no more input. */
+				break;
+		}
+
+		nr = len + 1;
+		tcsetattr(0, TCSANOW, &old_term);
+
+		if (*(parsenextc)) {		/* Handle command history log */
+			struct history *h = his_end;
+
+			if (!h) {       /* No previous history */
+				h = his_front = malloc(sizeof (struct history));
+				h->n = malloc(sizeof (struct history));
+				h->p = NULL;
+				h->s = strdup(parsenextc);
+
+				h->n->p = h;
+				h->n->n = NULL;
+				h->n->s = NULL;
+				his_end = h->n;
+				history_counter++;
+			} else {	/* Add a new history command */
+
+				h->n = malloc(sizeof (struct history));
+
+				h->n->p = h;
+				h->n->n = NULL;
+				h->n->s = NULL;
+				h->s = strdup(parsenextc);
+				his_end = h->n;
+
+				if (history_counter >= MAX_HISTORY) {	/* After max history, remove the last known command */
+					struct history *p = his_front->n;
+
+					p->p = NULL;
+					free(his_front->s);
+					free(his_front);
+					his_front = p;
+				} else {
+					history_counter++;
+				}
+			}
+		}
+	}
+
+	return nr;
+}
diff --git a/dash/hetio.h b/dash/hetio.h
new file mode 100644
index 0000000..c3e915c
--- /dev/null
+++ b/dash/hetio.h
@@ -0,0 +1,22 @@
+/*
+ * Termios command line History and Editting for NetBSD sh (ash)
+ * Copyright (c) 1999
+ *	Main code:	Adam Rogoyski <rogoyski@cs.utexas.edu> 
+ *	Etc:		Dave Cinege <dcinege@psychosis.com>
+ *
+ * You may use this code as you wish, so long as the original author(s)
+ * are attributed in any redistributions of the source code.
+ * This code is 'as is' with no warranty.
+ * This code may safely be consumed by a BSD or GPL license.
+ *
+ * v 0.5  19990328	Initial release 
+ *
+ * Future plans: Simple file and path name completion. (like BASH)
+ *
+ */
+
+void hetio_init(void);
+int hetio_read_input(int fd);
+void hetio_reset_term(void);
+
+extern int hetio_inter;
diff --git a/ash/histedit.c b/dash/histedit.c
similarity index 83%
rename from ash/histedit.c
rename to dash/histedit.c
index a2aa3a9..36d7937 100644
--- a/ash/histedit.c
+++ b/dash/histedit.c
@@ -1,8 +1,8 @@
-/*	$NetBSD: histedit.c,v 1.34 2003/10/27 06:19:29 lukem Exp $	*/
-
 /*-
  * Copyright (c) 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -32,24 +32,8 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)histedit.c	8.2 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: histedit.c,v 1.34 2003/10/27 06:19:29 lukem Exp $");
-#endif
-#endif /* not lint */
-
 #include <sys/param.h>
-#ifndef __KLIBC__
 #include <paths.h>
-#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -64,15 +48,11 @@
 #include "output.h"
 #include "mystring.h"
 #include "error.h"
-#ifdef KLIBC_SH_HISTORY
+#ifndef SMALL
 #include "myhistedit.h"
 #include "eval.h"
 #include "memalloc.h"
 
-#ifdef __linux__
-#include "compat_linux.h"
-#endif
-
 #define MAXHISTLOOPS	4	/* max recursions through fc */
 #define DEFEDITOR	"ed"	/* default editor *should* be $EDITOR */
 
@@ -116,8 +96,6 @@
 			/*
 			 * turn editing on
 			 */
-			char *term, *shname;
-
 			INTOFF;
 			if (el_in == NULL)
 				el_in = fdopen(0, "r");
@@ -130,20 +108,11 @@
 			if (tracefile)
 				el_err = tracefile;
 #endif
-			term = lookupvar("TERM");
-			if (term)
-				setenv("TERM", term, 1);
-			else
-				unsetenv("TERM");
-			shname = arg0;
-			if (shname[0] == '-')
-				shname++;
-			el = el_init(shname, el_in, el_out, el_err);
+			el = el_init(arg0, el_in, el_out, el_err);
 			if (el != NULL) {
 				if (hist)
 					el_set(el, EL_HIST, history, hist);
 				el_set(el, EL_PROMPT, getprompt);
-				el_set(el, EL_SIGNAL, 1);
 			} else {
 bad:
 				out2str("sh: can't initialize editing\n");
@@ -201,27 +170,6 @@
 		}
 }
 
-int
-inputrc(argc, argv)
-	int argc;
-	char **argv;
-{
-	if (argc != 2) {
-		out2str("usage: inputrc file\n");
-		return 1;
-	}
-	if (el != NULL) {
-		if (el_source(el, argv[1])) {
-			out2str("inputrc: failed\n");
-			return 1;
-		} else
-			return 0;
-	} else {
-		out2str("sh: inputrc ignored, not editing\n");
-		return 1;
-	}
-}
-
 /*
  *  This command is provided since POSIX decided to standardize
  *  the Korn shell fc command.  Oh well...
@@ -259,15 +207,16 @@
 #endif
 
 	if (hist == NULL)
-		error("history not active");
+		sh_error("history not active");
 
 	if (argc == 1)
-		error("missing history argument");
+		sh_error("missing history argument");
 
-#ifndef __linux__
-	optreset = 1; /* initialize getopt */
+#ifdef __GLIBC__
+	optind = 0;
+#else
+	optreset = 1; optind = 1; /* initialize getopt */
 #endif
-	optind = 1; /* initialize getopt */
 	while (not_fcnumber(argv[optind]) &&
 	      (ch = getopt(argc, argv, ":e:lnrs")) != -1)
 		switch ((char)ch) {
@@ -287,11 +236,11 @@
 			sflg = 1;
 			break;
 		case ':':
-			error("option -%c expects argument", optopt);
+			sh_error("option -%c expects argument", optopt);
 			/* NOTREACHED */
 		case '?':
 		default:
-			error("unknown option: -%c", optopt);
+			sh_error("unknown option: -%c", optopt);
 			/* NOTREACHED */
 		}
 	argc -= optind, argv += optind;
@@ -318,15 +267,15 @@
 		if (++active > MAXHISTLOOPS) {
 			active = 0;
 			displayhist = 0;
-			error("called recursively too many times");
+			sh_error("called recursively too many times");
 		}
 		/*
 		 * Set editor.
 		 */
 		if (sflg == 0) {
 			if (editor == NULL &&
-			    (editor = bltinlookup("FCEDIT", 1)) == NULL &&
-			    (editor = bltinlookup("EDITOR", 1)) == NULL)
+			    (editor = bltinlookup("FCEDIT")) == NULL &&
+			    (editor = bltinlookup("EDITOR")) == NULL)
 				editor = DEFEDITOR;
 			if (editor[0] == '-' && editor[1] == '\0') {
 				sflg = 1;	/* no edit */
@@ -361,7 +310,7 @@
 		laststr = argv[1];
 		break;
 	default:
-		error("too many args");
+		sh_error("too many args");
 		/* NOTREACHED */
 	}
 	/*
@@ -388,12 +337,12 @@
 	if (editor) {
 		int fd;
 		INTOFF;		/* easier */
-		snprintf(editfile, sizeof(editfile), "%s_shXXXXXX", _PATH_TMP);
+		sprintf(editfile, "%s_shXXXXXX", _PATH_TMP);
 		if ((fd = mkstemp(editfile)) < 0)
-			error("can't create temporary file %s", editfile);
+			sh_error("can't create temporary file %s", editfile);
 		if ((efp = fdopen(fd, "w")) == NULL) {
 			close(fd);
-			error("can't allocate stdio buffer for temp");
+			sh_error("can't allocate stdio buffer for temp");
 		}
 	}
 
@@ -421,7 +370,8 @@
 					out2str(s);
 				}
 
-				evalstring(strcpy(stalloc(strlen(s) + 1), s), 0);
+				evalstring(strcpy(stalloc(strlen(s) + 1), s),
+					   ~0);
 				if (displayhist && hist) {
 					/*
 					 *  XXX what about recursive and
@@ -445,7 +395,8 @@
 		fclose(efp);
 		editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
 		sprintf(editcmd, "%s %s", editor, editfile);
-		evalstring(editcmd, 0);	/* XXX - should use no JC command */
+		/* XXX - should use no JC command */
+		evalstring(editcmd, ~0);
 		INTON;
 		readcmdfile(editfile);	/* XXX - should read back - quick tst */
 		unlink(editfile);
@@ -526,16 +477,16 @@
 			}
 		}
 		if (retval == -1)
-			error("history number %s not found (internal error)",
-			       str);
+			sh_error("history number %s not found (internal error)",
+				 str);
 	} else {
 		/*
 		 * pattern
 		 */
 		retval = history(hist, &he, H_PREV_STR, str);
 		if (retval == -1)
-			error("history pattern not found: %s", str);
+			sh_error("history pattern not found: %s", str);
 	}
 	return (he.num);
 }
-#endif /* KLIBC_SH_HISTORY */
+#endif
diff --git a/ash/init.h b/dash/init.h
similarity index 94%
rename from ash/init.h
rename to dash/init.h
index 60d924e..e026e86 100644
--- a/ash/init.h
+++ b/dash/init.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: init.h,v 1.10 2003/08/07 09:05:32 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
diff --git a/ash/input.c b/dash/input.c
similarity index 74%
rename from ash/input.c
rename to dash/input.c
index ed440fd..639c023 100644
--- a/ash/input.c
+++ b/dash/input.c
@@ -1,8 +1,8 @@
-/*	$NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -32,23 +32,8 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)input.c	8.3 (Berkeley) 6/9/95";
-#else
-__RCSID("$NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $");
-#endif
-#endif /* not lint */
-
 #include <stdio.h>	/* defines BUFSIZ */
 #include <fcntl.h>
-#include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
@@ -67,19 +52,25 @@
 #include "error.h"
 #include "alias.h"
 #include "parser.h"
-#ifdef KLIBC_SH_HISTORY
+#include "main.h"
+#ifndef SMALL
 #include "myhistedit.h"
 #endif
 
+#ifdef HETIO
+#include "hetio.h"
+#endif
+
 #define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
+#define IBUFSIZ (BUFSIZ + 1)
 
 MKINIT
 struct strpush {
 	struct strpush *prev;	/* preceding string on stack */
 	char *prevstring;
 	int prevnleft;
-	int prevlleft;
 	struct alias *ap;	/* if push was associated with an alias */
+	char *string;		/* remember the string since it may change */
 };
 
 /*
@@ -106,12 +97,11 @@
 MKINIT int parselleft;		/* copy of parsefile->lleft */
 char *parsenextc;		/* copy of parsefile->nextc */
 MKINIT struct parsefile basepf;	/* top level input file */
-MKINIT char basebuf[BUFSIZ];	/* buffer for top level input file */
+MKINIT char basebuf[IBUFSIZ];	/* buffer for top level input file */
 struct parsefile *parsefile = &basepf;	/* current input file */
-int init_editline = 0;		/* editline library initialized? */
 int whichprompt;		/* 1 == PS1, 2 == PS2 */
 
-#ifdef KLIBC_SH_HISTORY
+#ifndef SMALL
 EditLine *el;			/* cookie for editline package */
 #endif
 
@@ -128,12 +118,7 @@
 }
 
 RESET {
-	if (exception != EXSHELLPROC)
-		parselleft = parsenleft = 0;	/* clear input buffer */
-	popallfiles();
-}
-
-SHELLPROC {
+	parselleft = parsenleft = 0;	/* clear input buffer */
 	popallfiles();
 }
 #endif
@@ -151,7 +136,7 @@
 	int c;
 
 	while (--nleft > 0) {
-		c = pgetc_macro();
+		c = pgetc2();
 		if (c == PEOF) {
 			if (p == line)
 				return NULL;
@@ -166,7 +151,6 @@
 }
 
 
-
 /*
  * Read a character from the script, returning PEOF on end of file.
  * Nul characters in the input are silently discarded.
@@ -179,6 +163,21 @@
 }
 
 
+/*
+ * Same as pgetc(), but ignores PEOA.
+ */
+
+int
+pgetc2()
+{
+	int c;
+	do {
+		c = pgetc_macro();
+	} while (c == PEOA);
+	return c;
+}
+
+
 static int
 preadfd(void)
 {
@@ -187,7 +186,7 @@
 	parsenextc = buf;
 
 retry:
-#ifdef KLIBC_SH_HISTORY
+#ifndef SMALL
 	if (parsefile->fd == 0 && el) {
 		static const char *rl_cp;
 		static int el_len;
@@ -198,8 +197,8 @@
 			nr = 0;
 		else {
 			nr = el_len;
-			if (nr > BUFSIZ - 8)
-				nr = BUFSIZ - 8;
+			if (nr > IBUFSIZ - 1)
+				nr = IBUFSIZ - 1;
 			memcpy(buf, rl_cp, nr);
 			if (nr != el_len) {
 				el_len -= nr;
@@ -210,25 +209,27 @@
 
 	} else
 #endif
-		nr = read(parsefile->fd, buf, BUFSIZ - 8);
+
+#ifdef HETIO
+		nr = hetio_read_input(parsefile->fd);
+		if (nr == -255)
+#endif
+		nr = read(parsefile->fd, buf, IBUFSIZ - 1);
 
 
-	if (nr <= 0) {
-                if (nr < 0) {
-                        if (errno == EINTR)
-                                goto retry;
-                        if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
-                                int flags = fcntl(0, F_GETFL, 0);
-                                if (flags >= 0 && flags & O_NONBLOCK) {
-                                        flags &=~ O_NONBLOCK;
-                                        if (fcntl(0, F_SETFL, flags) >= 0) {
-						out2str("sh: turning off NDELAY mode\n");
-                                                goto retry;
-                                        }
-                                }
-                        }
-                }
-                nr = -1;
+	if (nr < 0) {
+		if (errno == EINTR)
+			goto retry;
+		if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
+			int flags = fcntl(0, F_GETFL, 0);
+			if (flags >= 0 && flags & O_NONBLOCK) {
+				flags &=~ O_NONBLOCK;
+				if (fcntl(0, F_SETFL, flags) >= 0) {
+					out2str("sh: turning off NDELAY mode\n");
+					goto retry;
+				}
+			}
+		}
 	}
 	return nr;
 }
@@ -246,68 +247,87 @@
 int
 preadbuffer(void)
 {
-	char *p, *q;
+	char *q;
 	int more;
+#ifndef SMALL
 	int something;
+#endif
 	char savec;
 
-	if (parsefile->strpush) {
+	while (unlikely(parsefile->strpush)) {
+		if (
+			parsenleft == -1 && parsefile->strpush->ap &&
+			parsenextc[-1] != ' ' && parsenextc[-1] != '\t'
+		) {
+			return PEOA;
+		}
 		popstring();
 		if (--parsenleft >= 0)
 			return (*parsenextc++);
 	}
-	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+	if (unlikely(parsenleft == EOF_NLEFT || parsefile->buf == NULL))
 		return PEOF;
 	flushout(&output);
+#ifdef FLUSHERR
 	flushout(&errout);
+#endif
 
+	more = parselleft;
+	if (more <= 0) {
 again:
-	if (parselleft <= 0) {
-		if ((parselleft = preadfd()) == -1) {
+		if ((more = preadfd()) <= 0) {
 			parselleft = parsenleft = EOF_NLEFT;
 			return PEOF;
 		}
 	}
 
-	q = p = parsenextc;
+	q = parsenextc;
 
 	/* delete nul characters */
+#ifndef SMALL
 	something = 0;
-	for (more = 1; more;) {
-		switch (*p) {
-		case '\0':
-			p++;	/* Skip nul */
-			goto check;
+#endif
+	for (;;) {
+		int c;
 
-		case '\t':
-		case ' ':
-			break;
+		more--;
+		c = *q;
 
-		case '\n':
-			parsenleft = q - parsenextc;
-			more = 0; /* Stop processing here */
-			break;
+		if (!c)
+			memmove(q, q + 1, more);
+		else {
+			q++;
 
-		default:
-			something = 1;
-			break;
+			if (c == '\n') {
+				parsenleft = q - parsenextc - 1;
+				break;
+			}
+
+#ifndef SMALL
+			switch (c) {
+			default:
+				something = 1;
+				/* fall through */
+			case '\t':
+			case ' ':
+				break;
+			}
+#endif
 		}
 
-		*q++ = *p++;
-check:
-		if (--parselleft <= 0) {
+		if (more <= 0) {
 			parsenleft = q - parsenextc - 1;
 			if (parsenleft < 0)
 				goto again;
-			*q = '\0';
-			more = 0;
+			break;
 		}
 	}
+	parselleft = more;
 
 	savec = *q;
 	*q = '\0';
 
-#ifdef KLIBC_SH_HISTORY
+#ifndef SMALL
 	if (parsefile->fd == 0 && hist && something) {
 		HistEvent he;
 		INTOFF;
@@ -319,7 +339,9 @@
 
 	if (vflag) {
 		out2str(parsenextc);
+#ifdef FLUSHERR
 		flushout(out2);
+#endif
 	}
 
 	*q = savec;
@@ -344,10 +366,12 @@
  * We handle aliases this way.
  */
 void
-pushstring(char *s, int len, void *ap)
+pushstring(char *s, void *ap)
 {
 	struct strpush *sp;
+	size_t len;
 
+	len = strlen(s);
 	INTOFF;
 /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
 	if (parsefile->strpush) {
@@ -358,10 +382,11 @@
 		sp = parsefile->strpush = &(parsefile->basestrpush);
 	sp->prevstring = parsenextc;
 	sp->prevnleft = parsenleft;
-	sp->prevlleft = parselleft;
 	sp->ap = (struct alias *)ap;
-	if (ap)
+	if (ap) {
 		((struct alias *)ap)->flag |= ALIASINUSE;
+		sp->string = s;
+	}
 	parsenextc = s;
 	parsenleft = len;
 	INTON;
@@ -373,12 +398,21 @@
 	struct strpush *sp = parsefile->strpush;
 
 	INTOFF;
+	if (sp->ap) {
+		if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
+			checkkwd |= CHKALIAS;
+		}
+		if (sp->string != sp->ap->val) {
+			ckfree(sp->string);
+		}
+		sp->ap->flag &= ~ALIASINUSE;
+		if (sp->ap->flag & ALIASDEAD) {
+			unalias(sp->ap->name);
+		}
+	}
 	parsenextc = sp->prevstring;
 	parsenleft = sp->prevnleft;
-	parselleft = sp->prevlleft;
 /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
-	if (sp->ap)
-		sp->ap->flag &= ~ALIASINUSE;
 	parsefile->strpush = sp->prev;
 	if (sp != &(parsefile->basestrpush))
 		ckfree(sp);
@@ -390,24 +424,29 @@
  * old input onto the stack first.
  */
 
-void
-setinputfile(const char *fname, int push)
+int
+setinputfile(const char *fname, int flags)
 {
 	int fd;
 	int fd2;
 
 	INTOFF;
-	if ((fd = open(fname, O_RDONLY)) < 0)
-		error("Can't open %s", fname);
+	if ((fd = open(fname, O_RDONLY)) < 0) {
+		if (flags & INPUT_NOFILE_OK)
+			goto out;
+		sh_error("Can't open %s", fname);
+	}
 	if (fd < 10) {
 		fd2 = copyfd(fd, 10);
 		close(fd);
 		if (fd2 < 0)
-			error("Out of file descriptors");
+			sh_error("Out of file descriptors");
 		fd = fd2;
 	}
-	setinputfd(fd, push);
+	setinputfd(fd, flags & INPUT_PUSH_FILE);
+out:
 	INTON;
+	return fd;
 }
 
 
@@ -422,13 +461,11 @@
 	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
 	if (push) {
 		pushfile();
-		parsefile->buf = ckmalloc(BUFSIZ);
+		parsefile->buf = 0;
 	}
-	if (parsefile->fd > 0)
-		close(parsefile->fd);
 	parsefile->fd = fd;
 	if (parsefile->buf == NULL)
-		parsefile->buf = ckmalloc(BUFSIZ);
+		parsefile->buf = ckmalloc(IBUFSIZ);
 	parselleft = parsenleft = 0;
 	plinno = 1;
 }
@@ -439,13 +476,12 @@
  */
 
 void
-setinputstring(char *string, int push)
+setinputstring(char *string)
 {
 	INTOFF;
-	if (push)
-		pushfile();
+	pushfile();
 	parsenextc = string;
-	parselleft = parsenleft = strlen(string);
+	parsenleft = strlen(string);
 	parsefile->buf = NULL;
 	plinno = 1;
 	INTON;
@@ -514,22 +550,11 @@
 /*
  * Close the file(s) that the shell is reading commands from.  Called
  * after a fork is done.
- *
- * Takes one arg, vfork, which tells it to not modify its global vars
- * as it is still running in the parent.
- *
- * This code is (probably) unnecessary as the 'close on exec' flag is
- * set and should be enough.  In the vfork case it is definitely wrong
- * to close the fds as another fork() may be done later to feed data
- * from a 'here' document into a pipe and we don't want to close the
- * pipe!
  */
 
 void
-closescript(int vforked)
+closescript(void)
 {
-	if (vforked)
-		return;
 	popallfiles();
 	if (parsefile->fd > 0) {
 		close(parsefile->fd);
diff --git a/ash/input.h b/dash/input.h
similarity index 88%
rename from ash/input.h
rename to dash/input.h
index a9d3a12..7675653 100644
--- a/ash/input.h
+++ b/dash/input.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: input.h,v 1.15 2003/08/07 09:05:33 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -36,6 +36,11 @@
 
 /* PEOF (the end of file marker) is defined in syntax.h */
 
+enum {
+	INPUT_PUSH_FILE = 1,
+	INPUT_NOFILE_OK = 2,
+};
+
 /*
  * The input line number.  Input.c just defines this variable, and saves
  * and restores it when files are pushed and popped.  The user of this
@@ -44,19 +49,19 @@
 extern int plinno;
 extern int parsenleft;		/* number of characters left in input buffer */
 extern char *parsenextc;	/* next character in input buffer */
-extern int init_editline;	/* 0 == not setup, 1 == OK, -1 == failed */
 
 char *pfgets(char *, int);
 int pgetc(void);
+int pgetc2(void);
 int preadbuffer(void);
 void pungetc(void);
-void pushstring(char *, int, void *);
+void pushstring(char *, void *);
 void popstring(void);
-void setinputfile(const char *, int);
+int setinputfile(const char *, int);
 void setinputfd(int, int);
-void setinputstring(char *, int);
+void setinputstring(char *);
 void popfile(void);
 void popallfiles(void);
-void closescript(int);
+void closescript(void);
 
 #define pgetc_macro()	(--parsenleft >= 0? *parsenextc++ : preadbuffer())
diff --git a/dash/jobs.c b/dash/jobs.c
new file mode 100644
index 0000000..0113354
--- /dev/null
+++ b/dash/jobs.c
@@ -0,0 +1,1499 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <paths.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef BSD
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+#include <sys/ioctl.h>
+
+#include "shell.h"
+#if JOBS
+#include <termios.h>
+#undef CEOF			/* syntax.h redefines this */
+#endif
+#include "redir.h"
+#include "show.h"
+#include "main.h"
+#include "parser.h"
+#include "nodes.h"
+#include "jobs.h"
+#include "options.h"
+#include "trap.h"
+#include "syntax.h"
+#include "input.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "mystring.h"
+#include "system.h"
+
+/* mode flags for set_curjob */
+#define CUR_DELETE 2
+#define CUR_RUNNING 1
+#define CUR_STOPPED 0
+
+/* mode flags for dowait */
+#define DOWAIT_NORMAL 0
+#define DOWAIT_BLOCK 1
+
+/* array of jobs */
+static struct job *jobtab;
+/* size of array */
+static unsigned njobs;
+/* pid of last background process */
+pid_t backgndpid;
+
+#if JOBS
+/* pgrp of shell on invocation */
+static int initialpgrp;
+/* control terminal */
+static int ttyfd = -1;
+#endif
+
+/* current job */
+static struct job *curjob;
+/* number of presumed living untracked jobs */
+static int jobless;
+
+STATIC void set_curjob(struct job *, unsigned);
+STATIC int jobno(const struct job *);
+STATIC int sprint_status(char *, int, int);
+STATIC void freejob(struct job *);
+STATIC struct job *getjob(const char *, int);
+STATIC struct job *growjobtab(void);
+STATIC void forkchild(struct job *, union node *, int);
+STATIC void forkparent(struct job *, union node *, int, pid_t);
+STATIC int dowait(int, struct job *);
+#ifdef SYSV
+STATIC int onsigchild(void);
+#endif
+STATIC int waitproc(int, int *);
+STATIC char *commandtext(union node *);
+STATIC void cmdtxt(union node *);
+STATIC void cmdlist(union node *, int);
+STATIC void cmdputs(const char *);
+STATIC void showpipe(struct job *, struct output *);
+STATIC int getstatus(struct job *);
+
+#if JOBS
+static int restartjob(struct job *, int);
+static void xtcsetpgrp(int, pid_t);
+#endif
+
+STATIC void
+set_curjob(struct job *jp, unsigned mode)
+{
+	struct job *jp1;
+	struct job **jpp, **curp;
+
+	/* first remove from list */
+	jpp = curp = &curjob;
+	do {
+		jp1 = *jpp;
+		if (jp1 == jp)
+			break;
+		jpp = &jp1->prev_job;
+	} while (1);
+	*jpp = jp1->prev_job;
+
+	/* Then re-insert in correct position */
+	jpp = curp;
+	switch (mode) {
+	default:
+#ifdef DEBUG
+		abort();
+#endif
+	case CUR_DELETE:
+		/* job being deleted */
+		break;
+	case CUR_RUNNING:
+		/* newly created job or backgrounded job,
+		   put after all stopped jobs. */
+		do {
+			jp1 = *jpp;
+			if (!JOBS || !jp1 || jp1->state != JOBSTOPPED)
+				break;
+			jpp = &jp1->prev_job;
+		} while (1);
+		/* FALLTHROUGH */
+#if JOBS
+	case CUR_STOPPED:
+#endif
+		/* newly stopped job - becomes curjob */
+		jp->prev_job = *jpp;
+		*jpp = jp;
+		break;
+	}
+}
+
+#if JOBS
+/*
+ * Turn job control on and off.
+ *
+ * Note:  This code assumes that the third arg to ioctl is a character
+ * pointer, which is true on Berkeley systems but not System V.  Since
+ * System V doesn't have job control yet, this isn't a problem now.
+ *
+ * Called with interrupts off.
+ */
+
+int jobctl;
+
+void
+setjobctl(int on)
+{
+	int fd;
+	int pgrp;
+
+	if (on == jobctl || rootshell == 0)
+		return;
+	if (on) {
+		int ofd;
+		ofd = fd = open(_PATH_TTY, O_RDWR);
+		if (fd < 0) {
+			fd += 3;
+			while (!isatty(fd) && --fd >= 0)
+				;
+		}
+		fd = fcntl(fd, F_DUPFD, 10);
+		close(ofd);
+		if (fd < 0)
+			goto out;
+		fcntl(fd, F_SETFD, FD_CLOEXEC);
+		do { /* while we are in the background */
+			if ((pgrp = tcgetpgrp(fd)) < 0) {
+out:
+				sh_warnx("can't access tty; job control turned off");
+				mflag = on = 0;
+				goto close;
+			}
+			if (pgrp == getpgrp())
+				break;
+			killpg(0, SIGTTIN);
+		} while (1);
+		initialpgrp = pgrp;
+
+		setsignal(SIGTSTP);
+		setsignal(SIGTTOU);
+		setsignal(SIGTTIN);
+		pgrp = rootpid;
+		setpgid(0, pgrp);
+		xtcsetpgrp(fd, pgrp);
+	} else {
+		/* turning job control off */
+		fd = ttyfd;
+		pgrp = initialpgrp;
+		xtcsetpgrp(fd, pgrp);
+		setpgid(0, pgrp);
+		setsignal(SIGTSTP);
+		setsignal(SIGTTOU);
+		setsignal(SIGTTIN);
+close:
+		close(fd);
+		fd = -1;
+	}
+	ttyfd = fd;
+	jobctl = on;
+}
+#endif
+
+
+int
+killcmd(argc, argv)
+	int argc;
+	char **argv;
+{
+	extern char *signal_names[];
+	int signo = -1;
+	int list = 0;
+	int i;
+	pid_t pid;
+	struct job *jp;
+
+	if (argc <= 1) {
+usage:
+		sh_error(
+"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
+"kill -l [exitstatus]"
+		);
+	}
+
+	if (**++argv == '-') {
+		signo = decode_signal(*argv + 1, 1);
+		if (signo < 0) {
+			int c;
+
+			while ((c = nextopt("ls:")) != '\0')
+				switch (c) {
+				default:
+#ifdef DEBUG
+					abort();
+#endif
+				case 'l':
+					list = 1;
+					break;
+				case 's':
+					signo = decode_signal(optionarg, 1);
+					if (signo < 0) {
+						sh_error(
+							"invalid signal number or name: %s",
+							optionarg
+						);
+					}
+		                        break;
+				}
+			argv = argptr;
+		} else
+			argv++;
+	}
+
+	if (!list && signo < 0)
+		signo = SIGTERM;
+
+	if ((signo < 0 || !*argv) ^ list) {
+		goto usage;
+	}
+
+	if (list) {
+		struct output *out;
+
+		out = out1;
+		if (!*argv) {
+			outstr("0\n", out);
+			for (i = 1; i < NSIG; i++) {
+				outfmt(out, snlfmt, signal_names[i]);
+			}
+			return 0;
+		}
+		signo = number(*argv);
+		if (signo > 128)
+			signo -= 128;
+		if (0 < signo && signo < NSIG)
+			outfmt(out, snlfmt, signal_names[signo]);
+		else
+			sh_error("invalid signal number or exit status: %s",
+				 *argv);
+		return 0;
+	}
+
+	i = 0;
+	do {
+		if (**argv == '%') {
+			jp = getjob(*argv, 0);
+			pid = -jp->ps[0].pid;
+		} else
+			pid = **argv == '-' ?
+				-number(*argv + 1) : number(*argv);
+		if (kill(pid, signo) != 0) {
+			sh_warnx("%s\n", strerror(errno));
+			i = 1;
+		}
+	} while (*++argv);
+
+	return i;
+}
+
+STATIC int
+jobno(const struct job *jp)
+{
+	return jp - jobtab + 1;
+}
+
+#if JOBS
+int
+fgcmd(int argc, char **argv)
+{
+	struct job *jp;
+	struct output *out;
+	int mode;
+	int retval;
+
+	mode = (**argv == 'f') ? FORK_FG : FORK_BG;
+	nextopt(nullstr);
+	argv = argptr;
+	out = out1;
+	do {
+		jp = getjob(*argv, 1);
+		if (mode == FORK_BG) {
+			set_curjob(jp, CUR_RUNNING);
+			outfmt(out, "[%d] ", jobno(jp));
+		}
+		outstr(jp->ps->cmd, out);
+		showpipe(jp, out);
+		retval = restartjob(jp, mode);
+	} while (*argv && *++argv);
+	return retval;
+}
+
+int bgcmd(int, char **) __attribute__((__alias__("fgcmd")));
+
+
+STATIC int
+restartjob(struct job *jp, int mode)
+{
+	struct procstat *ps;
+	int i;
+	int status;
+	pid_t pgid;
+
+	INTOFF;
+	if (jp->state == JOBDONE)
+		goto out;
+	jp->state = JOBRUNNING;
+	pgid = jp->ps->pid;
+	if (mode == FORK_FG)
+		xtcsetpgrp(ttyfd, pgid);
+	killpg(pgid, SIGCONT);
+	ps = jp->ps;
+	i = jp->nprocs;
+	do {
+		if (WIFSTOPPED(ps->status)) {
+			ps->status = -1;
+		}
+	} while (ps++, --i);
+out:
+	status = (mode == FORK_FG) ? waitforjob(jp) : 0;
+	INTON;
+	return status;
+}
+#endif
+
+STATIC int
+sprint_status(char *s, int status, int sigonly)
+{
+	int col;
+	int st;
+
+	col = 0;
+	st = WEXITSTATUS(status);
+	if (!WIFEXITED(status)) {
+#if JOBS
+		st = WSTOPSIG(status);
+		if (!WIFSTOPPED(status))
+#endif
+			st = WTERMSIG(status);
+		if (sigonly) {
+			if (st == SIGINT || st == SIGPIPE)
+				goto out;
+#if JOBS
+			if (WIFSTOPPED(status))
+				goto out;
+#endif
+		}
+		col = fmtstr(s, 32, strsignal(st));
+		if (WCOREDUMP(status)) {
+			col += fmtstr(s + col, 16, " (core dumped)");
+		}
+	} else if (!sigonly) {
+		if (st)
+			col = fmtstr(s, 16, "Done(%d)", st);
+		else
+			col = fmtstr(s, 16, "Done");
+	}
+
+out:
+	return col;
+}
+
+static void
+showjob(struct output *out, struct job *jp, int mode)
+{
+	struct procstat *ps;
+	struct procstat *psend;
+	int col;
+	int indent;
+	char s[80];
+
+	ps = jp->ps;
+
+	if (mode & SHOW_PGID) {
+		/* just output process (group) id of pipeline */
+		outfmt(out, "%d\n", ps->pid);
+		return;
+	}
+
+	col = fmtstr(s, 16, "[%d]   ", jobno(jp));
+	indent = col;
+
+	if (jp == curjob)
+		s[col - 2] = '+';
+	else if (curjob && jp == curjob->prev_job)
+		s[col - 2] = '-';
+
+	if (mode & SHOW_PID)
+		col += fmtstr(s + col, 16, "%d ", ps->pid);
+
+	psend = ps + jp->nprocs;
+
+	if (jp->state == JOBRUNNING) {
+		scopy("Running", s + col);
+		col += strlen("Running");
+	} else {
+		int status = psend[-1].status;
+#if JOBS
+		if (jp->state == JOBSTOPPED)
+			status = jp->stopstatus;
+#endif
+		col += sprint_status(s + col, status, 0);
+	}
+
+	goto start;
+
+	do {
+		/* for each process */
+		col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
+
+start:
+		outfmt(
+			out, "%s%*c%s",
+			s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
+		);
+		if (!(mode & SHOW_PID)) {
+			showpipe(jp, out);
+			break;
+		}
+		if (++ps == psend) {
+			outcslow('\n', out);
+			break;
+		}
+	} while (1);
+
+	jp->changed = 0;
+
+	if (jp->state == JOBDONE) {
+		TRACE(("showjob: freeing job %d\n", jobno(jp)));
+		freejob(jp);
+	}
+}
+
+
+int
+jobscmd(int argc, char **argv)
+{
+	int mode, m;
+	struct output *out;
+
+	mode = 0;
+	while ((m = nextopt("lp")))
+		if (m == 'l')
+			mode = SHOW_PID;
+		else
+			mode = SHOW_PGID;
+
+	out = out1;
+	argv = argptr;
+	if (*argv)
+		do
+			showjob(out, getjob(*argv,0), mode);
+		while (*++argv);
+	else
+		showjobs(out, mode);
+
+	return 0;
+}
+
+
+/*
+ * Print a list of jobs.  If "change" is nonzero, only print jobs whose
+ * statuses have changed since the last call to showjobs.
+ */
+
+void
+showjobs(struct output *out, int mode)
+{
+	struct job *jp;
+
+	TRACE(("showjobs(%x) called\n", mode));
+
+	/* If not even one one job changed, there is nothing to do */
+	while (dowait(DOWAIT_NORMAL, NULL) > 0)
+		continue;
+
+	for (jp = curjob; jp; jp = jp->prev_job) {
+		if (!(mode & SHOW_CHANGED) || jp->changed)
+			showjob(out, jp, mode);
+	}
+}
+
+/*
+ * Mark a job structure as unused.
+ */
+
+STATIC void
+freejob(struct job *jp)
+{
+	struct procstat *ps;
+	int i;
+
+	INTOFF;
+	for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
+		if (ps->cmd != nullstr)
+			ckfree(ps->cmd);
+	}
+	if (jp->ps != &jp->ps0)
+		ckfree(jp->ps);
+	jp->used = 0;
+	set_curjob(jp, CUR_DELETE);
+	INTON;
+}
+
+
+
+int
+waitcmd(int argc, char **argv)
+{
+	struct job *job;
+	int retval;
+	struct job *jp;
+
+	EXSIGON();
+
+	nextopt(nullstr);
+	retval = 0;
+
+	argv = argptr;
+	if (!*argv) {
+		/* wait for all jobs */
+		for (;;) {
+			jp = curjob;
+			while (1) {
+				if (!jp) {
+					/* no running procs */
+					goto out;
+				}
+				if (jp->state == JOBRUNNING)
+					break;
+				jp->waited = 1;
+				jp = jp->prev_job;
+			}
+			dowait(DOWAIT_BLOCK, 0);
+		}
+	}
+
+	retval = 127;
+	do {
+		if (**argv != '%') {
+			pid_t pid = number(*argv);
+			job = curjob;
+			goto start;
+			do {
+				if (job->ps[job->nprocs - 1].pid == pid)
+					break;
+				job = job->prev_job;
+start:
+				if (!job)
+					goto repeat;
+			} while (1);
+		} else
+			job = getjob(*argv, 0);
+		/* loop until process terminated or stopped */
+		while (job->state == JOBRUNNING)
+			dowait(DOWAIT_BLOCK, 0);
+		job->waited = 1;
+		retval = getstatus(job);
+repeat:
+		;
+	} while (*++argv);
+
+out:
+	return retval;
+}
+
+
+
+/*
+ * Convert a job name to a job structure.
+ */
+
+STATIC struct job *
+getjob(const char *name, int getctl)
+{
+	struct job *jp;
+	struct job *found;
+	const char *err_msg = "No such job: %s";
+	unsigned num;
+	int c;
+	const char *p;
+	char *(*match)(const char *, const char *);
+
+	jp = curjob;
+	p = name;
+	if (!p)
+		goto currentjob;
+
+	if (*p != '%')
+		goto err;
+
+	c = *++p;
+	if (!c)
+		goto currentjob;
+
+	if (!p[1]) {
+		if (c == '+' || c == '%') {
+currentjob:
+			err_msg = "No current job";
+			goto check;
+		} else if (c == '-') {
+			if (jp)
+				jp = jp->prev_job;
+			err_msg = "No previous job";
+check:
+			if (!jp)
+				goto err;
+			goto gotit;
+		}
+	}
+
+	if (is_number(p)) {
+		num = atoi(p);
+		if (num < njobs) {
+			jp = jobtab + num - 1;
+			if (jp->used)
+				goto gotit;
+			goto err;
+		}
+	}
+
+	match = prefix;
+	if (*p == '?') {
+		match = strstr;
+		p++;
+	}
+
+	found = 0;
+	while (1) {
+		if (!jp)
+			goto err;
+		if (match(jp->ps[0].cmd, p)) {
+			if (found)
+				goto err;
+			found = jp;
+			err_msg = "%s: ambiguous";
+		}
+		jp = jp->prev_job;
+	}
+
+gotit:
+#if JOBS
+	err_msg = "job %s not created under job control";
+	if (getctl && jp->jobctl == 0)
+		goto err;
+#endif
+	return jp;
+err:
+	sh_error(err_msg, name);
+}
+
+
+
+/*
+ * Return a new job structure.
+ * Called with interrupts off.
+ */
+
+struct job *
+makejob(union node *node, int nprocs)
+{
+	int i;
+	struct job *jp;
+
+	for (i = njobs, jp = jobtab ; ; jp++) {
+		if (--i < 0) {
+			jp = growjobtab();
+			break;
+		}
+		if (jp->used == 0)
+			break;
+		if (jp->state != JOBDONE || !jp->waited)
+			continue;
+		if (jobctl)
+			continue;
+		freejob(jp);
+		break;
+	}
+	memset(jp, 0, sizeof(*jp));
+#if JOBS
+	if (jobctl)
+		jp->jobctl = 1;
+#endif
+	jp->prev_job = curjob;
+	curjob = jp;
+	jp->used = 1;
+	jp->ps = &jp->ps0;
+	if (nprocs > 1) {
+		jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
+	}
+	TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
+	    jobno(jp)));
+	return jp;
+}
+
+STATIC struct job *
+growjobtab(void)
+{
+	size_t len;
+	ptrdiff_t offset;
+	struct job *jp, *jq;
+
+	len = njobs * sizeof(*jp);
+	jq = jobtab;
+	jp = ckrealloc(jq, len + 4 * sizeof(*jp));
+
+	offset = (char *)jp - (char *)jq;
+	if (offset) {
+		/* Relocate pointers */
+		size_t l = len;
+
+		jq = (struct job *)((char *)jq + l);
+		while (l) {
+			l -= sizeof(*jp);
+			jq--;
+#define joff(p) ((struct job *)((char *)(p) + l))
+#define jmove(p) (p) = (void *)((char *)(p) + offset)
+			if (likely(joff(jp)->ps == &jq->ps0))
+				jmove(joff(jp)->ps);
+			if (joff(jp)->prev_job)
+				jmove(joff(jp)->prev_job);
+		}
+		if (curjob)
+			jmove(curjob);
+#undef joff
+#undef jmove
+	}
+
+	njobs += 4;
+	jobtab = jp;
+	jp = (struct job *)((char *)jp + len);
+	jq = jp + 3;
+	do {
+		jq->used = 0;
+	} while (--jq >= jp);
+	return jp;
+}
+
+
+/*
+ * Fork off a subshell.  If we are doing job control, give the subshell its
+ * own process group.  Jp is a job structure that the job is to be added to.
+ * N is the command that will be evaluated by the child.  Both jp and n may
+ * be NULL.  The mode parameter can be one of the following:
+ *	FORK_FG - Fork off a foreground process.
+ *	FORK_BG - Fork off a background process.
+ *	FORK_NOJOB - Like FORK_FG, but don't give the process its own
+ *		     process group even if job control is on.
+ *
+ * When job control is turned off, background processes have their standard
+ * input redirected to /dev/null (except for the second and later processes
+ * in a pipeline).
+ *
+ * Called with interrupts off.
+ */
+
+STATIC inline void
+forkchild(struct job *jp, union node *n, int mode)
+{
+	int oldlvl;
+
+	TRACE(("Child shell %d\n", getpid()));
+	oldlvl = shlvl;
+	shlvl++;
+
+	closescript();
+	clear_traps();
+#if JOBS
+	/* do job control only in root shell */
+	jobctl = 0;
+	if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
+		pid_t pgrp;
+
+		if (jp->nprocs == 0)
+			pgrp = getpid();
+		else
+			pgrp = jp->ps[0].pid;
+		/* This can fail because we are doing it in the parent also */
+		(void)setpgid(0, pgrp);
+		if (mode == FORK_FG)
+			xtcsetpgrp(ttyfd, pgrp);
+		setsignal(SIGTSTP);
+		setsignal(SIGTTOU);
+	} else
+#endif
+	if (mode == FORK_BG) {
+		ignoresig(SIGINT);
+		ignoresig(SIGQUIT);
+		if (jp->nprocs == 0) {
+			close(0);
+			if (open(_PATH_DEVNULL, O_RDONLY) != 0)
+				sh_error("Can't open %s", _PATH_DEVNULL);
+		}
+	}
+	if (!oldlvl && iflag) {
+		setsignal(SIGINT);
+		setsignal(SIGQUIT);
+		setsignal(SIGTERM);
+	}
+	for (jp = curjob; jp; jp = jp->prev_job)
+		freejob(jp);
+	jobless = 0;
+}
+
+STATIC inline void
+forkparent(struct job *jp, union node *n, int mode, pid_t pid)
+{
+	TRACE(("In parent shell:  child = %d\n", pid));
+	if (!jp) {
+		while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
+		jobless++;
+		return;
+	}
+#if JOBS
+	if (mode != FORK_NOJOB && jp->jobctl) {
+		int pgrp;
+
+		if (jp->nprocs == 0)
+			pgrp = pid;
+		else
+			pgrp = jp->ps[0].pid;
+		/* This can fail because we are doing it in the child also */
+		(void)setpgid(pid, pgrp);
+	}
+#endif
+	if (mode == FORK_BG) {
+		backgndpid = pid;		/* set $! */
+		set_curjob(jp, CUR_RUNNING);
+	}
+	if (jp) {
+		struct procstat *ps = &jp->ps[jp->nprocs++];
+		ps->pid = pid;
+		ps->status = -1;
+		ps->cmd = nullstr;
+		if (jobctl && n)
+			ps->cmd = commandtext(n);
+	}
+}
+
+int
+forkshell(struct job *jp, union node *n, int mode)
+{
+	int pid;
+
+	TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
+	pid = fork();
+	if (pid < 0) {
+		TRACE(("Fork failed, errno=%d", errno));
+		if (jp)
+			freejob(jp);
+		sh_error("Cannot fork");
+	}
+	if (pid == 0)
+		forkchild(jp, n, mode);
+	else
+		forkparent(jp, n, mode, pid);
+	return pid;
+}
+
+/*
+ * Wait for job to finish.
+ *
+ * Under job control we have the problem that while a child process is
+ * running interrupts generated by the user are sent to the child but not
+ * to the shell.  This means that an infinite loop started by an inter-
+ * active user may be hard to kill.  With job control turned off, an
+ * interactive user may place an interactive program inside a loop.  If
+ * the interactive program catches interrupts, the user doesn't want
+ * these interrupts to also abort the loop.  The approach we take here
+ * is to have the shell ignore interrupt signals while waiting for a
+ * forground process to terminate, and then send itself an interrupt
+ * signal if the child process was terminated by an interrupt signal.
+ * Unfortunately, some programs want to do a bit of cleanup and then
+ * exit on interrupt; unless these processes terminate themselves by
+ * sending a signal to themselves (instead of calling exit) they will
+ * confuse this approach.
+ *
+ * Called with interrupts off.
+ */
+
+int
+waitforjob(struct job *jp)
+{
+	int st;
+
+	TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
+	while (jp->state == JOBRUNNING) {
+		dowait(DOWAIT_BLOCK, jp);
+	}
+	st = getstatus(jp);
+#if JOBS
+	if (jp->jobctl) {
+		xtcsetpgrp(ttyfd, rootpid);
+		/*
+		 * This is truly gross.
+		 * If we're doing job control, then we did a TIOCSPGRP which
+		 * caused us (the shell) to no longer be in the controlling
+		 * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
+		 * intuit from the subprocess exit status whether a SIGINT
+		 * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
+		 */
+		if (jp->sigint)
+			raise(SIGINT);
+	}
+#endif
+	if (! JOBS || jp->state == JOBDONE)
+		freejob(jp);
+	return st;
+}
+
+
+
+/*
+ * Wait for a process to terminate.
+ */
+
+STATIC int
+dowait(int block, struct job *job)
+{
+	int pid;
+	int status;
+	struct job *jp;
+	struct job *thisjob;
+	int state;
+
+	TRACE(("dowait(%d) called\n", block));
+	pid = waitproc(block, &status);
+	TRACE(("wait returns pid %d, status=%d\n", pid, status));
+	if (pid <= 0)
+		return pid;
+	INTOFF;
+	thisjob = NULL;
+	for (jp = curjob; jp; jp = jp->prev_job) {
+		struct procstat *sp;
+		struct procstat *spend;
+		if (jp->state == JOBDONE)
+			continue;
+		state = JOBDONE;
+		spend = jp->ps + jp->nprocs;
+		sp = jp->ps;
+		do {
+			if (sp->pid == pid) {
+				TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->status, status));
+				sp->status = status;
+				thisjob = jp;
+			}
+			if (sp->status == -1)
+				state = JOBRUNNING;
+#if JOBS
+			if (state == JOBRUNNING)
+				continue;
+			if (WIFSTOPPED(sp->status)) {
+				jp->stopstatus = sp->status;
+				state = JOBSTOPPED;
+			}
+#endif
+		} while (++sp < spend);
+		if (thisjob)
+			goto gotjob;
+	}
+	if (!JOBS || !WIFSTOPPED(status))
+		jobless--;
+	goto out;
+
+gotjob:
+	if (state != JOBRUNNING) {
+		thisjob->changed = 1;
+
+		if (thisjob->state != state) {
+			TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state));
+			thisjob->state = state;
+#if JOBS
+			if (state == JOBSTOPPED) {
+				set_curjob(thisjob, CUR_STOPPED);
+			}
+#endif
+		}
+	}
+
+out:
+	INTON;
+
+	if (thisjob && thisjob == job) {
+		char s[48 + 1];
+		int len;
+
+		len = sprint_status(s, status, 1);
+		if (len) {
+			s[len] = '\n';
+			s[len + 1] = 0;
+			outstr(s, out2);
+		}
+	}
+	return pid;
+}
+
+
+
+/*
+ * Do a wait system call.  If job control is compiled in, we accept
+ * stopped processes.  If block is zero, we return a value of zero
+ * rather than blocking.
+ *
+ * System V doesn't have a non-blocking wait system call.  It does
+ * have a SIGCLD signal that is sent to a process when one of it's
+ * children dies.  The obvious way to use SIGCLD would be to install
+ * a handler for SIGCLD which simply bumped a counter when a SIGCLD
+ * was received, and have waitproc bump another counter when it got
+ * the status of a process.  Waitproc would then know that a wait
+ * system call would not block if the two counters were different.
+ * This approach doesn't work because if a process has children that
+ * have not been waited for, System V will send it a SIGCLD when it
+ * installs a signal handler for SIGCLD.  What this means is that when
+ * a child exits, the shell will be sent SIGCLD signals continuously
+ * until is runs out of stack space, unless it does a wait call before
+ * restoring the signal handler.  The code below takes advantage of
+ * this (mis)feature by installing a signal handler for SIGCLD and
+ * then checking to see whether it was called.  If there are any
+ * children to be waited for, it will be.
+ *
+ * If neither SYSV nor BSD is defined, we don't implement nonblocking
+ * waits at all.  In this case, the user will not be informed when
+ * a background process until the next time she runs a real program
+ * (as opposed to running a builtin command or just typing return),
+ * and the jobs command may give out of date information.
+ */
+
+#ifdef SYSV
+STATIC int gotsigchild;
+
+STATIC int onsigchild() {
+	gotsigchild = 1;
+}
+#endif
+
+
+STATIC int
+waitproc(int block, int *status)
+{
+#ifdef BSD
+	int flags = 0;
+
+#if JOBS
+	if (jobctl)
+		flags |= WUNTRACED;
+#endif
+	if (block == 0)
+		flags |= WNOHANG;
+	return wait3(status, flags, (struct rusage *)NULL);
+#else
+#ifdef SYSV
+	int (*save)();
+
+	if (block == 0) {
+		gotsigchild = 0;
+		save = signal(SIGCLD, onsigchild);
+		signal(SIGCLD, save);
+		if (gotsigchild == 0)
+			return 0;
+	}
+	return wait(status);
+#else
+	if (block == 0)
+		return 0;
+	return wait(status);
+#endif
+#endif
+}
+
+/*
+ * return 1 if there are stopped jobs, otherwise 0
+ */
+int job_warning;
+int
+stoppedjobs(void)
+{
+	struct job *jp;
+	int retval;
+
+	retval = 0;
+	if (job_warning)
+		goto out;
+	jp = curjob;
+	if (jp && jp->state == JOBSTOPPED) {
+		out2str("You have stopped jobs.\n");
+		job_warning = 2;
+		retval++;
+	}
+
+out:
+	return retval;
+}
+
+/*
+ * Return a string identifying a command (to be printed by the
+ * jobs command).
+ */
+
+STATIC char *cmdnextc;
+
+STATIC char *
+commandtext(union node *n)
+{
+	char *name;
+
+	STARTSTACKSTR(cmdnextc);
+	cmdtxt(n);
+	name = stackblock();
+	TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
+		name, cmdnextc, ps->cmd));
+	return savestr(name);
+}
+
+
+STATIC void
+cmdtxt(union node *n)
+{
+	union node *np;
+	struct nodelist *lp;
+	const char *p;
+	char s[2];
+
+	if (!n)
+		return;
+	switch (n->type) {
+	default:
+#if DEBUG
+		abort();
+#endif
+	case NPIPE:
+		lp = n->npipe.cmdlist;
+		for (;;) {
+			cmdtxt(lp->n);
+			lp = lp->next;
+			if (!lp)
+				break;
+			cmdputs(" | ");
+		}
+		break;
+	case NSEMI:
+		p = "; ";
+		goto binop;
+	case NAND:
+		p = " && ";
+		goto binop;
+	case NOR:
+		p = " || ";
+binop:
+		cmdtxt(n->nbinary.ch1);
+		cmdputs(p);
+		n = n->nbinary.ch2;
+		goto donode;
+	case NREDIR:
+	case NBACKGND:
+		n = n->nredir.n;
+		goto donode;
+	case NNOT:
+		cmdputs("!");
+		n = n->nnot.com;
+donode:
+		cmdtxt(n);
+		break;
+	case NIF:
+		cmdputs("if ");
+		cmdtxt(n->nif.test);
+		cmdputs("; then ");
+		n = n->nif.ifpart;
+		if (n->nif.elsepart) {
+			cmdtxt(n);
+			cmdputs("; else ");
+			n = n->nif.elsepart;
+		}
+		p = "; fi";
+		goto dotail;
+	case NSUBSHELL:
+		cmdputs("(");
+		n = n->nredir.n;
+		p = ")";
+		goto dotail;
+	case NWHILE:
+		p = "while ";
+		goto until;
+	case NUNTIL:
+		p = "until ";
+until:
+		cmdputs(p);
+		cmdtxt(n->nbinary.ch1);
+		n = n->nbinary.ch2;
+		p = "; done";
+dodo:
+		cmdputs("; do ");
+dotail:
+		cmdtxt(n);
+		goto dotail2;
+	case NFOR:
+		cmdputs("for ");
+		cmdputs(n->nfor.var);
+		cmdputs(" in ");
+		cmdlist(n->nfor.args, 1);
+		n = n->nfor.body;
+		p = "; done";
+		goto dodo;
+	case NDEFUN:
+		cmdputs(n->narg.text);
+		p = "() { ... }";
+		goto dotail2;
+	case NCMD:
+		cmdlist(n->ncmd.args, 1);
+		cmdlist(n->ncmd.redirect, 0);
+		break;
+	case NARG:
+		p = n->narg.text;
+dotail2:
+		cmdputs(p);
+		break;
+	case NHERE:
+	case NXHERE:
+		p = "<<...";
+		goto dotail2;
+	case NCASE:
+		cmdputs("case ");
+		cmdputs(n->ncase.expr->narg.text);
+		cmdputs(" in ");
+		for (np = n->ncase.cases; np; np = np->nclist.next) {
+			cmdtxt(np->nclist.pattern);
+			cmdputs(") ");
+			cmdtxt(np->nclist.body);
+			cmdputs(";; ");
+		}
+		p = "esac";
+		goto dotail2;
+	case NTO:
+		p = ">";
+		goto redir;
+	case NCLOBBER:
+		p = ">|";
+		goto redir;
+	case NAPPEND:
+		p = ">>";
+		goto redir;
+	case NTOFD:
+		p = ">&";
+		goto redir;
+	case NFROM:
+		p = "<";
+		goto redir;
+	case NFROMFD:
+		p = "<&";
+		goto redir;
+	case NFROMTO:
+		p = "<>";
+redir:
+		s[0] = n->nfile.fd + '0';
+		s[1] = '\0';
+		cmdputs(s);
+		cmdputs(p);
+		if (n->type == NTOFD || n->type == NFROMFD) {
+			s[0] = n->ndup.dupfd + '0';
+			p = s;
+			goto dotail2;
+		} else {
+			n = n->nfile.fname;
+			goto donode;
+		}
+	}
+}
+
+STATIC void
+cmdlist(union node *np, int sep)
+{
+	for (; np; np = np->narg.next) {
+		if (!sep)
+			cmdputs(spcstr);
+		cmdtxt(np);
+		if (sep && np->narg.next)
+			cmdputs(spcstr);
+	}
+}
+
+
+STATIC void
+cmdputs(const char *s)
+{
+	const char *p, *str;
+	char c, cc[2] = " ";
+	char *nextc;
+	int subtype = 0;
+	int quoted = 0;
+	static const char vstype[VSTYPE + 1][4] = {
+		"", "}", "-", "+", "?", "=",
+		"%", "%%", "#", "##",
+	};
+
+	nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
+	p = s;
+	while ((c = *p++) != 0) {
+		str = 0;
+		switch (c) {
+		case CTLESC:
+			c = *p++;
+			break;
+		case CTLVAR:
+			subtype = *p++;
+			if ((subtype & VSTYPE) == VSLENGTH)
+				str = "${#";
+			else
+				str = "${";
+			if (!(subtype & VSQUOTE) != !(quoted & 1)) {
+				quoted ^= 1;
+				c = '"';
+			} else
+				goto dostr;
+			break;
+		case CTLENDVAR:
+			str = "\"}" + !(quoted & 1);
+			quoted >>= 1;
+			subtype = 0;
+			goto dostr;
+		case CTLBACKQ:
+			str = "$(...)";
+			goto dostr;
+		case CTLBACKQ+CTLQUOTE:
+			str = "\"$(...)\"";
+			goto dostr;
+		case CTLARI:
+			str = "$((";
+			goto dostr;
+		case CTLENDARI:
+			str = "))";
+			goto dostr;
+		case CTLQUOTEMARK:
+			quoted ^= 1;
+			c = '"';
+			break;
+		case '=':
+			if (subtype == 0)
+				break;
+			if ((subtype & VSTYPE) != VSNORMAL)
+				quoted <<= 1;
+			str = vstype[subtype & VSTYPE];
+			if (subtype & VSNUL)
+				c = ':';
+			else
+				goto checkstr;
+			break;
+		case '\'':
+		case '\\':
+		case '"':
+		case '$':
+			/* These can only happen inside quotes */
+			cc[0] = c;
+			str = cc;
+			c = '\\';
+			break;
+		default:
+			break;
+		}
+		USTPUTC(c, nextc);
+checkstr:
+		if (!str)
+			continue;
+dostr:
+		while ((c = *str++)) {
+			USTPUTC(c, nextc);
+		}
+	}
+	if (quoted & 1) {
+		USTPUTC('"', nextc);
+	}
+	*nextc = 0;
+	cmdnextc = nextc;
+}
+
+
+STATIC void
+showpipe(struct job *jp, struct output *out)
+{
+	struct procstat *sp;
+	struct procstat *spend;
+
+	spend = jp->ps + jp->nprocs;
+	for (sp = jp->ps + 1; sp < spend; sp++)
+		outfmt(out, " | %s", sp->cmd);
+	outcslow('\n', out);
+	flushall();
+}
+
+
+#if JOBS
+STATIC void
+xtcsetpgrp(int fd, pid_t pgrp)
+{
+	if (tcsetpgrp(fd, pgrp))
+		sh_error("Cannot set tty process group (%s)", strerror(errno));
+}
+#endif
+
+
+STATIC int
+getstatus(struct job *job) {
+	int status;
+	int retval;
+
+	status = job->ps[job->nprocs - 1].status;
+	retval = WEXITSTATUS(status);
+	if (!WIFEXITED(status)) {
+#if JOBS
+		retval = WSTOPSIG(status);
+		if (!WIFSTOPPED(status))
+#endif
+		{
+			/* XXX: limits number of signals */
+			retval = WTERMSIG(status);
+#if JOBS
+			if (retval == SIGINT)
+				job->sigint = 1;
+#endif
+		}
+		retval += 128;
+	}
+	TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
+		jobno(job), job->nprocs, status, retval));
+	return retval;
+}
diff --git a/ash/jobs.h b/dash/jobs.h
similarity index 78%
rename from ash/jobs.h
rename to dash/jobs.h
index 47e76c2..26e421d 100644
--- a/ash/jobs.h
+++ b/dash/jobs.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: jobs.h,v 1.19 2003/11/27 21:16:14 dsl Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -34,7 +34,8 @@
  *	@(#)jobs.h	8.2 (Berkeley) 5/4/95
  */
 
-#include "output.h"
+#include <stdint.h>
+#include <sys/types.h>
 
 /* Mode argument to forkshell.  Don't change FORK_FG or FORK_BG. */
 #define FORK_FG 0
@@ -43,12 +44,8 @@
 
 /* mode flags for showjob(s) */
 #define	SHOW_PGID	0x01	/* only show pgid - for jobs -p */
-#define	SHOW_MULTILINE	0x02	/* one line per process */
 #define	SHOW_PID	0x04	/* include process pid */
 #define	SHOW_CHANGED	0x08	/* only jobs whose state has changed */
-#define	SHOW_SIGNALLED	0x10	/* only if stopped/exited on signal */
-#define	SHOW_ISSIG	0x20	/* job was signalled */
-#define	SHOW_NO_FREE	0x40	/* do not free job */
 
 
 /*
@@ -57,49 +54,55 @@
  * latter case, pidlist will be non-NULL, and will point to a -1 terminated
  * array of pids.
  */
-#define	MAXCMDTEXT	200
 
 struct procstat {
 	pid_t	pid;		/* process id */
  	int	status;		/* last process status from wait() */
- 	char	cmd[MAXCMDTEXT];/* text of command being run */
+ 	char	*cmd;		/* text of command being run */
 };
 
 struct job {
 	struct procstat ps0;	/* status of process */
 	struct procstat *ps;	/* status or processes when more than one */
-	int	nprocs;		/* number of processes */
-	pid_t	pgrp;		/* process group of this job */
-	char	state;
+#if JOBS
+	int stopstatus;		/* status of a stopped job */
+#endif
+	uint32_t
+		nprocs: 16,	/* number of processes */
+		state: 8,
 #define	JOBRUNNING	0	/* at least one proc running */
 #define	JOBSTOPPED	1	/* all procs are stopped */
 #define	JOBDONE		2	/* all procs are completed */
-	char	used;		/* true if this entry is in used */
-	char	changed;	/* true if status has changed */
 #if JOBS
-	char 	jobctl;		/* job running under job control */
-	int	prev_job;	/* previous job index */
+		sigint: 1,	/* job was killed by SIGINT */
+		jobctl: 1,	/* job running under job control */
 #endif
+		waited: 1,	/* true if this entry has been waited for */
+		used: 1,	/* true if this entry is in used */
+		changed: 1;	/* true if status has changed */
+	struct job *prev_job;	/* previous job */
 };
 
 extern pid_t backgndpid;	/* pid of last background process */
 extern int job_warning;		/* user was warned about stopped jobs */
+#if JOBS
+extern int jobctl;		/* true if doing job control */
+#else
+#define jobctl 0
+#endif
 
 void setjobctl(int);
+int killcmd(int, char **);
 int fgcmd(int, char **);
 int bgcmd(int, char **);
 int jobscmd(int, char **);
+struct output;
 void showjobs(struct output *, int);
 int waitcmd(int, char **);
-int jobidcmd(int, char **);
 struct job *makejob(union node *, int);
 int forkshell(struct job *, union node *, int);
-void forkchild(struct job *, union node *, int, int);
-int forkparent(struct job *, union node *, int, pid_t);
 int waitforjob(struct job *);
 int stoppedjobs(void);
-void commandtext(struct procstat *, union node *);
-int getjobpgrp(const char *);
 
 #if ! JOBS
 #define setjobctl(on)	/* do nothing */
diff --git a/ash/machdep.h b/dash/machdep.h
similarity index 95%
rename from ash/machdep.h
rename to dash/machdep.h
index 14e803b..f2ff0ad 100644
--- a/ash/machdep.h
+++ b/dash/machdep.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: machdep.h,v 1.11 2003/08/07 09:05:33 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
diff --git a/ash/mail.c b/dash/mail.c
similarity index 65%
rename from ash/mail.c
rename to dash/mail.c
index b71bc99..02e07f7 100644
--- a/ash/mail.c
+++ b/dash/mail.c
@@ -1,8 +1,8 @@
-/*	$NetBSD: mail.c,v 1.16 2003/08/07 09:05:33 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -32,20 +32,6 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)mail.c	8.2 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: mail.c,v 1.16 2003/08/07 09:05:33 agc Exp $");
-#endif
-#endif /* not lint */
-
 /*
  * Routines to check for mail.  (Perhaps make part of main.c?)
  */
@@ -54,72 +40,73 @@
 #include <stdlib.h>
 
 #include "shell.h"
+#include "nodes.h"
 #include "exec.h"	/* defines padvance() */
 #include "var.h"
 #include "output.h"
 #include "memalloc.h"
 #include "error.h"
 #include "mail.h"
+#include "mystring.h"
 
 
 #define MAXMBOXES 10
 
-
-STATIC int nmboxes;			/* number of mailboxes */
-STATIC time_t mailtime[MAXMBOXES];	/* times of mailboxes */
+/* times of mailboxes */
+static time_t mailtime[MAXMBOXES];
+/* Set if MAIL or MAILPATH is changed. */
+static int changed;
 
 
 
 /*
- * Print appropriate message(s) if mail has arrived.  If the argument is
- * nozero, then the value of MAIL has changed, so we just update the
- * values.
+ * Print appropriate message(s) if mail has arrived.  If changed is set,
+ * then the value of MAIL has changed, so we just update the values.
  */
 
 void
-chkmail(int silent)
+chkmail(void)
 {
-	int i;
 	const char *mpath;
 	char *p;
 	char *q;
+	time_t *mtp;
 	struct stackmark smark;
-	struct stat statb;
+	struct stat64 statb;
 
-	if (silent)
-		nmboxes = 10;
-	if (nmboxes == 0)
-		return;
 	setstackmark(&smark);
 	mpath = mpathset() ? mpathval() : mailval();
-	for (i = 0 ; i < nmboxes ; i++) {
+	for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
 		p = padvance(&mpath, nullstr);
 		if (p == NULL)
 			break;
 		if (*p == '\0')
 			continue;
 		for (q = p ; *q ; q++);
+#ifdef DEBUG
 		if (q[-1] != '/')
 			abort();
-		q[-1] = '\0';			/* delete trailing '/' */
-#ifdef notdef /* this is what the System V shell claims to do (it lies) */
-		if (stat(p, &statb) < 0)
-			statb.st_mtime = 0;
-		if (statb.st_mtime > mailtime[i] && ! silent) {
-			out2str(pathopt ? pathopt : "you have mail");
-			out2c('\n');
-		}
-		mailtime[i] = statb.st_mtime;
-#else /* this is what it should do */
-		if (stat(p, &statb) < 0)
-			statb.st_size = 0;
-		if (statb.st_size > mailtime[i] && ! silent) {
-			out2str(pathopt ? pathopt : "you have mail");
-			out2c('\n');
-		}
-		mailtime[i] = statb.st_size;
 #endif
+		q[-1] = '\0';			/* delete trailing '/' */
+		if (stat64(p, &statb) < 0) {
+			*mtp = 0;
+			continue;
+		}
+		if (!changed && statb.st_mtime != *mtp) {
+			outfmt(
+				&errout, snlfmt,
+				pathopt ? pathopt : "you have mail"
+			);
+		}
+		*mtp = statb.st_mtime;
 	}
-	nmboxes = i;
+	changed = 0;
 	popstackmark(&smark);
 }
+
+
+void
+changemail(const char *val)
+{
+	changed++;
+}
diff --git a/ash/mail.h b/dash/mail.h
similarity index 92%
rename from ash/mail.h
rename to dash/mail.h
index 9ea7c21..3c6b21d 100644
--- a/ash/mail.h
+++ b/dash/mail.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: mail.h,v 1.10 2003/08/07 09:05:34 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -34,4 +34,5 @@
  *	@(#)mail.h	8.2 (Berkeley) 5/4/95
  */
 
-void chkmail(int);
+void chkmail(void);
+void changemail(const char *);
diff --git a/ash/main.c b/dash/main.c
similarity index 68%
rename from ash/main.c
rename to dash/main.c
index 069d6f3..b421e4f 100644
--- a/ash/main.c
+++ b/dash/main.c
@@ -1,8 +1,8 @@
-/*	$NetBSD: main.c,v 1.48 2003/09/14 12:09:29 jmmv Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -32,36 +32,10 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef __COPYRIGHT
-#define __COPYRIGHT(arg)
-#endif
-#ifndef lint
-__COPYRIGHT("@(#) Copyright (c) 1991, 1993\n\
-	The Regents of the University of California.  All rights reserved.\n");
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)main.c	8.7 (Berkeley) 7/19/95";
-#else
-__RCSID("$NetBSD: main.c,v 1.48 2003/09/14 12:09:29 jmmv Exp $");
-#endif
-#endif /* not lint */
-
-#include <errno.h>
 #include <stdio.h>
 #include <signal.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#ifdef HAVE_LOCALE_H
-#include <locale.h>
-#endif
 #include <fcntl.h>
 
 
@@ -86,12 +60,17 @@
 #include "exec.h"
 #include "cd.h"
 
+#ifdef HETIO
+#include "hetio.h"
+#endif
+
 #define PROFILE 0
 
 int rootpid;
-int rootshell;
-STATIC union node *curcmd;
-STATIC union node *prevcmd;
+int shlvl;
+#ifdef __GLIBC__
+int *dash_errno;
+#endif
 #if PROFILE
 short profile_buf[16384];
 extern int etext();
@@ -99,6 +78,7 @@
 
 STATIC void read_profile(const char *);
 STATIC char *find_dot_file(char *);
+static int cmdloop(int);
 int main(int, char **);
 
 /*
@@ -112,79 +92,60 @@
 int
 main(int argc, char **argv)
 {
+	char *shinit;
+	volatile int state;
 	struct jmploc jmploc;
 	struct stackmark smark;
-	volatile int state;
-	char *shinit;
 
-#ifdef HAVE_LOCALE_H
-	setlocale(LC_ALL, "");
+#ifdef __GLIBC__
+	dash_errno = __errno_location();
 #endif
 
 #if PROFILE
 	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
 #endif
 	state = 0;
-	if (setjmp(jmploc.loc)) {
-		/*
-		 * When a shell procedure is executed, we raise the
-		 * exception EXSHELLPROC to clean up before executing
-		 * the shell procedure.
-		 */
-		switch (exception) {
-		case EXSHELLPROC:
-			rootpid = getpid();
-			rootshell = 1;
-			minusc = NULL;
-			state = 3;
-			break;
+	if (unlikely(setjmp(jmploc.loc))) {
+		int e;
+		int s;
 
-		case EXEXEC:
-			exitstatus = exerrno;
-			break;
-
-		case EXERROR:
-			exitstatus = 2;
-			break;
-
-		default:
-			break;
-		}
-
-		if (exception != EXSHELLPROC) {
-			if (state == 0 || iflag == 0 || ! rootshell)
-				exitshell(exitstatus);
-		}
 		reset();
-		if (exception == EXINT
+
+		e = exception;
+		if (e == EXERROR)
+			exitstatus = 2;
+
+		s = state;
+		if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
+			exitshell();
+
+		if (e == EXINT
 #if ATTY
 		 && (! attyset() || equal(termval(), "emacs"))
 #endif
 		 ) {
 			out2c('\n');
-			flushout(&errout);
+#ifdef FLUSHERR
+			flushout(out2);
+#endif
 		}
 		popstackmark(&smark);
 		FORCEINTON;				/* enable interrupts */
-		if (state == 1)
+		if (s == 1)
 			goto state1;
-		else if (state == 2)
+		else if (s == 2)
 			goto state2;
-		else if (state == 3)
+		else if (s == 3)
 			goto state3;
 		else
 			goto state4;
 	}
 	handler = &jmploc;
 #ifdef DEBUG
-#if DEBUG == 2
-	debug = 1;
-#endif
 	opentrace();
 	trputs("Shell args:  ");  trargs(argv);
 #endif
 	rootpid = getpid();
-	rootshell = 1;
 	init();
 	setstackmark(&smark);
 	procargs(argc, argv);
@@ -197,29 +158,18 @@
 	}
 state2:
 	state = 3;
-	if (getuid() == geteuid() && getgid() == getegid()) {
+	if (
+#ifndef linux
+		getuid() == geteuid() && getgid() == getegid() &&
+#endif
+		iflag
+	) {
 		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
-			state = 3;
 			read_profile(shinit);
 		}
 	}
 state3:
 	state = 4;
-	if (sflag == 0 || minusc) {
-		static int sigs[] =  {
-		    SIGINT, SIGQUIT, SIGHUP, 
-#ifdef SIGTSTP
-		    SIGTSTP,
-#endif
-		    SIGPIPE
-		};
-#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0]))
-		int i;
-
-		for (i = 0; i < SIGSSIZE; i++)
-		    setsignal(sigs[i], 0);
-	}
-
 	if (minusc)
 		evalstring(minusc, 0);
 
@@ -230,7 +180,13 @@
 #if PROFILE
 	monitor(0);
 #endif
-	exitshell(exitstatus);
+#if GPROF
+	{
+		extern void _mcleanup(void);
+		_mcleanup();
+	}
+#endif
+	exitshell();
 	/* NOTREACHED */
 }
 
@@ -240,7 +196,7 @@
  * loop; it turns on prompting if the shell is interactive.
  */
 
-void
+static int
 cmdloop(int top)
 {
 	union node *n;
@@ -249,18 +205,20 @@
 	int numeof = 0;
 
 	TRACE(("cmdloop(%d) called\n", top));
-	setstackmark(&smark);
+#ifdef HETIO
+	if(iflag && top)
+		hetio_init();
+#endif
 	for (;;) {
-		if (pendingsigs)
-			dotrap();
+		int skip;
+
+		setstackmark(&smark);
+		if (jobctl)
+			showjobs(out2, SHOW_CHANGED);
 		inter = 0;
 		if (iflag && top) {
-			inter = 1;
-			showjobs(out2, SHOW_CHANGED);
-#ifdef KLIBC_SH_MAIL
-			chkmail(0);
-#endif
-			flushout(&errout);
+			inter++;
+			chkmail();
 		}
 		n = parsecmd(inter);
 		/* showtree(n); DEBUG */
@@ -273,19 +231,21 @@
 				out2str("\nUse \"exit\" to leave shell.\n");
 			}
 			numeof++;
-		} else if (n != NULL && nflag == 0) {
+		} else if (nflag == 0) {
 			job_warning = (job_warning == 2) ? 1 : 0;
 			numeof = 0;
 			evaltree(n, 0);
 		}
 		popstackmark(&smark);
-		setstackmark(&smark);
-		if (evalskip == SKIPFILE) {
+
+		skip = evalskip;
+		if (skip) {
 			evalskip = 0;
-			break;
+			return skip & SKIPEVAL;
 		}
 	}
-	popstackmark(&smark);
+
+	return 0;
 }
 
 
@@ -297,31 +257,16 @@
 STATIC void
 read_profile(const char *name)
 {
-	int fd;
-	int xflag_set = 0;
-	int vflag_set = 0;
+	int skip;
 
-	INTOFF;
-	if ((fd = open(name, O_RDONLY)) >= 0)
-		setinputfd(fd, 1);
-	INTON;
-	if (fd < 0)
+	if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
 		return;
-	/* -q turns off -x and -v just when executing init files */
-	if (qflag)  {
-	    if (xflag)
-		    xflag = 0, xflag_set = 1;
-	    if (vflag)
-		    vflag = 0, vflag_set = 1;
-	}
-	cmdloop(0);
-	if (qflag)  {
-	    if (xflag_set)
-		    xflag = 1;
-	    if (vflag_set)
-		    vflag = 1;
-	}
+
+	skip = cmdloop(0);
 	popfile();
+
+	if (skip)
+		exitshell();
 }
 
 
@@ -333,14 +278,7 @@
 void
 readcmdfile(char *name)
 {
-	int fd;
-
-	INTOFF;
-	if ((fd = open(name, O_RDONLY)) >= 0)
-		setinputfd(fd, 1);
-	else
-		error("Can't open %s", name);
-	INTON;
+	setinputfile(name, INPUT_PUSH_FILE);
 	cmdloop(0);
 	popfile();
 }
@@ -376,28 +314,26 @@
 	}
 
 	/* not found in the PATH */
-	error("%s: not found", basename);
+	sh_error("%s: not found", basename);
 	/* NOTREACHED */
 }
 
 int
 dotcmd(int argc, char **argv)
 {
-	exitstatus = 0;
+	int status = 0;
 
 	if (argc >= 2) {		/* That's what SVR2 does */
 		char *fullname;
-		struct stackmark smark;
 
-		setstackmark(&smark);
 		fullname = find_dot_file(argv[1]);
-		setinputfile(fullname, 1);
+		setinputfile(fullname, INPUT_PUSH_FILE);
 		commandname = fullname;
 		cmdloop(0);
 		popfile();
-		popstackmark(&smark);
+		status = exitstatus;
 	}
-	return exitstatus;
+	return status;
 }
 
 
@@ -408,6 +344,6 @@
 		return 0;
 	if (argc > 1)
 		exitstatus = number(argv[1]);
-	exitshell(exitstatus);
+	exraise(EXEXIT);
 	/* NOTREACHED */
 }
diff --git a/ash/main.h b/dash/main.h
similarity index 82%
rename from ash/main.h
rename to dash/main.h
index d198e2d..19e4983 100644
--- a/ash/main.h
+++ b/dash/main.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: main.h,v 1.10 2003/08/07 09:05:34 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -34,10 +34,21 @@
  *	@(#)main.h	8.2 (Berkeley) 5/4/95
  */
 
-extern int rootpid;	/* pid of main shell */
-extern int rootshell;	/* true if we aren't a child of the main shell */
+#include <errno.h>
+
+/* pid of main shell */
+extern int rootpid;
+/* shell level: 0 for the main shell, 1 for its children, and so on */
+extern int shlvl;
+#define rootshell (!shlvl)
+
+#ifdef __GLIBC__
+/* glibc sucks */
+extern int *dash_errno;
+#undef errno
+#define errno (*dash_errno)
+#endif
 
 void readcmdfile(char *);
-void cmdloop(int);
 int dotcmd(int, char **);
 int exitcmd(int, char **);
diff --git a/ash/memalloc.c b/dash/memalloc.c
similarity index 75%
rename from ash/memalloc.c
rename to dash/memalloc.c
index c656371..358e6ec 100644
--- a/ash/memalloc.c
+++ b/dash/memalloc.c
@@ -1,8 +1,8 @@
-/*	$NetBSD: memalloc.c,v 1.28 2003/08/07 09:05:34 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -32,20 +32,6 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)memalloc.c	8.3 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: memalloc.c,v 1.28 2003/08/07 09:05:34 agc Exp $");
-#endif
-#endif /* not lint */
-
 #include <stdlib.h>
 #include <unistd.h>
 
@@ -55,19 +41,20 @@
 #include "error.h"
 #include "machdep.h"
 #include "mystring.h"
+#include "system.h"
 
 /*
  * Like malloc, but returns an error when out of space.
  */
 
 pointer
-ckmalloc(int nbytes)
+ckmalloc(size_t nbytes)
 {
 	pointer p;
 
 	p = malloc(nbytes);
 	if (p == NULL)
-		error("Out of space");
+		sh_error("Out of space");
 	return p;
 }
 
@@ -77,11 +64,11 @@
  */
 
 pointer
-ckrealloc(pointer p, int nbytes)
+ckrealloc(pointer p, size_t nbytes)
 {
 	p = realloc(p, nbytes);
 	if (p == NULL)
-		error("Out of space");
+		sh_error("Out of space");
 	return p;
 }
 
@@ -93,10 +80,9 @@
 char *
 savestr(const char *s)
 {
-	char *p;
-
-	p = ckmalloc(strlen(s) + 1);
-	scopy(s, p);
+	char *p = strdup(s);
+	if (!p)
+		sh_error("Out of space");
 	return p;
 }
 
@@ -106,11 +92,12 @@
  * to make this more efficient, and also to avoid all sorts of exception
  * handling code to handle interrupts in the middle of a parse.
  *
- * The size was chosen because the klibc malloc handles that size
+ * The size 504 was chosen because the Ultrix malloc handles that size
  * well.
  */
 
-#define MINSIZE (65536-5*sizeof(void *))	/* minimum size of a block */
+/* minimum size of a block */
+#define MINSIZE SHELL_ALIGN(504)
 
 struct stack_block {
 	struct stack_block *prev;
@@ -121,34 +108,40 @@
 struct stack_block *stackp = &stackbase;
 struct stackmark *markp;
 char *stacknxt = stackbase.space;
-int stacknleft = MINSIZE;
-int sstrnleft;
+size_t stacknleft = MINSIZE;
+char *sstrend = stackbase.space + MINSIZE;
 int herefd = -1;
 
 pointer
-stalloc(int nbytes)
+stalloc(size_t nbytes)
 {
 	char *p;
+	size_t aligned;
 
-	nbytes = SHELL_ALIGN(nbytes);
-	if (nbytes > stacknleft) {
-		int blocksize;
+	aligned = SHELL_ALIGN(nbytes);
+	if (aligned > stacknleft) {
+		size_t len;
+		size_t blocksize;
 		struct stack_block *sp;
 
-		blocksize = nbytes;
+		blocksize = aligned;
 		if (blocksize < MINSIZE)
 			blocksize = MINSIZE;
+		len = sizeof(struct stack_block) - MINSIZE + blocksize;
+		if (len < blocksize)
+			sh_error("Out of space");
 		INTOFF;
-		sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
+		sp = ckmalloc(len);
 		sp->prev = stackp;
 		stacknxt = sp->space;
 		stacknleft = blocksize;
+		sstrend = stacknxt + blocksize;
 		stackp = sp;
 		INTON;
 	}
 	p = stacknxt;
-	stacknxt += nbytes;
-	stacknleft -= nbytes;
+	stacknxt += aligned;
+	stacknleft -= aligned;
 	return p;
 }
 
@@ -156,10 +149,12 @@
 void
 stunalloc(pointer p)
 {
-	if (p == NULL) {		/*DEBUG */
+#ifdef DEBUG
+	if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
 		write(2, "stunalloc\n", 10);
 		abort();
 	}
+#endif
 	stacknleft += stacknxt - (char *)p;
 	stacknxt = p;
 }
@@ -191,6 +186,7 @@
 	}
 	stacknxt = mark->stacknxt;
 	stacknleft = mark->stacknleft;
+	sstrend = mark->stacknxt + mark->stacknleft;
 	INTON;
 }
 
@@ -208,23 +204,32 @@
 void
 growstackblock(void)
 {
-	int newlen = SHELL_ALIGN(stacknleft * 2 + 100);
+	size_t newlen;
+
+ 	newlen = stacknleft * 2;
+	if (newlen < stacknleft)
+		sh_error("Out of space");
+	if (newlen < 128)
+		newlen += 128;
 
 	if (stacknxt == stackp->space && stackp != &stackbase) {
 		struct stack_block *oldstackp;
 		struct stackmark *xmark;
 		struct stack_block *sp;
+		struct stack_block *prevstackp;
+		size_t grosslen;
 
 		INTOFF;
 		oldstackp = stackp;
 		sp = stackp;
-		stackp = sp->prev;
-		sp = ckrealloc((pointer)sp,
-		    sizeof(struct stack_block) - MINSIZE + newlen);
-		sp->prev = stackp;
+		prevstackp = sp->prev;
+		grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
+		sp = ckrealloc((pointer)sp, grosslen);
+		sp->prev = prevstackp;
 		stackp = sp;
 		stacknxt = sp->space;
 		stacknleft = newlen;
+		sstrend = sp->space + newlen;
 
 		/*
 		 * Stack marks pointing to the start of the old block
@@ -243,14 +248,14 @@
 		int oldlen = stacknleft;
 		char *p = stalloc(newlen);
 
-		(void)memcpy(p, oldspace, oldlen);
-		stacknxt = p;			/* free the space */
-		stacknleft += newlen;		/* we just allocated */
+		/* free the space we just allocated */
+		stacknxt = memcpy(p, oldspace, oldlen);
+		stacknleft += newlen;
 	}
 }
 
 void
-grabstackblock(int len)
+grabstackblock(size_t len)
 {
 	len = SHELL_ALIGN(len);
 	stacknxt += len;
@@ -275,17 +280,15 @@
  * is space for at least one character.
  */
 
-char *
+void *
 growstackstr(void)
 {
-	int len = stackblocksize();
+	size_t len = stackblocksize();
 	if (herefd >= 0 && len >= 1024) {
 		xwrite(herefd, stackblock(), len);
-		sstrnleft = len - 1;
 		return stackblock();
 	}
 	growstackblock();
-	sstrnleft = stackblocksize() - len - 1;
 	return stackblock() + len;
 }
 
@@ -294,19 +297,33 @@
  */
 
 char *
-makestrspace(void)
+makestrspace(size_t newlen, char *p)
 {
-	int len = stackblocksize() - sstrnleft;
-	growstackblock();
-	sstrnleft = stackblocksize() - len;
+	size_t len = p - stacknxt;
+	size_t size = stackblocksize();
+
+	for (;;) {
+		size_t nleft;
+
+		size = stackblocksize();
+		nleft = size - len;
+		if (nleft >= newlen)
+			break;
+		growstackblock();
+	}
 	return stackblock() + len;
 }
 
-void
-ungrabstackstr(char *s, char *p)
+char *
+stnputs(const char *s, size_t n, char *p)
 {
-	stacknleft += stacknxt - s;
-	stacknxt = s;
-	sstrnleft = stacknleft - (p - s);
+	p = makestrspace(n, p);
+	p = mempcpy(p, s, n);
+	return p;
+}
 
+char *
+stputs(const char *s, char *p)
+{
+	return stnputs(s, strlen(s), p);
 }
diff --git a/ash/memalloc.h b/dash/memalloc.h
similarity index 64%
rename from ash/memalloc.h
rename to dash/memalloc.h
index e793880..1691d13 100644
--- a/ash/memalloc.h
+++ b/dash/memalloc.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: memalloc.h,v 1.14 2003/08/07 09:05:34 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -34,44 +34,64 @@
  *	@(#)memalloc.h	8.2 (Berkeley) 5/4/95
  */
 
+#include <stddef.h>
+
 struct stackmark {
 	struct stack_block *stackp;
 	char *stacknxt;
-	int stacknleft;
+	size_t stacknleft;
 	struct stackmark *marknext;
 };
 
 
 extern char *stacknxt;
-extern int stacknleft;
-extern int sstrnleft;
+extern size_t stacknleft;
+extern char *sstrend;
 extern int herefd;
 
-pointer ckmalloc(int);
-pointer ckrealloc(pointer, int);
+pointer ckmalloc(size_t);
+pointer ckrealloc(pointer, size_t);
 char *savestr(const char *);
-pointer stalloc(int);
+pointer stalloc(size_t);
 void stunalloc(pointer);
 void setstackmark(struct stackmark *);
 void popstackmark(struct stackmark *);
 void growstackblock(void);
-void grabstackblock(int);
-char *growstackstr(void);
-char *makestrspace(void);
-void ungrabstackstr(char *, char *);
+void grabstackblock(size_t);
+void *growstackstr(void);
+char *makestrspace(size_t, char *);
+char *stnputs(const char *, size_t, char *);
+char *stputs(const char *, char *);
 
 
+static inline char *_STPUTC(char c, char *p) {
+	if (p == sstrend)
+		p = growstackstr();
+	*p++ = c;
+	return p;
+}
 
-#define stackblock() stacknxt
+#define stackblock() ((void *)stacknxt)
 #define stackblocksize() stacknleft
-#define STARTSTACKSTR(p)	p = stackblock(), sstrnleft = stackblocksize()
-#define STPUTC(c, p)	(--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
-#define CHECKSTRSPACE(n, p)	{ if (sstrnleft < n) p = makestrspace(); }
-#define USTPUTC(c, p)	(--sstrnleft, *p++ = (c))
-#define STACKSTRNUL(p)	(sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
-#define STUNPUTC(p)	(++sstrnleft, --p)
+#define STARTSTACKSTR(p) ((p) = stackblock())
+#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
+#define CHECKSTRSPACE(n, p) \
+	({ \
+		char *q = (p); \
+		size_t l = (n); \
+		size_t m = sstrend - q; \
+		if (l > m) \
+			(p) = makestrspace(l, q); \
+		0; \
+	})
+#define USTPUTC(c, p)	(*p++ = (c))
+#define STACKSTRNUL(p)	((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0'))
+#define STUNPUTC(p)	(--p)
 #define STTOPC(p)	p[-1]
-#define STADJUST(amount, p)	(p += (amount), sstrnleft -= (amount))
-#define grabstackstr(p)	stalloc(stackblocksize() - sstrnleft)
+#define STADJUST(amount, p)	(p += (amount))
+
+#define grabstackstr(p)	stalloc((char *)(p) - (char *)stackblock())
+#define ungrabstackstr(s, p) stunalloc((s))
+#define stackstrend() ((void *)sstrend)
 
 #define ckfree(p)	free((pointer)(p))
diff --git a/ash/miscbltin.c b/dash/miscbltin.c
similarity index 61%
rename from ash/miscbltin.c
rename to dash/miscbltin.c
index bac80b4..3f91bc3 100644
--- a/ash/miscbltin.c
+++ b/dash/miscbltin.c
@@ -1,8 +1,8 @@
-/*	$NetBSD: miscbltin.c,v 1.34 2004/04/19 01:36:32 lukem Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -32,20 +32,6 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)miscbltin.c	8.4 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: miscbltin.c,v 1.34 2004/04/19 01:36:32 lukem Exp $");
-#endif
-#endif /* not lint */
-
 /*
  * Miscelaneous builtins.
  */
@@ -58,7 +44,7 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <ctype.h>
-#include <errno.h>
+#include <stdint.h>
 
 #include "shell.h"
 #include "options.h"
@@ -68,10 +54,12 @@
 #include "error.h"
 #include "miscbltin.h"
 #include "mystring.h"
+#include "main.h"
 
 #undef rflag
 
 
+
 /*
  * The read builtin.  The -e option causes backslashes to escape the
  * following character.
@@ -87,7 +75,7 @@
 	char c;
 	int rflag;
 	char *prompt;
-	char *ifs;
+	const char *ifs;
 	char *p;
 	int startword;
 	int status;
@@ -103,12 +91,14 @@
 	}
 	if (prompt && isatty(0)) {
 		out2str(prompt);
+#ifdef FLUSHERR
 		flushall();
+#endif
 	}
 	if (*(ap = argptr) == NULL)
-		error("arg count");
-	if ((ifs = bltinlookup("IFS", 1)) == NULL)
-		ifs = nullstr;
+		sh_error("arg count");
+	if ((ifs = bltinlookup("IFS")) == NULL)
+		ifs = defifs;
 	status = 0;
 	startword = 1;
 	backslash = 0;
@@ -123,7 +113,7 @@
 		if (backslash) {
 			backslash = 0;
 			if (c != '\n')
-				STPUTC(c, p);
+				goto put;
 			continue;
 		}
 		if (!rflag && c == '\\') {
@@ -136,25 +126,20 @@
 			continue;
 		}
 		startword = 0;
-		if (backslash && c == '\\') {
-			if (read(0, &c, 1) != 1) {
-				status = 1;
-				break;
-			}
-			STPUTC(c, p);
-		} else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
+		if (ap[1] != NULL && strchr(ifs, c) != NULL) {
 			STACKSTRNUL(p);
 			setvar(*ap, stackblock(), 0);
 			ap++;
 			startword = 1;
 			STARTSTACKSTR(p);
 		} else {
+put:
 			STPUTC(c, p);
 		}
 	}
 	STACKSTRNUL(p);
 	/* Remove trailing blanks */
-	while (stackblock() <= --p && strchr(ifs, *p) != NULL)
+	while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
 		*p = '\0';
 	setvar(*ap, stackblock(), 0);
 	while (*++ap != NULL)
@@ -164,6 +149,15 @@
 
 
 
+/*
+ * umask builtin
+ *
+ * This code was ripped from pdksh 5.2.14 and hacked for use with
+ * dash by Herbert Xu.
+ *
+ * Public domain.
+ */
+
 int
 umaskcmd(int argc, char **argv)
 {
@@ -183,70 +177,102 @@
 
 	if ((ap = *argptr) == NULL) {
 		if (symbolic_mode) {
-			char u[4], g[4], o[4];
+			char buf[18];
+			int j;
 
-			i = 0;
-			if ((mask & S_IRUSR) == 0)
-				u[i++] = 'r';
-			if ((mask & S_IWUSR) == 0)
-				u[i++] = 'w';
-			if ((mask & S_IXUSR) == 0)
-				u[i++] = 'x';
-			u[i] = '\0';
-
-			i = 0;
-			if ((mask & S_IRGRP) == 0)
-				g[i++] = 'r';
-			if ((mask & S_IWGRP) == 0)
-				g[i++] = 'w';
-			if ((mask & S_IXGRP) == 0)
-				g[i++] = 'x';
-			g[i] = '\0';
-
-			i = 0;
-			if ((mask & S_IROTH) == 0)
-				o[i++] = 'r';
-			if ((mask & S_IWOTH) == 0)
-				o[i++] = 'w';
-			if ((mask & S_IXOTH) == 0)
-				o[i++] = 'x';
-			o[i] = '\0';
-
-			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
+			mask = ~mask;
+			ap = buf;
+			for (i = 0; i < 3; i++) {
+				*ap++ = "ugo"[i];
+				*ap++ = '=';
+				for (j = 0; j < 3; j++)
+					if (mask & (1 << (8 - (3*i + j))))
+						*ap++ = "rwx"[j];
+				*ap++ = ',';
+			}
+			ap[-1] = '\0';
+			out1fmt("%s\n", buf);
 		} else {
 			out1fmt("%.4o\n", mask);
 		}
 	} else {
-		if (isdigit((unsigned char)*ap)) {
-			mask = 0;
+		int new_mask;
+
+		if (isdigit(*ap)) {
+			new_mask = 0;
 			do {
 				if (*ap >= '8' || *ap < '0')
-					error("Illegal number: %s", argv[1]);
-				mask = (mask << 3) + (*ap - '0');
+					sh_error(illnum, *argptr);
+				new_mask = (new_mask << 3) + (*ap - '0');
 			} while (*++ap != '\0');
-			umask(mask);
 		} else {
-#ifdef _HAVE_SETMODE
-			void *set;
+			int positions, new_val;
+			char op;
 
-			INTOFF;
-			if ((set = setmode(ap)) != 0) {
-				mask = getmode(set, ~mask & 0777);
-				ckfree(set);
+			mask = ~mask;
+			new_mask = mask;
+			positions = 0;
+			while (*ap) {
+				while (*ap && strchr("augo", *ap))
+					switch (*ap++) {
+					case 'a': positions |= 0111; break;
+					case 'u': positions |= 0100; break;
+					case 'g': positions |= 0010; break;
+					case 'o': positions |= 0001; break;
+					}
+				if (!positions)
+					positions = 0111; /* default is a */
+				if (!strchr("=+-", op = *ap))
+					break;
+				ap++;
+				new_val = 0;
+				while (*ap && strchr("rwxugoXs", *ap))
+					switch (*ap++) {
+					case 'r': new_val |= 04; break;
+					case 'w': new_val |= 02; break;
+					case 'x': new_val |= 01; break;
+					case 'u': new_val |= mask >> 6;
+						  break;
+					case 'g': new_val |= mask >> 3;
+						  break;
+					case 'o': new_val |= mask >> 0;
+						  break;
+					case 'X': if (mask & 0111)
+							new_val |= 01;
+						  break;
+					case 's': /* ignored */
+						  break;
+					}
+				new_val = (new_val & 07) * positions;
+				switch (op) {
+				case '-':
+					new_mask &= ~new_val;
+					break;
+				case '=':
+					new_mask = new_val
+					    | (new_mask & ~(positions * 07));
+					break;
+				case '+':
+					new_mask |= new_val;
+				}
+				if (*ap == ',') {
+					positions = 0;
+					ap++;
+				} else if (!strchr("=+-", *ap))
+					break;
 			}
-			INTON;
-			if (!set)
-#endif
-				error("Illegal mode: %s", ap);
-#ifdef _HAVE_SETMODE
-			umask(~mask & 0777);
-#endif
+			if (*ap) {
+				sh_error("Illegal mode: %s", *argptr);
+				return 1;
+			}
+			new_mask = ~new_mask;
 		}
+		umask(new_mask);
 	}
 	return 0;
 }
 
-#ifndef __KLIBC__
+#ifdef HAVE_GETRLIMIT
 /*
  * ulimit builtin
  *
@@ -277,7 +303,7 @@
 #ifdef RLIMIT_STACK
 	{ "stack(kbytes)",		RLIMIT_STACK,	1024, 's' },
 #endif
-#ifdef  RLIMIT_CORE
+#ifdef RLIMIT_CORE
 	{ "coredump(blocks)",		RLIMIT_CORE,	 512, 'c' },
 #endif
 #ifdef RLIMIT_RSS
@@ -287,37 +313,86 @@
 	{ "locked memory(kbytes)",	RLIMIT_MEMLOCK, 1024, 'l' },
 #endif
 #ifdef RLIMIT_NPROC
-	{ "process(processes)",		RLIMIT_NPROC,      1, 'p' },
+	{ "process",			RLIMIT_NPROC,      1, 'p' },
 #endif
 #ifdef RLIMIT_NOFILE
-	{ "nofiles(descriptors)",	RLIMIT_NOFILE,     1, 'n' },
+	{ "nofiles",			RLIMIT_NOFILE,     1, 'n' },
 #endif
-#ifdef RLIMIT_VMEM
-	{ "vmemory(kbytes)",		RLIMIT_VMEM,	1024, 'v' },
+#ifdef RLIMIT_AS
+	{ "vmemory(kbytes)",		RLIMIT_AS,	1024, 'v' },
 #endif
-#ifdef RLIMIT_SWAP
-	{ "swap(kbytes)",		RLIMIT_SWAP,	1024, 'w' },
-#endif
-#ifdef RLIMIT_SBSIZE
-	{ "sbsize(bytes)",		RLIMIT_SBSIZE,	   1, 'b' },
+#ifdef RLIMIT_LOCKS
+	{ "locks",			RLIMIT_LOCKS,	   1, 'w' },
 #endif
 	{ (char *) 0,			0,		   0,  '\0' }
 };
 
+enum limtype { SOFT = 0x1, HARD = 0x2 };
+
+static void printlim(enum limtype how, const struct rlimit *limit,
+		     const struct limits *l)
+{
+	rlim_t val;
+
+	val = limit->rlim_max;
+	if (how & SOFT)
+		val = limit->rlim_cur;
+
+	if (val == RLIM_INFINITY)
+		out1fmt("unlimited\n");
+	else {
+		val /= l->factor;
+		out1fmt("%jd\n", (intmax_t) val);
+	}
+}
+
 int
 ulimitcmd(int argc, char **argv)
 {
 	int	c;
 	rlim_t val = 0;
-	enum { SOFT = 0x1, HARD = 0x2 }
-			how = SOFT | HARD;
+	enum limtype how = SOFT | HARD;
 	const struct limits	*l;
 	int		set, all = 0;
 	int		optc, what;
 	struct rlimit	limit;
 
 	what = 'f';
-	while ((optc = nextopt("HSabtfdsmcnpl")) != '\0')
+	while ((optc = nextopt("HSa"
+#ifdef RLIMIT_CPU
+			       "t"
+#endif
+#ifdef RLIMIT_FSIZE
+			       "f"
+#endif
+#ifdef RLIMIT_DATA
+			       "d"
+#endif
+#ifdef RLIMIT_STACK
+			       "s"
+#endif
+#ifdef RLIMIT_CORE
+			       "c"
+#endif
+#ifdef RLIMIT_RSS
+			       "m"
+#endif
+#ifdef RLIMIT_MEMLOCK
+			       "l"
+#endif
+#ifdef RLIMIT_NPROC
+			       "p"
+#endif
+#ifdef RLIMIT_NOFILE
+			       "n"
+#endif
+#ifdef RLIMIT_AS
+			       "v"
+#endif
+#ifdef RLIMIT_LOCKS
+			       "w"
+#endif
+	)) != '\0')
 		switch (optc) {
 		case 'H':
 			how = HARD;
@@ -332,17 +407,15 @@
 			what = optc;
 		}
 
-	for (l = limits; l->name && l->option != what; l++)
+	for (l = limits; l->option != what; l++)
 		;
-	if (!l->name)
-		error("internal error (%c)", what);
 
 	set = *argptr ? 1 : 0;
 	if (set) {
 		char *p = *argptr;
 
 		if (all || argptr[1])
-			error("too many arguments");
+			sh_error("too many arguments");
 		if (strcmp(p, "unlimited") == 0)
 			val = RLIM_INFINITY;
 		else {
@@ -355,30 +428,15 @@
 					break;
 			}
 			if (c)
-				error("bad number");
+				sh_error("bad number");
 			val *= l->factor;
 		}
 	}
 	if (all) {
 		for (l = limits; l->name; l++) {
 			getrlimit(l->cmd, &limit);
-			if (how & SOFT)
-				val = limit.rlim_cur;
-			else if (how & HARD)
-				val = limit.rlim_max;
-
 			out1fmt("%-20s ", l->name);
-			if (val == RLIM_INFINITY)
-				out1fmt("unlimited\n");
-			else
-			{
-				val /= l->factor;
-#ifdef BSD4_4
-				out1fmt("%lld\n", (long long) val);
-#else
-				out1fmt("%ld\n", (long) val);
-#endif
-			}
+			printlim(how, &limit, l);
 		}
 		return 0;
 	}
@@ -390,25 +448,10 @@
 		if (how & SOFT)
 			limit.rlim_cur = val;
 		if (setrlimit(l->cmd, &limit) < 0)
-			error("error setting limit (%s)", strerror(errno));
+			sh_error("error setting limit (%s)", strerror(errno));
 	} else {
-		if (how & SOFT)
-			val = limit.rlim_cur;
-		else if (how & HARD)
-			val = limit.rlim_max;
-
-		if (val == RLIM_INFINITY)
-			out1fmt("unlimited\n");
-		else
-		{
-			val /= l->factor;
-#ifdef BSD4_4
-			out1fmt("%lld\n", (long long) val);
-#else
-			out1fmt("%ld\n", (long) val);
-#endif
-		}
+		printlim(how, &limit, l);
 	}
 	return 0;
 }
-#endif /* __KLIBC__ */
+#endif
diff --git a/ash/miscbltin.h b/dash/miscbltin.h
similarity index 94%
rename from ash/miscbltin.h
rename to dash/miscbltin.h
index 4c12c82..dd9a8d1 100644
--- a/ash/miscbltin.h
+++ b/dash/miscbltin.h
@@ -1,7 +1,7 @@
-/*	$NetBSD: miscbltin.h,v 1.3 2003/08/21 17:57:53 christos Exp $	*/
-
 /*
  * Copyright (c) 1997 Christos Zoulas.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
diff --git a/dash/mkbuiltins b/dash/mkbuiltins
new file mode 100644
index 0000000..f3f91c5
--- /dev/null
+++ b/dash/mkbuiltins
@@ -0,0 +1,101 @@
+#!/bin/sh -
+#	$NetBSD: mkbuiltins,v 1.17 2002/11/24 22:35:41 christos Exp $
+#
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)mkbuiltins	8.2 (Berkeley) 5/4/95
+
+tempfile=tempfile
+if ! type tempfile > /dev/null 2>&1; then
+	tempfile=mktemp
+fi
+
+trap 'rm -f $temp $temp2' EXIT
+temp=$($tempfile)
+temp2=$($tempfile)
+
+builtins=$1
+
+exec > builtins.c
+cat <<\!
+/*
+ * This file was generated by the mkbuiltins program.
+ */
+
+#include "shell.h"
+#include "builtins.h"
+
+!
+< $builtins sed '/^#/d; /^$/d' > $temp
+awk '{	printf "int %s(int, char **);\n", $1}' $temp
+echo '
+const struct builtincmd builtincmd[] = {'
+awk '{	for (i = 2 ; i <= NF ; i++) {
+		line = $i "\t" $1
+		if ($i ~ /^-/)
+			line = $(++i) "\t" line
+		print line
+	}}' $temp | sort -k 1,1 | tee $temp2 | awk '{
+		opt = ""
+		if (NF > 2) {
+			opt = substr($2, 2)
+			$2 = $3
+		}
+		printf "\t{ \"%s\", %s, %d },\n", $1, $2,
+			(opt ~ /s/) + (opt ~ /[su]/) * 2 + (opt ~ /a/) * 4
+	}'
+echo '};'
+
+exec > builtins.h
+cat <<\!
+/*
+ * This file was generated by the mkbuiltins program.
+ */
+
+!
+sed 's/	-[a-z]*//' $temp2 | nl -v 0 | sort -u -k 3,3 |
+tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ |
+	awk '{	printf "#define %s (builtincmd + %d)\n", $3, $1}'
+printf '\n#define NUMBUILTINS %d\n' $(wc -l < $temp2)
+echo '
+#define BUILTIN_SPECIAL 0x1
+#define BUILTIN_REGULAR 0x2
+#define BUILTIN_ASSIGN 0x4
+
+struct builtincmd {
+	const char *name;
+	int (*builtin)(int, char **);
+	unsigned flags;
+};
+
+extern const struct builtincmd builtincmd[];'
diff --git a/dash/mkinit.c b/dash/mkinit.c
new file mode 100644
index 0000000..e803751
--- /dev/null
+++ b/dash/mkinit.c
@@ -0,0 +1,476 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This program scans all the source files for code to handle various
+ * special events and combines this code into one file.  This (allegedly)
+ * improves the structure of the program since there is no need for
+ * anyone outside of a module to know that that module performs special
+ * operations on particular events.
+ *
+ * Usage:  mkinit sourcefile...
+ */
+
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+
+/*
+ * OUTFILE is the name of the output file.  Output is initially written
+ * to the file OUTTEMP, which is then moved to OUTFILE.
+ */
+
+#define OUTFILE "init.c"
+#define OUTTEMP "init.c.new"
+
+
+/*
+ * A text structure is basicly just a string that grows as more characters
+ * are added onto the end of it.  It is implemented as a linked list of
+ * blocks of characters.  The routines addstr and addchar append a string
+ * or a single character, respectively, to a text structure.  Writetext
+ * writes the contents of a text structure to a file.
+ */
+
+#define BLOCKSIZE 512
+
+struct text {
+	char *nextc;
+	int nleft;
+	struct block *start;
+	struct block *last;
+};
+
+struct block {
+	struct block *next;
+	char text[BLOCKSIZE];
+};
+
+
+/*
+ * There is one event structure for each event that mkinit handles.
+ */
+
+struct event {
+	char *name;		/* name of event (e.g. INIT) */
+	char *routine;		/* name of routine called on event */
+	char *comment;		/* comment describing routine */
+	struct text code;	/* code for handling event */
+};
+
+
+char writer[] = "\
+/*\n\
+ * This file was generated by the mkinit program.\n\
+ */\n\
+\n";
+
+char init[] = "\
+/*\n\
+ * Initialization code.\n\
+ */\n";
+
+char reset[] = "\
+/*\n\
+ * This routine is called when an error or an interrupt occurs in an\n\
+ * interactive shell and control is returned to the main command loop.\n\
+ */\n";
+
+
+struct event event[] = {
+	{"INIT", "init", init},
+	{"RESET", "reset", reset},
+	{NULL, NULL}
+};
+
+
+char *curfile;				/* current file */
+int linno;				/* current line */
+char *header_files[200];		/* list of header files */
+struct text defines;			/* #define statements */
+struct text decls;			/* declarations */
+int amiddecls;				/* for formatting */
+
+
+void readfile(char *);
+int match(char *, char *);
+int gooddefine(char *);
+void doevent(struct event *, FILE *, char *);
+void doinclude(char *);
+void dodecl(char *, FILE *);
+void output(void);
+void addstr(char *, struct text *);
+void addchar(int, struct text *);
+void writetext(struct text *, FILE *);
+FILE *ckfopen(char *, char *);
+void *ckmalloc(int);
+char *savestr(char *);
+static void error(char *);
+int main(int, char **);
+
+#define equal(s1, s2)	(strcmp(s1, s2) == 0)
+
+int
+main(int argc, char **argv)
+{
+	char **ap;
+
+	header_files[0] = "\"shell.h\"";
+	header_files[1] = "\"mystring.h\"";
+	header_files[2] = "\"init.h\"";
+	for (ap = argv + 1 ; *ap ; ap++)
+		readfile(*ap);
+	output();
+	rename(OUTTEMP, OUTFILE);
+	exit(0);
+	/* NOTREACHED */
+}
+
+
+/*
+ * Parse an input file.
+ */
+
+void
+readfile(char *fname)
+{
+	FILE *fp;
+	char line[1024];
+	struct event *ep;
+
+	fp = ckfopen(fname, "r");
+	curfile = fname;
+	linno = 0;
+	amiddecls = 0;
+	while (fgets(line, sizeof line, fp) != NULL) {
+		linno++;
+		for (ep = event ; ep->name ; ep++) {
+			if (line[0] == ep->name[0] && match(ep->name, line)) {
+				doevent(ep, fp, fname);
+				break;
+			}
+		}
+		if (line[0] == 'I' && match("INCLUDE", line))
+			doinclude(line);
+		if (line[0] == 'M' && match("MKINIT", line))
+			dodecl(line, fp);
+		if (line[0] == '#' && gooddefine(line)) {
+		        char *cp;
+			char line2[1024];
+			static const char undef[] = "#undef ";
+
+			strcpy(line2, line);
+			memcpy(line2, undef, sizeof(undef) - 1);
+			cp = line2 + sizeof(undef) - 1;
+			while(*cp && (*cp == ' ' || *cp == '\t'))
+			        cp++;
+			while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n')
+			        cp++;
+			*cp++ = '\n'; *cp = '\0';
+			addstr(line2, &defines);
+			addstr(line, &defines);
+		}
+	}
+	fclose(fp);
+}
+
+
+int
+match(char *name, char *line)
+{
+	char *p, *q;
+
+	p = name, q = line;
+	while (*p) {
+		if (*p++ != *q++)
+			return 0;
+	}
+	if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n')
+		return 0;
+	return 1;
+}
+
+
+int
+gooddefine(char *line)
+{
+	char *p;
+
+	if (! match("#define", line))
+		return 0;			/* not a define */
+	p = line + 7;
+	while (*p == ' ' || *p == '\t')
+		p++;
+	while (*p != ' ' && *p != '\t') {
+		if (*p == '(')
+			return 0;		/* macro definition */
+		p++;
+	}
+	while (*p != '\n' && *p != '\0')
+		p++;
+	if (p[-1] == '\\')
+		return 0;			/* multi-line definition */
+	return 1;
+}
+
+
+void
+doevent(struct event *ep, FILE *fp, char *fname)
+{
+	char line[1024];
+	int indent;
+	char *p;
+
+	sprintf(line, "\n      /* from %s: */\n", fname);
+	addstr(line, &ep->code);
+	addstr("      {\n", &ep->code);
+	for (;;) {
+		linno++;
+		if (fgets(line, sizeof line, fp) == NULL)
+			error("Unexpected EOF");
+		if (equal(line, "}\n"))
+			break;
+		indent = 6;
+		for (p = line ; *p == '\t' ; p++)
+			indent += 8;
+		for ( ; *p == ' ' ; p++)
+			indent++;
+		if (*p == '\n' || *p == '#')
+			indent = 0;
+		while (indent >= 8) {
+			addchar('\t', &ep->code);
+			indent -= 8;
+		}
+		while (indent > 0) {
+			addchar(' ', &ep->code);
+			indent--;
+		}
+		addstr(p, &ep->code);
+	}
+	addstr("      }\n", &ep->code);
+}
+
+
+void
+doinclude(char *line)
+{
+	char *p;
+	char *name;
+	char **pp;
+
+	for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++);
+	if (*p == '\0')
+		error("Expecting '\"' or '<'");
+	name = p;
+	while (*p != ' ' && *p != '\t' && *p != '\n')
+		p++;
+	if (p[-1] != '"' && p[-1] != '>')
+		error("Missing terminator");
+	*p = '\0';
+
+	/* name now contains the name of the include file */
+	for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++);
+	if (*pp == NULL)
+		*pp = savestr(name);
+}
+
+
+void
+dodecl(char *line1, FILE *fp)
+{
+	char line[1024];
+	char *p, *q;
+
+	if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */
+		addchar('\n', &decls);
+		do {
+			linno++;
+			if (fgets(line, sizeof line, fp) == NULL)
+				error("Unterminated structure declaration");
+			addstr(line, &decls);
+		} while (line[0] != '}');
+		amiddecls = 0;
+	} else {
+		if (! amiddecls)
+			addchar('\n', &decls);
+		q = NULL;
+		for (p = line1 + 6 ; *p && strchr("=/\n", *p) == NULL; p++)
+			continue;
+		if (*p == '=') {		/* eliminate initialization */
+			for (q = p ; *q && *q != ';' ; q++);
+			if (*q == '\0')
+				q = NULL;
+			else {
+				while (p[-1] == ' ')
+					p--;
+				*p = '\0';
+			}
+		}
+		addstr("extern", &decls);
+		addstr(line1 + 6, &decls);
+		if (q != NULL)
+			addstr(q, &decls);
+		amiddecls = 1;
+	}
+}
+
+
+
+/*
+ * Write the output to the file OUTTEMP.
+ */
+
+void
+output(void)
+{
+	FILE *fp;
+	char **pp;
+	struct event *ep;
+
+	fp = ckfopen(OUTTEMP, "w");
+	fputs(writer, fp);
+	for (pp = header_files ; *pp ; pp++)
+		fprintf(fp, "#include %s\n", *pp);
+	fputs("\n\n\n", fp);
+	writetext(&defines, fp);
+	fputs("\n\n", fp);
+	writetext(&decls, fp);
+	for (ep = event ; ep->name ; ep++) {
+		fputs("\n\n\n", fp);
+		fputs(ep->comment, fp);
+		fprintf(fp, "\nvoid\n%s() {\n", ep->routine);
+		writetext(&ep->code, fp);
+		fprintf(fp, "}\n");
+	}
+	fclose(fp);
+}
+
+
+/*
+ * A text structure is simply a block of text that is kept in memory.
+ * Addstr appends a string to the text struct, and addchar appends a single
+ * character.
+ */
+
+void
+addstr(char *s, struct text *text)
+{
+	while (*s) {
+		if (--text->nleft < 0)
+			addchar(*s++, text);
+		else
+			*text->nextc++ = *s++;
+	}
+}
+
+
+void
+addchar(int c, struct text *text)
+{
+	struct block *bp;
+
+	if (--text->nleft < 0) {
+		bp = ckmalloc(sizeof *bp);
+		if (text->start == NULL)
+			text->start = bp;
+		else
+			text->last->next = bp;
+		text->last = bp;
+		text->nextc = bp->text;
+		text->nleft = BLOCKSIZE - 1;
+	}
+	*text->nextc++ = c;
+}
+
+/*
+ * Write the contents of a text structure to a file.
+ */
+void
+writetext(struct text *text, FILE *fp)
+{
+	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);
+	}
+}
+
+FILE *
+ckfopen(char *file, char *mode)
+{
+	FILE *fp;
+
+	if ((fp = fopen(file, mode)) == NULL) {
+		fprintf(stderr, "Can't open %s\n", file);
+		exit(2);
+	}
+	return fp;
+}
+
+void *
+ckmalloc(int nbytes)
+{
+	char *p;
+
+	if ((p = malloc(nbytes)) == NULL)
+		error("Out of space");
+	return p;
+}
+
+char *
+savestr(char *s)
+{
+	char *p;
+
+	p = ckmalloc(strlen(s) + 1);
+	strcpy(p, s);
+	return p;
+}
+
+static void
+error(char *msg)
+{
+	if (curfile != NULL)
+		fprintf(stderr, "%s:%d: ", curfile, linno);
+	fprintf(stderr, "%s\n", msg);
+	exit(2);
+	/* NOTREACHED */
+}
diff --git a/dash/mknodes.c b/dash/mknodes.c
new file mode 100644
index 0000000..1903a60
--- /dev/null
+++ b/dash/mknodes.c
@@ -0,0 +1,448 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This program reads the nodetypes file and nodes.c.pat file.  It generates
+ * the files nodes.h and nodes.c.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define MAXTYPES 50		/* max number of node types */
+#define MAXFIELDS 20		/* max fields in a structure */
+#define BUFLEN 100		/* size of character buffers */
+
+/* field types */
+#define T_NODE 1		/* union node *field */
+#define T_NODELIST 2		/* struct nodelist *field */
+#define T_STRING 3
+#define T_INT 4			/* int field */
+#define T_OTHER 5		/* other */
+#define T_TEMP 6		/* don't copy this field */
+
+
+struct field {			/* a structure field */
+	char *name;		/* name of field */
+	int type;			/* type of field */
+	char *decl;		/* declaration of field */
+};
+
+
+struct str {			/* struct representing a node structure */
+	char *tag;		/* structure tag */
+	int nfields;		/* number of fields in the structure */
+	struct field field[MAXFIELDS];	/* the fields of the structure */
+	int done;			/* set if fully parsed */
+};
+
+
+static int ntypes;			/* number of node types */
+static char *nodename[MAXTYPES];	/* names of the nodes */
+static struct str *nodestr[MAXTYPES];	/* type of structure used by the node */
+static int nstr;			/* number of structures */
+static struct str str[MAXTYPES];	/* the structures */
+static struct str *curstr;		/* current structure */
+static FILE *infp;
+static char line[1024];
+static int linno;
+static char *linep;
+
+static void parsenode(void);
+static void parsefield(void);
+static void output(char *);
+static void outsizes(FILE *);
+static void outfunc(FILE *, int);
+static void indent(int, FILE *);
+static int nextfield(char *);
+static void skipbl(void);
+static int readline(void);
+static void error(const char *, ...);
+static char *savestr(const char *);
+int main(int, char **);
+
+
+int
+main(int argc, char **argv)
+{
+
+	/*
+	 * some versions of linux complain: initializer element is not
+	 * constant if this is done at compile time.
+	 */
+	infp = stdin;
+
+	if (argc != 3)
+		error("usage: mknodes file");
+	if ((infp = fopen(argv[1], "r")) == NULL)
+		error("Can't open %s", argv[1]);
+	while (readline()) {
+		if (line[0] == ' ' || line[0] == '\t')
+			parsefield();
+		else if (line[0] != '\0')
+			parsenode();
+	}
+	output(argv[2]);
+	exit(0);
+	/* NOTREACHED */
+}
+
+
+
+static void
+parsenode(void)
+{
+	char name[BUFLEN];
+	char tag[BUFLEN];
+	struct str *sp;
+
+	if (curstr && curstr->nfields > 0)
+		curstr->done = 1;
+	nextfield(name);
+	if (! nextfield(tag))
+		error("Tag expected");
+	if (*linep != '\0')
+		error("Garbage at end of line");
+	nodename[ntypes] = savestr(name);
+	for (sp = str ; sp < str + nstr ; sp++) {
+		if (strcmp(sp->tag, tag) == 0)
+			break;
+	}
+	if (sp >= str + nstr) {
+		sp->tag = savestr(tag);
+		sp->nfields = 0;
+		curstr = sp;
+		nstr++;
+	}
+	nodestr[ntypes] = sp;
+	ntypes++;
+}
+
+
+static void
+parsefield(void)
+{
+	char name[BUFLEN];
+	char type[BUFLEN];
+	char decl[2 * BUFLEN];
+	struct field *fp;
+
+	if (curstr == NULL || curstr->done)
+		error("No current structure to add field to");
+	if (! nextfield(name))
+		error("No field name");
+	if (! nextfield(type))
+		error("No field type");
+	fp = &curstr->field[curstr->nfields];
+	fp->name = savestr(name);
+	if (strcmp(type, "nodeptr") == 0) {
+		fp->type = T_NODE;
+		sprintf(decl, "union node *%s", name);
+	} else if (strcmp(type, "nodelist") == 0) {
+		fp->type = T_NODELIST;
+		sprintf(decl, "struct nodelist *%s", name);
+	} else if (strcmp(type, "string") == 0) {
+		fp->type = T_STRING;
+		sprintf(decl, "char *%s", name);
+	} else if (strcmp(type, "int") == 0) {
+		fp->type = T_INT;
+		sprintf(decl, "int %s", name);
+	} else if (strcmp(type, "other") == 0) {
+		fp->type = T_OTHER;
+	} else if (strcmp(type, "temp") == 0) {
+		fp->type = T_TEMP;
+	} else {
+		error("Unknown type %s", type);
+	}
+	if (fp->type == T_OTHER || fp->type == T_TEMP) {
+		skipbl();
+		fp->decl = savestr(linep);
+	} else {
+		if (*linep)
+			error("Garbage at end of line");
+		fp->decl = savestr(decl);
+	}
+	curstr->nfields++;
+}
+
+
+char writer[] = "\
+/*\n\
+ * This file was generated by the mknodes program.\n\
+ */\n\
+\n";
+
+static void
+output(char *file)
+{
+	FILE *hfile;
+	FILE *cfile;
+	FILE *patfile;
+	int i;
+	struct str *sp;
+	struct field *fp;
+	char *p;
+
+	if ((patfile = fopen(file, "r")) == NULL)
+		error("Can't open %s", file);
+	if ((hfile = fopen("nodes.h", "w")) == NULL)
+		error("Can't create nodes.h");
+	if ((cfile = fopen("nodes.c", "w")) == NULL)
+		error("Can't create nodes.c");
+	fputs(writer, hfile);
+	for (i = 0 ; i < ntypes ; i++)
+		fprintf(hfile, "#define %s %d\n", nodename[i], i);
+	fputs("\n\n\n", hfile);
+	for (sp = str ; sp < &str[nstr] ; sp++) {
+		fprintf(hfile, "struct %s {\n", sp->tag);
+		for (i = sp->nfields, fp = sp->field ; --i >= 0 ; fp++) {
+			fprintf(hfile, "      %s;\n", fp->decl);
+		}
+		fputs("};\n\n\n", hfile);
+	}
+	fputs("union node {\n", hfile);
+	fprintf(hfile, "      int type;\n");
+	for (sp = str ; sp < &str[nstr] ; sp++) {
+		fprintf(hfile, "      struct %s %s;\n", sp->tag, sp->tag);
+	}
+	fputs("};\n\n\n", hfile);
+	fputs("struct nodelist {\n", hfile);
+	fputs("\tstruct nodelist *next;\n", hfile);
+	fputs("\tunion node *n;\n", hfile);
+	fputs("};\n\n\n", hfile);
+	fputs("struct funcnode {\n", hfile);
+	fputs("\tint count;\n", hfile);
+	fputs("\tunion node n;\n", hfile);
+	fputs("};\n\n\n", hfile);
+	fputs("struct funcnode *copyfunc(union node *);\n", hfile);
+	fputs("void freefunc(struct funcnode *);\n", hfile);
+
+	fputs(writer, cfile);
+	while (fgets(line, sizeof line, patfile) != NULL) {
+		for (p = line ; *p == ' ' || *p == '\t' ; p++);
+		if (strcmp(p, "%SIZES\n") == 0)
+			outsizes(cfile);
+		else if (strcmp(p, "%CALCSIZE\n") == 0)
+			outfunc(cfile, 1);
+		else if (strcmp(p, "%COPY\n") == 0)
+			outfunc(cfile, 0);
+		else
+			fputs(line, cfile);
+	}
+}
+
+
+
+static void
+outsizes(FILE *cfile)
+{
+	int i;
+
+	fprintf(cfile, "static const short nodesize[%d] = {\n", ntypes);
+	for (i = 0 ; i < ntypes ; i++) {
+		fprintf(cfile, "      SHELL_ALIGN(sizeof (struct %s)),\n",
+		    nodestr[i]->tag);
+	}
+	fprintf(cfile, "};\n");
+}
+
+
+static void
+outfunc(FILE *cfile, int calcsize)
+{
+	struct str *sp;
+	struct field *fp;
+	int i;
+
+	fputs("      if (n == NULL)\n", cfile);
+	if (calcsize)
+		fputs("	    return;\n", cfile);
+	else
+		fputs("	    return NULL;\n", cfile);
+	if (calcsize)
+		fputs("      funcblocksize += nodesize[n->type];\n", cfile);
+	else {
+		fputs("      new = funcblock;\n", cfile);
+		fputs("      funcblock = (char *) funcblock + nodesize[n->type];\n", cfile);
+	}
+	fputs("      switch (n->type) {\n", cfile);
+	for (sp = str ; sp < &str[nstr] ; sp++) {
+		for (i = 0 ; i < ntypes ; i++) {
+			if (nodestr[i] == sp)
+				fprintf(cfile, "      case %s:\n", nodename[i]);
+		}
+		for (i = sp->nfields ; --i >= 1 ; ) {
+			fp = &sp->field[i];
+			switch (fp->type) {
+			case T_NODE:
+				if (calcsize) {
+					indent(12, cfile);
+					fprintf(cfile, "calcsize(n->%s.%s);\n",
+						sp->tag, fp->name);
+				} else {
+					indent(12, cfile);
+					fprintf(cfile, "new->%s.%s = copynode(n->%s.%s);\n",
+						sp->tag, fp->name, sp->tag, fp->name);
+				}
+				break;
+			case T_NODELIST:
+				if (calcsize) {
+					indent(12, cfile);
+					fprintf(cfile, "sizenodelist(n->%s.%s);\n",
+						sp->tag, fp->name);
+				} else {
+					indent(12, cfile);
+					fprintf(cfile, "new->%s.%s = copynodelist(n->%s.%s);\n",
+						sp->tag, fp->name, sp->tag, fp->name);
+				}
+				break;
+			case T_STRING:
+				if (calcsize) {
+					indent(12, cfile);
+					fprintf(cfile, "funcstringsize += strlen(n->%s.%s) + 1;\n",
+						sp->tag, fp->name);
+				} else {
+					indent(12, cfile);
+					fprintf(cfile, "new->%s.%s = nodesavestr(n->%s.%s);\n",
+						sp->tag, fp->name, sp->tag, fp->name);
+				}
+				break;
+			case T_INT:
+			case T_OTHER:
+				if (! calcsize) {
+					indent(12, cfile);
+					fprintf(cfile, "new->%s.%s = n->%s.%s;\n",
+						sp->tag, fp->name, sp->tag, fp->name);
+				}
+				break;
+			}
+		}
+		indent(12, cfile);
+		fputs("break;\n", cfile);
+	}
+	fputs("      };\n", cfile);
+	if (! calcsize)
+		fputs("      new->type = n->type;\n", cfile);
+}
+
+
+static void
+indent(int amount, FILE *fp)
+{
+	while (amount >= 8) {
+		putc('\t', fp);
+		amount -= 8;
+	}
+	while (--amount >= 0) {
+		putc(' ', fp);
+	}
+}
+
+
+static int
+nextfield(char *buf)
+{
+	char *p, *q;
+
+	p = linep;
+	while (*p == ' ' || *p == '\t')
+		p++;
+	q = buf;
+	while (*p != ' ' && *p != '\t' && *p != '\0')
+		*q++ = *p++;
+	*q = '\0';
+	linep = p;
+	return (q > buf);
+}
+
+
+static void
+skipbl(void)
+{
+	while (*linep == ' ' || *linep == '\t')
+		linep++;
+}
+
+
+static int
+readline(void)
+{
+	char *p;
+
+	if (fgets(line, 1024, infp) == NULL)
+		return 0;
+	for (p = line ; *p != '#' && *p != '\n' && *p != '\0' ; p++);
+	while (p > line && (p[-1] == ' ' || p[-1] == '\t'))
+		p--;
+	*p = '\0';
+	linep = line;
+	linno++;
+	if (p - line > BUFLEN)
+		error("Line too long");
+	return 1;
+}
+
+
+
+static void
+error(const char *msg, ...)
+{
+	va_list va;
+
+	va_start(va, msg);
+
+	(void) fprintf(stderr, "line %d: ", linno);
+	(void) vfprintf(stderr, msg, va);
+	(void) fputc('\n', stderr);
+
+	va_end(va);
+
+	exit(2);
+	/* NOTREACHED */
+}
+
+
+
+static char *
+savestr(const char *s)
+{
+	char *p;
+
+	if ((p = malloc(strlen(s) + 1)) == NULL)
+		error("Out of space");
+	(void) strcpy(p, s);
+	return p;
+}
diff --git a/dash/mksignames.c b/dash/mksignames.c
new file mode 100644
index 0000000..03b8742
--- /dev/null
+++ b/dash/mksignames.c
@@ -0,0 +1,419 @@
+/* signames.c -- Create and write `signames.c', which contains an array of
+   signal names. */
+
+/* Copyright (C) 1992 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2, or (at your option) any later
+   version.
+
+   Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with Bash; see the file COPYING.  If not, write to the Free Software
+   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#if !defined (NSIG)
+#  define NSIG 64
+#endif
+
+/*
+ * Special traps:
+ *	EXIT == 0
+ */
+#define LASTSIG NSIG-1
+
+char *signal_names[2 * NSIG + 3];
+
+#define signal_names_size (sizeof(signal_names)/sizeof(signal_names[0]))
+
+char *progname;
+
+/* AIX 4.3 defines SIGRTMIN and SIGRTMAX as 888 and 999 respectively.
+   I don't want to allocate so much unused space for the intervening signal
+   numbers, so we just punt if SIGRTMAX is past the bounds of the
+   signal_names array (handled in configure). */
+#if defined (SIGRTMAX) && defined (UNUSABLE_RT_SIGNALS)
+#  undef SIGRTMAX
+#  undef SIGRTMIN
+#endif
+
+#if defined (SIGRTMAX) || defined (SIGRTMIN)
+#  define RTLEN 14
+#  define RTLIM 256
+#endif
+
+void
+initialize_signames ()
+{
+  register int i;
+#if defined (SIGRTMAX) || defined (SIGRTMIN)
+  int rtmin, rtmax, rtcnt;
+#endif
+
+  for (i = 1; i < signal_names_size; i++)
+    signal_names[i] = (char *)NULL;
+
+  /* `signal' 0 is what we do on exit. */
+  signal_names[0] = "EXIT";
+
+  /* Place signal names which can be aliases for more common signal
+     names first.  This allows (for example) SIGABRT to overwrite SIGLOST. */
+
+  /* POSIX 1003.1b-1993 real time signals, but take care of incomplete
+     implementations. Acoording to the standard, both, SIGRTMIN and
+     SIGRTMAX must be defined, SIGRTMIN must be stricly less than
+     SIGRTMAX, and the difference must be at least 7, that is, there
+     must be at least eight distinct real time signals. */
+
+  /* The generated signal names are SIGRTMIN, SIGRTMIN+1, ...,
+     SIGRTMIN+x, SIGRTMAX-x, ..., SIGRTMAX-1, SIGRTMAX. If the number
+     of RT signals is odd, there is an extra SIGRTMIN+(x+1).
+     These names are the ones used by ksh and /usr/xpg4/bin/sh on SunOS5. */
+
+#if defined (SIGRTMIN)
+  rtmin = SIGRTMIN;
+  signal_names[rtmin] = "RTMIN";
+#endif
+
+#if defined (SIGRTMAX)
+  rtmax = SIGRTMAX;
+  signal_names[rtmax] = "RTMAX";
+#endif
+
+#if defined (SIGRTMAX) && defined (SIGRTMIN)
+  if (rtmax > rtmin)
+    {
+      rtcnt = (rtmax - rtmin - 1) / 2;
+      /* croak if there are too many RT signals */
+      if (rtcnt >= RTLIM/2)
+	{
+	  rtcnt = RTLIM/2-1;
+	  fprintf(stderr, "%s: error: more than %i real time signals, fix `%s'\n",
+		  progname, RTLIM, progname);
+	}
+
+      for (i = 1; i <= rtcnt; i++)
+	{
+	  signal_names[rtmin+i] = (char *)malloc(RTLEN);
+	  if (signal_names[rtmin+i])
+	    sprintf (signal_names[rtmin+i], "RTMIN+%d", i);
+	  signal_names[rtmax-i] = (char *)malloc(RTLEN);
+	  if (signal_names[rtmax-i])
+	    sprintf (signal_names[rtmax-i], "RTMAX-%d", i);
+	}
+
+      if (rtcnt < RTLIM/2-1 && rtcnt != (rtmax-rtmin)/2)
+	{
+	  /* Need an extra RTMIN signal */
+	  signal_names[rtmin+rtcnt+1] = (char *)malloc(RTLEN);
+	  if (signal_names[rtmin+rtcnt+1])
+	    sprintf (signal_names[rtmin+rtcnt+1], "RTMIN+%d", rtcnt+1);
+	}
+    }
+#endif /* SIGRTMIN && SIGRTMAX */
+
+/* AIX */
+#if defined (SIGLOST)	/* resource lost (eg, record-lock lost) */
+  signal_names[SIGLOST] = "LOST";
+#endif
+
+#if defined (SIGMSG)	/* HFT input data pending */
+  signal_names[SIGMSG] = "MSG";
+#endif
+
+#if defined (SIGDANGER)	/* system crash imminent */
+  signal_names[SIGDANGER] = "DANGER";
+#endif
+
+#if defined (SIGMIGRATE) /* migrate process to another CPU */
+  signal_names[SIGMIGRATE] = "MIGRATE";
+#endif
+
+#if defined (SIGPRE)	/* programming error */
+  signal_names[SIGPRE] = "PRE";
+#endif
+
+#if defined (SIGVIRT)	/* AIX virtual time alarm */
+  signal_names[SIGVIRT] = "VIRT";
+#endif
+
+#if defined (SIGALRM1)	/* m:n condition variables */
+  signal_names[SIGALRM1] = "ALRM1";
+#endif
+
+#if defined (SIGWAITING)	/* m:n scheduling */
+  signal_names[SIGWAITING] = "WAITING";
+#endif
+
+#if defined (SIGGRANT)	/* HFT monitor mode granted */
+  signal_names[SIGGRANT] = "GRANT";
+#endif
+
+#if defined (SIGKAP)	/* keep alive poll from native keyboard */
+  signal_names[SIGKAP] = "KAP";
+#endif
+
+#if defined (SIGRETRACT) /* HFT monitor mode retracted */
+  signal_names[SIGRETRACT] = "RETRACT";
+#endif
+
+#if defined (SIGSOUND)	/* HFT sound sequence has completed */
+  signal_names[SIGSOUND] = "SOUND";
+#endif
+
+#if defined (SIGSAK)	/* Secure Attention Key */
+  signal_names[SIGSAK] = "SAK";
+#endif
+
+/* SunOS5 */
+#if defined (SIGLWP)	/* special signal used by thread library */
+  signal_names[SIGLWP] = "LWP";
+#endif
+
+#if defined (SIGFREEZE)	/* special signal used by CPR */
+  signal_names[SIGFREEZE] = "FREEZE";
+#endif
+
+#if defined (SIGTHAW)	/* special signal used by CPR */
+  signal_names[SIGTHAW] = "THAW";
+#endif
+
+#if defined (SIGCANCEL)	/* thread cancellation signal used by libthread */
+  signal_names[SIGCANCEL] = "CANCEL";
+#endif
+
+/* HP-UX */
+#if defined (SIGDIL)	/* DIL signal (?) */
+  signal_names[SIGDIL] = "DIL";
+#endif
+
+/* System V */
+#if defined (SIGCLD)	/* Like SIGCHLD.  */
+  signal_names[SIGCLD] = "CLD";
+#endif
+
+#if defined (SIGPWR)	/* power state indication */
+  signal_names[SIGPWR] = "PWR";
+#endif
+
+#if defined (SIGPOLL)	/* Pollable event (for streams)  */
+  signal_names[SIGPOLL] = "POLL";
+#endif
+
+/* Unknown */
+#if defined (SIGWINDOW)
+  signal_names[SIGWINDOW] = "WINDOW";
+#endif
+
+/* Common */
+#if defined (SIGHUP)	/* hangup */
+  signal_names[SIGHUP] = "HUP";
+#endif
+
+#if defined (SIGINT)	/* interrupt */
+  signal_names[SIGINT] = "INT";
+#endif
+
+#if defined (SIGQUIT)	/* quit */
+  signal_names[SIGQUIT] = "QUIT";
+#endif
+
+#if defined (SIGILL)	/* illegal instruction (not reset when caught) */
+  signal_names[SIGILL] = "ILL";
+#endif
+
+#if defined (SIGTRAP)	/* trace trap (not reset when caught) */
+  signal_names[SIGTRAP] = "TRAP";
+#endif
+
+#if defined (SIGIOT)	/* IOT instruction */
+  signal_names[SIGIOT] = "IOT";
+#endif
+
+#if defined (SIGABRT)	/* Cause current process to dump core. */
+  signal_names[SIGABRT] = "ABRT";
+#endif
+
+#if defined (SIGEMT)	/* EMT instruction */
+  signal_names[SIGEMT] = "EMT";
+#endif
+
+#if defined (SIGFPE)	/* floating point exception */
+  signal_names[SIGFPE] = "FPE";
+#endif
+
+#if defined (SIGKILL)	/* kill (cannot be caught or ignored) */
+  signal_names[SIGKILL] = "KILL";
+#endif
+
+#if defined (SIGBUS)	/* bus error */
+  signal_names[SIGBUS] = "BUS";
+#endif
+
+#if defined (SIGSEGV)	/* segmentation violation */
+  signal_names[SIGSEGV] = "SEGV";
+#endif
+
+#if defined (SIGSYS)	/* bad argument to system call */
+  signal_names[SIGSYS] = "SYS";
+#endif
+
+#if defined (SIGPIPE)	/* write on a pipe with no one to read it */
+  signal_names[SIGPIPE] = "PIPE";
+#endif
+
+#if defined (SIGALRM)	/* alarm clock */
+  signal_names[SIGALRM] = "ALRM";
+#endif
+
+#if defined (SIGTERM)	/* software termination signal from kill */
+  signal_names[SIGTERM] = "TERM";
+#endif
+
+#if defined (SIGURG)	/* urgent condition on IO channel */
+  signal_names[SIGURG] = "URG";
+#endif
+
+#if defined (SIGSTOP)	/* sendable stop signal not from tty */
+  signal_names[SIGSTOP] = "STOP";
+#endif
+
+#if defined (SIGTSTP)	/* stop signal from tty */
+  signal_names[SIGTSTP] = "TSTP";
+#endif
+
+#if defined (SIGCONT)	/* continue a stopped process */
+  signal_names[SIGCONT] = "CONT";
+#endif
+
+#if defined (SIGCHLD)	/* to parent on child stop or exit */
+  signal_names[SIGCHLD] = "CHLD";
+#endif
+
+#if defined (SIGTTIN)	/* to readers pgrp upon background tty read */
+  signal_names[SIGTTIN] = "TTIN";
+#endif
+
+#if defined (SIGTTOU)	/* like TTIN for output if (tp->t_local&LTOSTOP) */
+  signal_names[SIGTTOU] = "TTOU";
+#endif
+
+#if defined (SIGIO)	/* input/output possible signal */
+  signal_names[SIGIO] = "IO";
+#endif
+
+#if defined (SIGXCPU)	/* exceeded CPU time limit */
+  signal_names[SIGXCPU] = "XCPU";
+#endif
+
+#if defined (SIGXFSZ)	/* exceeded file size limit */
+  signal_names[SIGXFSZ] = "XFSZ";
+#endif
+
+#if defined (SIGVTALRM)	/* virtual time alarm */
+  signal_names[SIGVTALRM] = "VTALRM";
+#endif
+
+#if defined (SIGPROF)	/* profiling time alarm */
+  signal_names[SIGPROF] = "PROF";
+#endif
+
+#if defined (SIGWINCH)	/* window changed */
+  signal_names[SIGWINCH] = "WINCH";
+#endif
+
+/* 4.4 BSD */
+#if defined (SIGINFO) && !defined (_SEQUENT_)	/* information request */
+  signal_names[SIGINFO] = "INFO";
+#endif
+
+#if defined (SIGUSR1)	/* user defined signal 1 */
+  signal_names[SIGUSR1] = "USR1";
+#endif
+
+#if defined (SIGUSR2)	/* user defined signal 2 */
+  signal_names[SIGUSR2] = "USR2";
+#endif
+
+#if defined (SIGKILLTHR)	/* BeOS: Kill Thread */
+  signal_names[SIGKILLTHR] = "KILLTHR";
+#endif
+
+  for (i = 0; i < NSIG; i++)
+    if (signal_names[i] == (char *)NULL)
+      {
+	signal_names[i] = (char *)malloc (18);
+	if (signal_names[i])
+	  sprintf (signal_names[i], "%d", i);
+      }
+}
+
+void
+write_signames (stream)
+     FILE *stream;
+{
+  register int i;
+
+  fprintf (stream, "/* This file was automatically created by %s.\n",
+	   progname);
+  fprintf (stream, "   Do not edit.  Edit support/mksignames.c instead. */\n\n");
+  fprintf (stream, "#include <signal.h>\n\n");
+  fprintf (stream,
+	   "/* A translation list so we can be polite to our users. */\n");
+  fprintf (stream, "const char *const signal_names[NSIG + 1] = {\n");
+
+  for (i = 0; i <= LASTSIG; i++)
+    fprintf (stream, "    \"%s\",\n", signal_names[i]);
+
+  fprintf (stream, "    (char *)0x0\n");
+  fprintf (stream, "};\n");
+}
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  char *stream_name;
+  FILE *stream;
+
+  progname = argv[0];
+
+  if (argc == 1)
+    {
+      stream_name = "signames.c";
+    }
+  else if (argc == 2)
+    {
+      stream_name = argv[1];
+    }
+  else
+    {
+      fprintf (stderr, "Usage: %s [output-file]\n", progname);
+      exit (1);
+    }
+
+  stream = fopen (stream_name, "w");
+  if (!stream)
+    {
+      fprintf (stderr, "%s: %s: cannot open for writing\n",
+	       progname, stream_name);
+      exit (2);
+    }
+
+  initialize_signames ();
+  write_signames (stream);
+  exit (0);
+}
diff --git a/dash/mksyntax.c b/dash/mksyntax.c
new file mode 100644
index 0000000..5e65bdd
--- /dev/null
+++ b/dash/mksyntax.c
@@ -0,0 +1,395 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This program creates syntax.h and syntax.c.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "parser.h"
+
+
+struct synclass {
+	char *name;
+	char *comment;
+};
+
+/* Syntax classes */
+struct synclass synclass[] = {
+	{ "CWORD",	"character is nothing special" },
+	{ "CNL",	"newline character" },
+	{ "CBACK",	"a backslash character" },
+	{ "CSQUOTE",	"single quote" },
+	{ "CDQUOTE",	"double quote" },
+	{ "CENDQUOTE",	"a terminating quote" },
+	{ "CBQUOTE",	"backwards single quote" },
+	{ "CVAR",	"a dollar sign" },
+	{ "CENDVAR",	"a '}' character" },
+	{ "CLP",	"a left paren in arithmetic" },
+	{ "CRP",	"a right paren in arithmetic" },
+	{ "CEOF",	"end of file" },
+	{ "CCTL",	"like CWORD, except it must be escaped" },
+	{ "CSPCL",	"these terminate a word" },
+	{ "CIGN",	"character should be ignored" },
+	{ NULL,		NULL }
+};
+
+
+/*
+ * Syntax classes for is_ functions.  Warning:  if you add new classes
+ * you may have to change the definition of the is_in_name macro.
+ */
+struct synclass is_entry[] = {
+	{ "ISDIGIT",	"a digit" },
+	{ "ISUPPER",	"an upper case letter" },
+	{ "ISLOWER",	"a lower case letter" },
+	{ "ISUNDER",	"an underscore" },
+	{ "ISSPECL",	"the name of a special parameter" },
+	{ NULL, 	NULL }
+};
+
+static char writer[] = "\
+/*\n\
+ * This file was generated by the mksyntax program.\n\
+ */\n\
+\n";
+
+
+static FILE *cfile;
+static FILE *hfile;
+static char *syntax[513];
+static int base;
+static int size;	/* number of values which a char variable can have */
+static int nbits;	/* number of bits in a character */
+static int digit_contig;/* true if digits are contiguous */
+
+static void filltable(char *);
+static void init(void);
+static void add(char *, char *);
+static void print(char *);
+static void output_type_macros(int);
+static void digit_convert(void);
+int main(int, char **);
+
+int
+main(int argc, char **argv)
+{
+#ifdef	TARGET_CHAR
+	TARGET_CHAR c;
+	TARGET_CHAR d;
+#else
+	char c;
+	char d;
+#endif
+	int sign;
+	int i;
+	char buf[80];
+	int pos;
+	static char digit[] = "0123456789";
+
+	/* Create output files */
+	if ((cfile = fopen("syntax.c", "w")) == NULL) {
+		perror("syntax.c");
+		exit(2);
+	}
+	if ((hfile = fopen("syntax.h", "w")) == NULL) {
+		perror("syntax.h");
+		exit(2);
+	}
+	fputs(writer, hfile);
+	fputs(writer, cfile);
+
+	/* Determine the characteristics of chars. */
+	c = -1;
+	if (c <= 0)
+		sign = 1;
+	else
+		sign = 0;
+	for (nbits = 1 ; ; nbits++) {
+		d = (1 << nbits) - 1;
+		if (d == c)
+			break;
+	}
+	printf("%s %d bit chars\n", sign? "signed" : "unsigned", nbits);
+	if (nbits > 9) {
+		fputs("Characters can't have more than 9 bits\n", stderr);
+		exit(2);
+	}
+	size = (1 << nbits) + 1;
+	base = 2;
+	if (sign)
+		base += 1 << (nbits - 1);
+	digit_contig = 1;
+	for (i = 0 ; i < 10 ; i++) {
+		if (digit[i] != '0' + i)
+			digit_contig = 0;
+	}
+
+	fputs("#include <ctype.h>\n", hfile);
+	fputs("\n", hfile);
+	fputs("#ifdef CEOF\n", hfile);
+	fputs("#undef CEOF\n", hfile);
+	fputs("#endif\n", hfile);
+	fputs("\n", hfile);
+
+	/* Generate the #define statements in the header file */
+	fputs("/* Syntax classes */\n", hfile);
+	for (i = 0 ; synclass[i].name ; i++) {
+		sprintf(buf, "#define %s %d", synclass[i].name, i);
+		fputs(buf, hfile);
+		for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07)
+			putc('\t', hfile);
+		fprintf(hfile, "/* %s */\n", synclass[i].comment);
+	}
+	putc('\n', hfile);
+	fputs("/* Syntax classes for is_ functions */\n", hfile);
+	for (i = 0 ; is_entry[i].name ; i++) {
+		sprintf(buf, "#define %s %#o", is_entry[i].name, 1 << i);
+		fputs(buf, hfile);
+		for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07)
+			putc('\t', hfile);
+		fprintf(hfile, "/* %s */\n", is_entry[i].comment);
+	}
+	putc('\n', hfile);
+	fprintf(hfile, "#define SYNBASE %d\n", base);
+	fprintf(hfile, "#define PEOF %d\n\n", -base);
+	fprintf(hfile, "#define PEOA %d\n\n", -base + 1);
+	putc('\n', hfile);
+	fputs("#define BASESYNTAX (basesyntax + SYNBASE)\n", hfile);
+	fputs("#define DQSYNTAX (dqsyntax + SYNBASE)\n", hfile);
+	fputs("#define SQSYNTAX (sqsyntax + SYNBASE)\n", hfile);
+	fputs("#define ARISYNTAX (arisyntax + SYNBASE)\n", hfile);
+	putc('\n', hfile);
+	output_type_macros(sign);		/* is_digit, etc. */
+	putc('\n', hfile);
+
+	/* Generate the syntax tables. */
+	fputs("#include \"shell.h\"\n", cfile);
+	fputs("#include \"syntax.h\"\n\n", cfile);
+	init();
+	fputs("/* syntax table used when not in quotes */\n", cfile);
+	add("\n", "CNL");
+	add("\\", "CBACK");
+	add("'", "CSQUOTE");
+	add("\"", "CDQUOTE");
+	add("`", "CBQUOTE");
+	add("$", "CVAR");
+	add("}", "CENDVAR");
+	add("<>();&| \t", "CSPCL");
+	syntax[1] = "CSPCL";
+	print("basesyntax");
+	init();
+	fputs("\n/* syntax table used when in double quotes */\n", cfile);
+	add("\n", "CNL");
+	add("\\", "CBACK");
+	add("\"", "CENDQUOTE");
+	add("`", "CBQUOTE");
+	add("$", "CVAR");
+	add("}", "CENDVAR");
+	/* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */
+	add("!*?[=~:/-]", "CCTL");
+	print("dqsyntax");
+	init();
+	fputs("\n/* syntax table used when in single quotes */\n", cfile);
+	add("\n", "CNL");
+	add("'", "CENDQUOTE");
+	/* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */
+	add("!*?[=~:/-]\\", "CCTL");
+	print("sqsyntax");
+	init();
+	fputs("\n/* syntax table used when in arithmetic */\n", cfile);
+	add("\n", "CNL");
+	add("\\", "CBACK");
+	add("`", "CBQUOTE");
+	add("$", "CVAR");
+	add("}", "CENDVAR");
+	add("(", "CLP");
+	add(")", "CRP");
+	print("arisyntax");
+	filltable("0");
+	fputs("\n/* character classification table */\n", cfile);
+	add("0123456789", "ISDIGIT");
+	add("abcdefghijklmnopqrstucvwxyz", "ISLOWER");
+	add("ABCDEFGHIJKLMNOPQRSTUCVWXYZ", "ISUPPER");
+	add("_", "ISUNDER");
+	add("#?$!-*@", "ISSPECL");
+	print("is_type");
+	if (! digit_contig)
+		digit_convert();
+	exit(0);
+	/* NOTREACHED */
+}
+
+
+
+/*
+ * Clear the syntax table.
+ */
+
+static void
+filltable(char *dftval)
+{
+	int i;
+
+	for (i = 0 ; i < size ; i++)
+		syntax[i] = dftval;
+}
+
+
+/*
+ * Initialize the syntax table with default values.
+ */
+
+static void
+init(void)
+{
+	int ctl;
+
+	filltable("CWORD");
+	syntax[0] = "CEOF";
+	syntax[1] = "CIGN";
+	for (ctl = CTL_FIRST; ctl <= CTL_LAST; ctl++ )
+#ifdef TARGET_CHAR
+		syntax[base + (TARGET_CHAR)ctl ] = "CCTL";
+#else
+		syntax[base + ctl] = "CCTL";
+#endif /* TARGET_CHAR */
+}
+
+
+/*
+ * Add entries to the syntax table.
+ */
+
+static void
+add(char *p, char *type)
+{
+	while (*p)
+		syntax[*p++ + base] = type;
+}
+
+
+
+/*
+ * Output the syntax table.
+ */
+
+static void
+print(char *name)
+{
+	int i;
+	int col;
+
+	fprintf(hfile, "extern const char %s[];\n", name);
+	fprintf(cfile, "const char %s[%d] = {\n", name, size);
+	col = 0;
+	for (i = 0 ; i < size ; i++) {
+		if (i == 0) {
+			fputs("      ", cfile);
+		} else if ((i & 03) == 0) {
+			fputs(",\n      ", cfile);
+			col = 0;
+		} else {
+			putc(',', cfile);
+			while (++col < 9 * (i & 03))
+				putc(' ', cfile);
+		}
+		fputs(syntax[i], cfile);
+		col += strlen(syntax[i]);
+	}
+	fputs("\n};\n", cfile);
+}
+
+
+
+/*
+ * Output character classification macros (e.g. is_digit).  If digits are
+ * contiguous, we can test for them quickly.
+ */
+
+static char *macro[] = {
+	"#define is_digit(c)\t((is_type+SYNBASE)[(signed char)(c)] & ISDIGIT)\n",
+	"#define is_alpha(c)\tisalpha((unsigned char)(c))\n",
+	"#define is_name(c)\t((c) == '_' || isalpha((unsigned char)(c)))\n",
+	"#define is_in_name(c)\t((c) == '_' || isalnum((unsigned char)(c)))\n",
+	"#define is_special(c)\t((is_type+SYNBASE)[(signed char)(c)] & (ISSPECL|ISDIGIT))\n",
+	NULL
+};
+
+static void
+output_type_macros(int sign)
+{
+	char **pp;
+
+	if (digit_contig)
+		macro[0] = "#define is_digit(c)\t((unsigned)((c) - '0') <= 9)\n";
+	for (pp = macro ; *pp ; pp++)
+		fprintf(hfile, *pp, sign ? "char" : "unsigned char");
+	if (digit_contig)
+		fputs("#define digit_val(c)\t((c) - '0')\n", hfile);
+	else
+		fputs("#define digit_val(c)\t(digit_value[(unsigned char)(c)])\n", hfile);
+}
+
+
+
+/*
+ * Output digit conversion table (if digits are not contiguous).
+ */
+
+static void
+digit_convert(void)
+{
+	int maxdigit;
+	static char digit[] = "0123456789";
+	char *p;
+	int i;
+
+	maxdigit = 0;
+	for (p = digit ; *p ; p++)
+		if (*p > maxdigit)
+			maxdigit = *p;
+	fputs("extern const char digit_value[];\n", hfile);
+	fputs("\n\nconst char digit_value[] = {\n", cfile);
+	for (i = 0 ; i <= maxdigit ; i++) {
+		for (p = digit ; *p && *p != i ; p++);
+		if (*p == '\0')
+			p = digit;
+		fprintf(cfile, "      %ld,\n", (long)(p - digit));
+	}
+	fputs("};\n", cfile);
+}
diff --git a/ash/mktokens b/dash/mktokens
similarity index 89%
rename from ash/mktokens
rename to dash/mktokens
index 25f2e6e..7c873af 100644
--- a/ash/mktokens
+++ b/dash/mktokens
@@ -1,8 +1,8 @@
 #!/bin/sh -
-#	$NetBSD: mktokens,v 1.10 2003/08/22 11:22:23 agc Exp $
-#
 # Copyright (c) 1991, 1993
 #	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 #
 # This code is derived from software contributed to Berkeley by
 # Kenneth Almquist.
@@ -51,21 +51,22 @@
 TENDBQUOTE 1	"`"
 TREDIR	0	redirection
 TWORD	0	word
-TIF	0	"if"
-TTHEN	1	"then"
-TELSE	1	"else"
-TELIF	1	"elif"
-TFI	1	"fi"
-TWHILE	0	"while"
-TUNTIL	0	"until"
-TFOR	0	"for"
+TNOT	0	"!"
+TCASE	0	"case"
 TDO	1	"do"
 TDONE	1	"done"
+TELIF	1	"elif"
+TELSE	1	"else"
+TESAC	1	"esac"
+TFI	1	"fi"
+TFOR	0	"for"
+TIF	0	"if"
+TIN	0	"in"
+TTHEN	1	"then"
+TUNTIL	0	"until"
+TWHILE	0	"while"
 TBEGIN	0	"{"
 TEND	1	"}"
-TCASE	0	"case"
-TESAC	1	"esac"
-TNOT	0	"!"
 !
 nl=`wc -l /tmp/ka$$`
 exec > token.h
@@ -83,10 +84,9 @@
 echo '};
 '
 sed 's/"//g' /tmp/ka$$ | awk '
-/TIF/{print "#define KWDOFFSET " NR-1; print ""; 
-      print "const char *const parsekwd[] = {"}
-/TIF/,/neverfound/{print "	\"" $3 "\","}'
-echo '	0
-};'
+/TNOT/{print "#define KWDOFFSET " NR-1; print ""; 
+      print "STATIC const char *const parsekwd[] = {"}
+/TNOT/,/neverfound/{if (last) print "	\"" last "\","; last = $3}
+END{print "	\"" last "\"\n};"}'
 
 rm /tmp/ka$$
diff --git a/ash/myhistedit.h b/dash/myhistedit.h
similarity index 95%
rename from ash/myhistedit.h
rename to dash/myhistedit.h
index 1c77147..22e5c43 100644
--- a/ash/myhistedit.h
+++ b/dash/myhistedit.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: myhistedit.h,v 1.10 2003/08/07 09:05:35 agc Exp $	*/
-
 /*-
  * Copyright (c) 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -41,7 +41,6 @@
 void sethistsize(const char *);
 void setterm(const char *);
 int histcmd(int, char **);
-int inputrc(int, char **);
 int not_fcnumber(char *);
 int str_to_event(const char *, int);
 
diff --git a/ash/mystring.c b/dash/mystring.c
similarity index 65%
rename from ash/mystring.c
rename to dash/mystring.c
index ba750aa..49201a9 100644
--- a/ash/mystring.c
+++ b/dash/mystring.c
@@ -1,8 +1,8 @@
-/*	$NetBSD: mystring.c,v 1.16 2003/08/07 09:05:35 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -32,20 +32,6 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)mystring.c	8.2 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: mystring.c,v 1.16 2003/08/07 09:05:35 agc Exp $");
-#endif
-#endif /* not lint */
-
 /*
  * String functions.
  *
@@ -61,9 +47,17 @@
 #include "syntax.h"
 #include "error.h"
 #include "mystring.h"
+#include "memalloc.h"
+#include "parser.h"
+#include "system.h"
 
 
 char nullstr[1];		/* zero length string */
+const char spcstr[] = " ";
+const char snlfmt[] = "%s\n";
+const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
+const char illnum[] = "Illegal number: %s";
+const char homestr[] = "HOME";
 
 /*
  * equal - #defined in mystring.h
@@ -74,6 +68,7 @@
  */
 
 
+#if 0
 /*
  * scopyn - copy a string from "from" to "to", truncating the string
  *		if necessary.  "To" is always nul terminated, even if
@@ -90,20 +85,21 @@
 	}
 	*to = '\0';
 }
+#endif
 
 
 /*
  * prefix -- see if pfx is a prefix of string.
  */
 
-int
-prefix(const char *pfx, const char *string)
+char *
+prefix(const char *string, const char *pfx)
 {
 	while (*pfx) {
 		if (*pfx++ != *string++)
 			return 0;
 	}
-	return 1;
+	return (char *) string;
 }
 
 
@@ -117,7 +113,7 @@
 {
 
 	if (! is_number(s))
-		error("Illegal number: %s", s);
+		sh_error(illnum, s);
 	return atoi(s);
 }
 
@@ -136,3 +132,78 @@
 	} while (*++p != '\0');
 	return 1;
 }
+
+
+/*
+ * Produce a possibly single quoted string suitable as input to the shell.
+ * The return string is allocated on the stack.
+ */
+
+char *
+single_quote(const char *s) {
+	char *p;
+
+	STARTSTACKSTR(p);
+
+	do {
+		char *q;
+		size_t len;
+
+		len = strchrnul(s, '\'') - s;
+
+		q = p = makestrspace(len + 3, p);
+
+		*q++ = '\'';
+		q = mempcpy(q, s, len);
+		*q++ = '\'';
+		s += len;
+
+		STADJUST(q - p, p);
+
+		len = strspn(s, "'");
+		if (!len)
+			break;
+
+		q = p = makestrspace(len + 3, p);
+
+		*q++ = '"';
+		q = mempcpy(q, s, len);
+		*q++ = '"';
+		s += len;
+
+		STADJUST(q - p, p);
+	} while (*s);
+
+	USTPUTC(0, p);
+
+	return stackblock();
+}
+
+/*
+ * Like strdup but works with the ash stack.
+ */
+
+char *
+sstrdup(const char *p)
+{
+	size_t len = strlen(p) + 1;
+	return memcpy(stalloc(len), p, len);
+}
+
+/*
+ * Wrapper around strcmp for qsort/bsearch/...
+ */
+int
+pstrcmp(const void *a, const void *b)
+{
+	return strcmp(*(const char *const *) a, *(const char *const *) b);
+}
+
+/*
+ * Find a string is in a sorted array.
+ */
+const char *const *
+findstring(const char *s, const char *const *array, size_t nmemb)
+{
+	return bsearch(&s, array, nmemb, sizeof(const char *), pstrcmp);
+}
diff --git a/ash/mystring.h b/dash/mystring.h
similarity index 79%
rename from ash/mystring.h
rename to dash/mystring.h
index 08a73e9..44fd7e4 100644
--- a/ash/mystring.h
+++ b/dash/mystring.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: mystring.h,v 1.11 2003/08/07 09:05:35 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -36,10 +36,23 @@
 
 #include <string.h>
 
+extern const char snlfmt[];
+extern const char spcstr[];
+extern const char dolatstr[];
+#define DOLATSTRLEN 4
+extern const char illnum[];
+extern const char homestr[];
+
+#if 0
 void scopyn(const char *, char *, int);
-int prefix(const char *, const char *);
+#endif
+char *prefix(const char *, const char *);
 int number(const char *);
 int is_number(const char *);
+char *single_quote(const char *);
+char *sstrdup(const char *);
+int pstrcmp(const void *, const void *);
+const char *const *findstring(const char *, const char *const *, size_t);
 
 #define equal(s1, s2)	(strcmp(s1, s2) == 0)
 #define scopy(s1, s2)	((void)strcpy(s2, s1))
diff --git a/ash/nodes.c.pat b/dash/nodes.c.pat
similarity index 85%
rename from ash/nodes.c.pat
rename to dash/nodes.c.pat
index 1b5de99..9125bc7 100644
--- a/ash/nodes.c.pat
+++ b/dash/nodes.c.pat
@@ -1,8 +1,8 @@
-/*	$NetBSD: nodes.c.pat,v 1.11 2003/08/07 09:05:36 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -44,6 +44,7 @@
 #include "memalloc.h"
 #include "machdep.h"
 #include "mystring.h"
+#include "system.h"
 
 
 int     funcblocksize;		/* size of structures in function */
@@ -66,18 +67,22 @@
  * Make a copy of a parse tree.
  */
 
-union node *
-copyfunc(n)
-	union node *n;
+struct funcnode *
+copyfunc(union node *n)
 {
-	if (n == NULL)
-		return NULL;
-	funcblocksize = 0;
+	struct funcnode *f;
+	size_t blocksize;
+
+	funcblocksize = offsetof(struct funcnode, n);
 	funcstringsize = 0;
 	calcsize(n);
-	funcblock = ckmalloc(funcblocksize + funcstringsize);
-	funcstring = (char *) funcblock + funcblocksize;
-	return copynode(n);
+	blocksize = funcblocksize;
+	f = ckmalloc(blocksize + funcstringsize);
+	funcblock = (char *) f + offsetof(struct funcnode, n);
+	funcstring = (char *) f + blocksize;
+	copynode(n);
+	f->count = 0;
+	return f;
 }
 
 
@@ -141,13 +146,9 @@
 nodesavestr(s)
 	char   *s;
 {
-	register char *p = s;
-	register char *q = funcstring;
 	char   *rtn = funcstring;
 
-	while ((*q++ = *p++) != '\0')
-		continue;
-	funcstring = q;
+	funcstring = stpcpy(funcstring, s) + 1;
 	return rtn;
 }
 
@@ -158,9 +159,8 @@
  */
 
 void
-freefunc(n)
-	union node *n;
+freefunc(struct funcnode *f)
 {
-	if (n)
-		ckfree(n);
+	if (f && --f->count < 0)
+		ckfree(f);
 }
diff --git a/ash/nodetypes b/dash/nodetypes
similarity index 97%
rename from ash/nodetypes
rename to dash/nodetypes
index 4adebc0..17a7b3c 100644
--- a/ash/nodetypes
+++ b/dash/nodetypes
@@ -1,6 +1,7 @@
-#	$NetBSD: nodetypes,v 1.12 2003/08/22 11:22:23 agc Exp $
 # Copyright (c) 1991, 1993
 #	The Regents of the University of California.  All rights reserved.
+# Copyright (c) 1997-2005
+#	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 #
 # This code is derived from software contributed to Berkeley by
 # Kenneth Almquist.
@@ -48,14 +49,9 @@
 # The last two types should be followed by the text of a C declaration for
 # the field.
 
-NSEMI nbinary			# two commands separated by a semicolon
-	type	  int
-	ch1	  nodeptr		# the first child
-	ch2	  nodeptr		# the second child
-
 NCMD ncmd			# a simple command
 	type	  int
-	backgnd	  int			# set to run command in background
+	assign    nodeptr		# variable assignments
 	args	  nodeptr		# the arguments
 	redirect  nodeptr		# list of file redirections
 
@@ -75,6 +71,11 @@
 NAND nbinary			# the && operator
 NOR nbinary			# the || operator
 
+NSEMI nbinary			# two commands separated by a semicolon
+	type	  int
+	ch1	  nodeptr		# the first child
+	ch2	  nodeptr		# the second child
+
 NIF nif				# the if statement.  Elif clauses are handled
 	type	  int		    # using multiple if nodes.
 	test	  nodeptr		# if test
diff --git a/ash/options.c b/dash/options.c
similarity index 73%
rename from ash/options.c
rename to dash/options.c
index 523a15e..85f1406 100644
--- a/ash/options.c
+++ b/dash/options.c
@@ -1,8 +1,8 @@
-/*	$NetBSD: options.c,v 1.36 2004/01/05 23:23:32 jmmv Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -32,20 +32,6 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)options.c	8.2 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: options.c,v 1.36 2004/01/05 23:23:32 jmmv Exp $");
-#endif
-#endif /* not lint */
-
 #include <signal.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -64,7 +50,7 @@
 #include "memalloc.h"
 #include "error.h"
 #include "mystring.h"
-#ifdef KLIBC_SH_HISTORY
+#ifndef SMALL
 #include "myhistedit.h"
 #endif
 #include "show.h"
@@ -77,11 +63,53 @@
 
 char *minusc;			/* argument to -c option */
 
+static const char *const optnames[NOPTS] = {
+	"errexit",
+	"noglob",
+	"ignoreeof",
+	"interactive",
+	"monitor",
+	"noexec",
+	"stdin",
+	"xtrace",
+	"verbose",
+	"vi",
+	"emacs",
+	"noclobber",
+	"allexport",
+	"notify",
+	"nounset",
+	"nolog",
+	"debug",
+};
+
+const char optletters[NOPTS] = {
+	'e',
+	'f',
+	'I',
+	'i',
+	'm',
+	'n',
+	's',
+	'x',
+	'v',
+	'V',
+	'E',
+	'C',
+	'a',
+	'b',
+	'u',
+	0,
+	0,
+};
+
+char optlist[NOPTS];
+
 
 STATIC void options(int);
 STATIC void minus_o(char *, int);
 STATIC void setoption(int, int);
-STATIC int getopts(char *, char *, char **, char ***, char **);
+STATIC int getopts(char *, char *, char **, int *, int *); 
 
 
 /*
@@ -92,47 +120,53 @@
 procargs(int argc, char **argv)
 {
 	int i;
+	const char *xminusc;
+	char **xargv;
 
-	argptr = argv;
+	xargv = argv;
+	arg0 = xargv[0];
 	if (argc > 0)
-		argptr++;
+		xargv++;
 	for (i = 0; i < NOPTS; i++)
-		optlist[i].val = 2;
+		optlist[i] = 2;
+	argptr = xargv;
 	options(1);
-	if (*argptr == NULL && minusc == NULL)
+	xargv = argptr;
+	xminusc = minusc;
+	if (*xargv == NULL) {
+		if (xminusc)
+			sh_error("-c requires an argument");
 		sflag = 1;
+	}
 	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
 		iflag = 1;
 	if (mflag == 2)
 		mflag = iflag;
 	for (i = 0; i < NOPTS; i++)
-		if (optlist[i].val == 2)
-			optlist[i].val = 0;
+		if (optlist[i] == 2)
+			optlist[i] = 0;
 #if DEBUG == 2
 	debug = 1;
 #endif
-	arg0 = argv[0];
-	if (sflag == 0 && minusc == NULL) {
-		commandname = argv[0];
-		arg0 = *argptr++;
-		setinputfile(arg0, 0);
+	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
+	if (xminusc) {
+		minusc = *xargv++;
+		if (*xargv)
+			goto setarg0;
+	} else if (!sflag) {
+		setinputfile(*xargv, 0);
+setarg0:
+		arg0 = *xargv++;
 		commandname = arg0;
 	}
-	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
-	if (minusc != NULL) {
-		if (argptr == NULL || *argptr == NULL)
-			error("Bad -c option");
-		minusc = *argptr++;
-		if (*argptr != 0)
-			arg0 = *argptr++;
-	}
 
-	shellparam.p = argptr;
-	shellparam.reset = 1;
+	shellparam.p = xargv;
+	shellparam.optind = 1;
+	shellparam.optoff = -1;
 	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
-	while (*argptr) {
+	while (*xargv) {
 		shellparam.nparam++;
-		argptr++;
+		xargv++;
 	}
 	optschanged();
 }
@@ -141,8 +175,11 @@
 void
 optschanged(void)
 {
+#ifdef DEBUG
+	opentrace();
+#endif
 	setinteractive(iflag);
-#ifdef KLIBC_SH_HISTORY
+#ifndef SMALL
 	histedit();
 #endif
 	setjobctl(mflag);
@@ -185,7 +222,7 @@
 		}
 		while ((c = *p++) != '\0') {
 			if (c == 'c' && cmdline) {
-				minusc = "";	/* command is after shell args*/
+				minusc = p;	/* command is after shell args*/
 			} else if (c == 'o') {
 				minus_o(*argptr, val);
 				if (*argptr)
@@ -197,25 +234,6 @@
 	}
 }
 
-static void
-set_opt_val(int i, int val)
-{
-	int j;
-	int flag;
-
-	if (val && (flag = optlist[i].opt_set)) {
-		/* some options (eg vi/emacs) are mutually exclusive */
-		for (j = 0; j < NOPTS; j++)
-		    if (optlist[j].opt_set == flag)
-			optlist[j].val = 0;
-	}
-	optlist[i].val = val;
-#ifdef DEBUG
-	if (&optlist[i].val == &debug)
-		opentrace();
-#endif
-}
-
 STATIC void
 minus_o(char *name, int val)
 {
@@ -224,15 +242,15 @@
 	if (name == NULL) {
 		out1str("Current option settings\n");
 		for (i = 0; i < NOPTS; i++)
-			out1fmt("%-16s%s\n", optlist[i].name,
-				optlist[i].val ? "on" : "off");
+			out1fmt("%-16s%s\n", optnames[i],
+				optlist[i] ? "on" : "off");
 	} else {
 		for (i = 0; i < NOPTS; i++)
-			if (equal(name, optlist[i].name)) {
-				set_opt_val(i, val);
+			if (equal(name, optnames[i])) {
+				optlist[i] = val;
 				return;
 			}
-		error("Illegal option -o %s", name);
+		sh_error("Illegal option -o %s", name);
 	}
 }
 
@@ -243,30 +261,23 @@
 	int i;
 
 	for (i = 0; i < NOPTS; i++)
-		if (optlist[i].letter == flag) {
-			set_opt_val( i, val );
+		if (optletters[i] == flag) {
+			optlist[i] = val;
+			if (val) {
+				/* #%$ hack for ksh semantics */
+				if (flag == 'V')
+					Eflag = 0;
+				else if (flag == 'E')
+					Vflag = 0;
+			}
 			return;
 		}
-	error("Illegal option -%c", flag);
+	sh_error("Illegal option -%c", flag);
 	/* NOTREACHED */
 }
 
 
 
-#ifdef mkinit
-INCLUDE "options.h"
-
-SHELLPROC {
-	int i;
-
-	for (i = 0; optlist[i].name; i++)
-		optlist[i].val = 0;
-	optschanged();
-
-}
-#endif
-
-
 /*
  * Set the shell parameters.
  */
@@ -288,7 +299,8 @@
 	shellparam.malloc = 1;
 	shellparam.nparam = nparam;
 	shellparam.p = newparam;
-	shellparam.optnext = NULL;
+	shellparam.optind = 1;
+	shellparam.optoff = -1;
 }
 
 
@@ -324,7 +336,7 @@
 	if (argc > 1)
 		n = number(argv[1]);
 	if (n > shellparam.nparam)
-		error("can't shift that many");
+		sh_error("can't shift that many");
 	INTOFF;
 	shellparam.nparam -= n;
 	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
@@ -333,7 +345,8 @@
 	}
 	ap2 = shellparam.p;
 	while ((*ap2++ = *ap1++) != NULL);
-	shellparam.optnext = NULL;
+	shellparam.optind = 1;
+	shellparam.optoff = -1;
 	INTON;
 	return 0;
 }
@@ -348,7 +361,7 @@
 setcmd(int argc, char **argv)
 {
 	if (argc == 1)
-		return showvars(0, 0, 1);
+		return showvars(nullstr, 0, VUNSET);
 	INTOFF;
 	options(0);
 	optschanged();
@@ -364,10 +377,8 @@
 getoptsreset(value)
 	const char *value;
 {
-	if (number(value) == 1) {
-		shellparam.optnext = NULL;
-		shellparam.reset = 1;
-	}
+	shellparam.optind = number(value);
+	shellparam.optoff = -1;
 }
 
 /*
@@ -383,46 +394,54 @@
 	char **optbase;
 
 	if (argc < 3)
-		error("usage: getopts optstring var [arg]");
-	else if (argc == 3)
+		sh_error("Usage: getopts optstring var [arg]");
+	else if (argc == 3) {
 		optbase = shellparam.p;
-	else
+		if (shellparam.optind > shellparam.nparam + 1) {
+			shellparam.optind = 1;
+			shellparam.optoff = -1;
+		}
+	}
+	else {
 		optbase = &argv[3];
-
-	if (shellparam.reset == 1) {
-		shellparam.optnext = optbase;
-		shellparam.optptr = NULL;
-		shellparam.reset = 0;
+		if (shellparam.optind > argc - 2) {
+			shellparam.optind = 1;
+			shellparam.optoff = -1;
+		}
 	}
 
-	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
-		       &shellparam.optptr);
+	return getopts(argv[1], argv[2], optbase, &shellparam.optind,
+		       &shellparam.optoff);
 }
 
 STATIC int
-getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr)
+getopts(char *optstr, char *optvar, char **optfirst, int *optind, int *optoff)
 {
 	char *p, *q;
 	char c = '?';
 	int done = 0;
-	int ind = 0;
 	int err = 0;
 	char s[12];
+	char **optnext;
 
-	if ((p = *optpptr) == NULL || *p == '\0') {
+	if (*optind < 1)
+		return 1;
+	optnext = optfirst + *optind - 1;
+
+	if (*optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
+		p = NULL;
+	else
+		p = optnext[-1] + *optoff;
+	if (p == NULL || *p == '\0') {
 		/* Current word is done, advance */
-		if (*optnext == NULL)
-			return 1;
-		p = **optnext;
+		p = *optnext;
 		if (p == NULL || *p != '-' || *++p == '\0') {
 atend:
-			ind = *optnext - optfirst + 1;
-			*optnext = NULL;
 			p = NULL;
 			done = 1;
 			goto out;
 		}
-		(*optnext)++;
+		optnext++;
 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
 			goto atend;
 	}
@@ -436,17 +455,17 @@
 				err |= setvarsafe("OPTARG", s, 0);
 			} else {
 				outfmt(&errout, "Illegal option -%c\n", c);
-				(void) unsetvar("OPTARG", 0);
+				(void) unsetvar("OPTARG");
 			}
 			c = '?';
-			goto bad;
+			goto out;
 		}
 		if (*++q == ':')
 			q++;
 	}
 
 	if (*++q == ':') {
-		if (*p == '\0' && (p = **optnext) == NULL) {
+		if (*p == '\0' && (p = *optnext) == NULL) {
 			if (optstr[0] == ':') {
 				s[0] = c;
 				s[1] = '\0';
@@ -454,35 +473,30 @@
 				c = ':';
 			} else {
 				outfmt(&errout, "No arg for -%c option\n", c);
-				(void) unsetvar("OPTARG", 0);
+				(void) unsetvar("OPTARG");
 				c = '?';
 			}
-			goto bad;
+			goto out;
 		}
 
-		if (p == **optnext)
-			(*optnext)++;
+		if (p == *optnext)
+			optnext++;
 		err |= setvarsafe("OPTARG", p, 0);
 		p = NULL;
 	} else
-		err |= setvarsafe("OPTARG", "", 0);
-	ind = *optnext - optfirst + 1;
-	goto out;
+		err |= setvarsafe("OPTARG", nullstr, 0);
 
-bad:
-	ind = 1;
-	*optnext = NULL;
-	p = NULL;
 out:
-	*optpptr = p;
-	fmtstr(s, sizeof(s), "%d", ind);
+	*optoff = p ? p - *(optnext - 1) : -1;
+	*optind = optnext - optfirst + 1;
+	fmtstr(s, sizeof(s), "%d", *optind);
 	err |= setvarsafe("OPTIND", s, VNOFUNC);
 	s[0] = c;
 	s[1] = '\0';
 	err |= setvarsafe(optvar, s, 0);
 	if (err) {
-		*optnext = NULL;
-		*optpptr = NULL;
+		*optind = 1;
+		*optoff = -1;
 		flushall();
 		exraise(EXERROR);
 	}
@@ -518,13 +532,13 @@
 	c = *p++;
 	for (q = optstring ; *q != c ; ) {
 		if (*q == '\0')
-			error("Illegal option -%c", c);
+			sh_error("Illegal option -%c", c);
 		if (*++q == ':')
 			q++;
 	}
 	if (*++q == ':') {
 		if (*p == '\0' && (p = *argptr++) == NULL)
-			error("No arg for -%c option", c);
+			sh_error("No arg for -%c option", c);
 		optionarg = p;
 		p = NULL;
 	}
diff --git a/dash/options.h b/dash/options.h
new file mode 100644
index 0000000..45bbe5a
--- /dev/null
+++ b/dash/options.h
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)options.h	8.2 (Berkeley) 5/4/95
+ */
+
+struct shparam {
+	int nparam;		/* # of positional parameters (without $0) */
+	unsigned char malloc;	/* if parameter list dynamically allocated */
+	char **p;		/* parameter list */
+	int optind;		/* next parameter to be processed by getopts */
+	int optoff;		/* used by getopts */
+};
+
+
+
+#define eflag optlist[0]
+#define fflag optlist[1]
+#define Iflag optlist[2]
+#define iflag optlist[3]
+#define mflag optlist[4]
+#define nflag optlist[5]
+#define sflag optlist[6]
+#define xflag optlist[7]
+#define vflag optlist[8]
+#define Vflag optlist[9]
+#define	Eflag optlist[10]
+#define	Cflag optlist[11]
+#define	aflag optlist[12]
+#define	bflag optlist[13]
+#define	uflag optlist[14]
+#define	nolog optlist[15]
+#define	debug optlist[16]
+
+#define NOPTS	17
+
+extern const char optletters[NOPTS];
+extern char optlist[NOPTS];
+
+
+extern char *minusc;		/* argument to -c option */
+extern char *arg0;		/* $0 */
+extern struct shparam shellparam;  /* $@ */
+extern char **argptr;		/* argument list for builtin commands */
+extern char *optionarg;		/* set by nextopt */
+extern char *optptr;		/* used by nextopt */
+
+void procargs(int, char **);
+void optschanged(void);
+void setparam(char **);
+void freeparam(volatile struct shparam *);
+int shiftcmd(int, char **);
+int setcmd(int, char **);
+int getoptscmd(int, char **);
+int nextopt(const char *);
+void getoptsreset(const char *);
diff --git a/dash/output.c b/dash/output.c
new file mode 100644
index 0000000..2f9b5c4
--- /dev/null
+++ b/dash/output.c
@@ -0,0 +1,385 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Shell output routines.  We use our own output routines because:
+ *	When a builtin command is interrupted we have to discard
+ *		any pending output.
+ *	When a builtin command appears in back quotes, we want to
+ *		save the output of the command in a region obtained
+ *		via malloc, rather than doing a fork and reading the
+ *		output of the command via a pipe.
+ *	Our output routines may be smaller than the stdio routines.
+ */
+
+#include <sys/types.h>		/* quad_t */
+#include <sys/param.h>		/* BSD4_4 */
+#include <sys/ioctl.h>
+
+#include <stdio.h>	/* defines BUFSIZ */
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifdef USE_GLIBC_STDIO
+#include <fcntl.h>
+#endif
+#include <limits.h>
+
+#include "shell.h"
+#include "syntax.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "main.h"
+#include "system.h"
+
+
+#define OUTBUFSIZ BUFSIZ
+#define MEM_OUT -3		/* output to dynamically allocated memory */
+
+
+#ifdef USE_GLIBC_STDIO
+struct output output = {
+	stream: 0, nextc: 0, end: 0, buf: 0, bufsize: 0, fd: 1, flags: 0
+};
+struct output errout = {
+	stream: 0, nextc: 0, end: 0, buf: 0, bufsize: 0, fd: 2, flags: 0
+}
+#ifdef notyet
+struct output memout = {
+	stream: 0, nextc: 0, end: 0, buf: 0, bufsize: 0, fd: MEM_OUT, flags: 0
+};
+#endif
+#else
+struct output output = {
+	nextc: 0, end: 0, buf: 0, bufsize: OUTBUFSIZ, fd: 1, flags: 0
+};
+struct output errout = {
+	nextc: 0, end: 0, buf: 0, bufsize: 0, fd: 2, flags: 0
+};
+struct output preverrout;
+#ifdef notyet
+struct output memout = {
+	nextc: 0, end: 0, buf: 0, bufsize: 0, fd: MEM_OUT, flags: 0
+};
+#endif
+#endif
+struct output *out1 = &output;
+struct output *out2 = &errout;
+
+
+#ifndef USE_GLIBC_STDIO
+static void __outstr(const char *, size_t, struct output *);
+#endif
+static int xvsnprintf(char *, size_t, const char *, va_list);
+
+
+#ifdef mkinit
+
+INCLUDE "output.h"
+INCLUDE "memalloc.h"
+
+INIT {
+#ifdef USE_GLIBC_STDIO
+	initstreams();
+#endif
+}
+
+RESET {
+#ifdef notyet
+	out1 = &output;
+	out2 = &errout;
+#ifdef USE_GLIBC_STDIO
+	if (memout.stream != NULL)
+		__closememout();
+#endif
+	if (memout.buf != NULL) {
+		ckfree(memout.buf);
+		memout.buf = NULL;
+	}
+#endif
+}
+
+#endif
+
+
+#ifndef USE_GLIBC_STDIO
+static void
+__outstr(const char *p, size_t len, struct output *dest)
+{
+	size_t bufsize;
+	size_t offset;
+	size_t nleft;
+
+	nleft = dest->end - dest->nextc;
+	if (nleft >= len) {
+buffered:
+		dest->nextc = mempcpy(dest->nextc, p, len);
+		return;
+	}
+
+	bufsize = dest->bufsize;
+	if (!bufsize) {
+		;
+	} else if (dest->buf == NULL) {
+		if (dest->fd == MEM_OUT && len > bufsize) {
+			bufsize = len;
+		}
+		offset = 0;
+		goto alloc;
+	} else if (dest->fd == MEM_OUT) {
+		offset = bufsize;
+		if (bufsize >= len) {
+			bufsize <<= 1;
+		} else {
+			bufsize += len;
+		}
+		if (bufsize < offset)
+			goto err;
+alloc:
+		INTOFF;
+		dest->buf = ckrealloc(dest->buf, bufsize);
+		dest->bufsize = bufsize;
+		dest->end = dest->buf + bufsize;
+		dest->nextc = dest->buf + offset;
+		INTON;
+	} else {
+		flushout(dest);
+	}
+
+	nleft = dest->end - dest->nextc;
+	if (nleft > len)
+		goto buffered;
+
+	if ((xwrite(dest->fd, p, len))) {
+err:
+		dest->flags |= OUTPUT_ERR;
+	}
+}
+#endif
+
+
+void
+outstr(const char *p, struct output *file)
+{
+#ifdef USE_GLIBC_STDIO
+	INTOFF;
+	fputs(p, file->stream);
+	INTON;
+#else
+	size_t len;
+
+	len = strlen(p);
+	__outstr(p, len, file);
+#endif
+}
+
+
+#ifndef USE_GLIBC_STDIO
+
+
+void
+outcslow(int c, struct output *dest)
+{
+	char buf = c;
+	__outstr(&buf, 1, dest);
+}
+#endif
+
+
+void
+flushall(void)
+{
+	flushout(&output);
+#ifdef FLUSHERR
+	flushout(&errout);
+#endif
+}
+
+
+void
+flushout(struct output *dest)
+{
+#ifdef USE_GLIBC_STDIO
+	INTOFF;
+	fflush(dest->stream);
+	INTON;
+#else
+	size_t len;
+
+	len = dest->nextc - dest->buf;
+	if (!len || dest->fd < 0)
+		return;
+	dest->nextc = dest->buf;
+	if ((xwrite(dest->fd, dest->buf, len)))
+		dest->flags |= OUTPUT_ERR;
+#endif
+}
+
+
+void
+outfmt(struct output *file, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	doformat(file, fmt, ap);
+	va_end(ap);
+}
+
+
+void
+out1fmt(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	doformat(out1, fmt, ap);
+	va_end(ap);
+}
+
+
+int
+fmtstr(char *outbuf, size_t length, const char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	va_start(ap, fmt);
+	ret = xvsnprintf(outbuf, length, fmt, ap);
+	va_end(ap);
+	return ret;
+}
+
+
+#ifndef USE_GLIBC_STDIO
+void
+doformat(struct output *dest, const char *f, va_list ap)
+{
+	struct stackmark smark;
+	char *s;
+	int len, ret;
+	size_t size;
+	va_list ap2;
+
+	va_copy(ap2, ap);
+	size = dest->end - dest->nextc;
+	len = xvsnprintf(dest->nextc, size, f, ap2);
+	va_end(ap2);
+	if (len < 0) {
+		dest->flags |= OUTPUT_ERR;
+		return;
+	}
+	if (len < size) {
+		dest->nextc += len;
+		return;
+	}
+	setstackmark(&smark);
+	s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1);
+	ret = xvsnprintf(s, len + 1, f, ap);
+	if (ret == len)
+		__outstr(s, len, dest);
+	else
+		dest->flags |= OUTPUT_ERR;
+	popstackmark(&smark);
+}
+#endif
+
+
+
+/*
+ * Version of write which resumes after a signal is caught.
+ */
+
+int
+xwrite(int fd, const void *p, size_t n)
+{
+	const char *buf = p;
+
+	while (n) {
+		ssize_t i;
+		size_t m;
+
+		m = n;
+		if (m > SSIZE_MAX)
+			m = SSIZE_MAX;
+		do {
+			i = write(fd, buf, m);
+		} while (i < 0 && errno == EINTR);
+		if (i < 0)
+			return -1;
+		buf += i;
+		n -= i;
+	}
+	return 0;
+}
+
+
+#ifdef notyet
+#ifdef USE_GLIBC_STDIO
+void initstreams() {
+	output.stream = stdout;
+	errout.stream = stderr;
+}
+
+
+void
+openmemout(void) {
+	INTOFF;
+	memout.stream = open_memstream(&memout.buf, &memout.bufsize);
+	INTON;
+}
+
+
+int
+__closememout(void) {
+	int error;
+	error = fclose(memout.stream);
+	memout.stream = NULL;
+	return error;
+}
+#endif
+#endif
+
+
+static int
+xvsnprintf(char *outbuf, size_t length, const char *fmt, va_list ap)
+{
+	int ret;
+
+	INTOFF;
+	ret = vsnprintf(outbuf, length, fmt, ap);
+	INTON;
+	return ret;
+}
diff --git a/ash/output.h b/dash/output.h
similarity index 66%
rename from ash/output.h
rename to dash/output.h
index 17b6a38..d123301 100644
--- a/ash/output.h
+++ b/dash/output.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: output.h,v 1.17 2003/08/07 09:05:36 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -37,47 +37,76 @@
 #ifndef OUTPUT_INCL
 
 #include <stdarg.h>
+#ifdef USE_GLIBC_STDIO
+#include <stdio.h>
+#endif
+#include <sys/types.h>
 
 struct output {
+#ifdef USE_GLIBC_STDIO
+	FILE *stream;
+#endif
 	char *nextc;
-	int nleft;
+	char *end;
 	char *buf;
-	int bufsize;
-	short fd;
-	short flags;
+	size_t bufsize;
+	int fd;
+	int flags;
 };
 
 extern struct output output;
 extern struct output errout;
+extern struct output preverrout;
+#ifdef notyet
 extern struct output memout;
+#endif
 extern struct output *out1;
 extern struct output *out2;
 
-void open_mem(char *, int, struct output *);
-void out1str(const char *);
-void out2str(const char *);
 void outstr(const char *, struct output *);
-void emptyoutbuf(struct output *);
+#ifndef USE_GLIBC_STDIO
+void outcslow(int, struct output *);
+#endif
 void flushall(void);
 void flushout(struct output *);
-void freestdout(void);
 void outfmt(struct output *, const char *, ...)
     __attribute__((__format__(__printf__,2,3)));
 void out1fmt(const char *, ...)
     __attribute__((__format__(__printf__,1,2)));
-#ifndef __linux__
-void dprintf(const char *, ...)
-    __attribute__((__format__(__printf__,1,2)));
-#endif
-void fmtstr(char *, size_t, const char *, ...)
+int fmtstr(char *, size_t, const char *, ...)
     __attribute__((__format__(__printf__,3,4)));
+#ifndef USE_GLIBC_STDIO
 void doformat(struct output *, const char *, va_list);
-int xwrite(int, char *, int);
-int xioctl(int, unsigned long, char *);
+#endif
+int xwrite(int, const void *, size_t);
+#ifdef notyet
+#ifdef USE_GLIBC_STDIO
+void initstreams(void);
+void openmemout(void);
+int __closememout(void);
+#endif
+#endif
 
-#define outc(c, file)	(--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c)))
-#define out1c(c)	outc(c, out1);
-#define out2c(c)	outc(c, out2);
+static inline void
+freestdout()
+{
+	output.nextc = output.buf;
+	output.flags = 0;
+}
+
+#define OUTPUT_ERR 01		/* error occurred on output */
+
+#ifdef USE_GLIBC_STDIO
+#define outc(c, o)	putc((c), (o)->stream)
+#define doformat(d, f, a)	vfprintf((d)->stream, (f), (a))
+#else
+#define outc(c, file)	((file)->nextc == (file)->end ? outcslow((c), (file)) : (*(file)->nextc = (c), (file)->nextc++))
+#endif
+#define out1c(c)	outc((c), out1)
+#define out2c(c)	outcslow((c), out2)
+#define out1str(s)	outstr((s), out1)
+#define out2str(s)	outstr((s), out2)
+#define outerr(f)	(f)->flags
 
 #define OUTPUT_INCL
 #endif
diff --git a/ash/parser.c b/dash/parser.c
similarity index 75%
rename from ash/parser.c
rename to dash/parser.c
index cb7e7c1..c62a950 100644
--- a/ash/parser.c
+++ b/dash/parser.c
@@ -1,8 +1,8 @@
-/*	$NetBSD: parser.c,v 1.55 2003/08/07 09:05:37 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -32,28 +32,14 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)parser.c	8.7 (Berkeley) 5/16/95";
-#else
-__RCSID("$NetBSD: parser.c,v 1.55 2003/08/07 09:05:37 agc Exp $");
-#endif
-#endif /* not lint */
-
 #include <stdlib.h>
 
 #include "shell.h"
 #include "parser.h"
 #include "nodes.h"
 #include "expand.h"	/* defines rmescapes() */
-#include "eval.h"	/* defines commandname */
 #include "redir.h"	/* defines copyfd() */
+#include "exec.h"	/* defines find_builtin() */
 #include "syntax.h"
 #include "options.h"
 #include "input.h"
@@ -64,7 +50,8 @@
 #include "mystring.h"
 #include "alias.h"
 #include "show.h"
-#ifdef KLIBC_SH_HISTORY
+#include "builtins.h"
+#ifndef SMALL
 #include "myhistedit.h"
 #endif
 
@@ -77,8 +64,6 @@
 /* values returned by readtoken */
 #include "token.h"
 
-#define OPENBRACE '{'
-#define CLOSEBRACE '}'
 
 
 struct heredoc {
@@ -90,7 +75,6 @@
 
 
 
-static int noalias = 0;		/* when set, don't handle aliases */
 struct heredoc *heredoclist;	/* list of here documents to read */
 int parsebackquote;		/* nonzero if we are inside backquotes */
 int doprompt;			/* if set, prompt the user */
@@ -98,7 +82,7 @@
 int lasttoken;			/* last token read */
 MKINIT int tokpushback;		/* last token pushed back */
 char *wordtext;			/* text of last word returned by readtoken */
-MKINIT int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */
+int checkkwd;
 struct nodelist *backquotelist;
 union node *redirnode;
 struct heredoc *heredoc;
@@ -110,7 +94,7 @@
 STATIC union node *andor(void);
 STATIC union node *pipeline(void);
 STATIC union node *command(void);
-STATIC union node *simplecmd(union node **, union node *);
+STATIC union node *simplecmd(void);
 STATIC union node *makename(void);
 STATIC void parsefname(void);
 STATIC void parseheredoc(void);
@@ -124,6 +108,16 @@
 STATIC void setprompt(int);
 
 
+static inline int
+isassignment(const char *p)
+{
+	const char *q = endofname(p);
+	if (p == q)
+		return 0;
+	return *q == '=';
+}
+
+
 /*
  * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
  * valid parse tree indicating a blank line.)
@@ -137,9 +131,7 @@
 	tokpushback = 0;
 	doprompt = interact;
 	if (doprompt)
-		setprompt(1);
-	else
-		setprompt(0);
+		setprompt(doprompt);
 	needprompt = 0;
 	t = readtoken();
 	if (t == TEOF)
@@ -157,24 +149,24 @@
 	union node *n1, *n2, *n3;
 	int tok;
 
-	checkkwd = 2;
-	if (nlflag == 0 && tokendlist[peektoken()])
+	checkkwd = CHKNL | CHKKWD | CHKALIAS;
+	if (nlflag == 2 && tokendlist[peektoken()])
 		return NULL;
 	n1 = NULL;
 	for (;;) {
 		n2 = andor();
 		tok = readtoken();
 		if (tok == TBACKGND) {
-			if (n2->type == NCMD || n2->type == NPIPE) {
-				n2->ncmd.backgnd = 1;
-			} else if (n2->type == NREDIR) {
-				n2->type = NBACKGND;
+			if (n2->type == NPIPE) {
+				n2->npipe.backgnd = 1;
 			} else {
-				n3 = (union node *)stalloc(sizeof (struct nredir));
-				n3->type = NBACKGND;
-				n3->nredir.n = n2;
-				n3->nredir.redirect = NULL;
-				n2 = n3;
+				if (n2->type != NREDIR) {
+					n3 = stalloc(sizeof(struct nredir));
+					n3->nredir.n = n2;
+					n3->nredir.redirect = NULL;
+					n2 = n3;
+				}
+				n2->type = NBACKGND;
 			}
 		}
 		if (n1 == NULL) {
@@ -195,12 +187,12 @@
 		case TNL:
 			if (tok == TNL) {
 				parseheredoc();
-				if (nlflag)
+				if (nlflag == 1)
 					return n1;
 			} else {
 				tokpushback++;
 			}
-			checkkwd = 2;
+			checkkwd = CHKNL | CHKKWD | CHKALIAS;
 			if (tokendlist[peektoken()])
 				return n1;
 			break;
@@ -211,7 +203,7 @@
 				pungetc();		/* push back EOF on input */
 			return n1;
 		default:
-			if (nlflag)
+			if (nlflag == 1)
 				synexpect(-1);
 			tokpushback++;
 			return n1;
@@ -237,6 +229,7 @@
 			tokpushback++;
 			return n1;
 		}
+		checkkwd = CHKNL | CHKKWD | CHKALIAS;
 		n2 = pipeline();
 		n3 = (union node *)stalloc(sizeof (struct nbinary));
 		n3->type = t;
@@ -257,9 +250,11 @@
 
 	negate = 0;
 	TRACE(("pipeline: entered\n"));
-	while (readtoken() == TNOT)
+	if (readtoken() == TNOT) {
 		negate = !negate;
-	tokpushback++;
+		checkkwd = CHKKWD | CHKALIAS;
+	} else
+		tokpushback++;
 	n1 = command();
 	if (readtoken() == TPIPE) {
 		pipenode = (union node *)stalloc(sizeof (struct npipe));
@@ -271,6 +266,7 @@
 		do {
 			prev = lp;
 			lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
+			checkkwd = CHKNL | CHKKWD | CHKALIAS;
 			lp->n = command();
 			prev->next = lp;
 		} while (readtoken() == TPIPE);
@@ -296,28 +292,16 @@
 	union node *ap, **app;
 	union node *cp, **cpp;
 	union node *redir, **rpp;
-	int t, negate = 0;
+	union node **rpp2;
+	int t;
 
-	checkkwd = 2;
 	redir = NULL;
-	n1 = NULL;
-	rpp = &redir;
-
-	/* Check for redirection which may precede command */
-	while (readtoken() == TREDIR) {
-		*rpp = n2 = redirnode;
-		rpp = &n2->nfile.next;
-		parsefname();
-	}
-	tokpushback++;
-
-	while (readtoken() == TNOT) {
-		TRACE(("command: TNOT recognized\n"));
-		negate = !negate;
-	}
-	tokpushback++;
+	rpp2 = &redir;
 
 	switch (readtoken()) {
+	default:
+		synexpect(-1);
+		/* NOTREACHED */
 	case TIF:
 		n1 = (union node *)stalloc(sizeof (struct nif));
 		n1->type = NIF;
@@ -341,9 +325,7 @@
 			n2->nif.elsepart = NULL;
 			tokpushback++;
 		}
-		if (readtoken() != TFI)
-			synexpect(TFI);
-		checkkwd = 1;
+		t = TFI;
 		break;
 	case TWHILE:
 	case TUNTIL: {
@@ -356,9 +338,7 @@
 			synexpect(TDO);
 		}
 		n1->nbinary.ch2 = list(0);
-		if (readtoken() != TDONE)
-			synexpect(TDONE);
-		checkkwd = 1;
+		t = TDONE;
 		break;
 	}
 	case TFOR:
@@ -367,7 +347,8 @@
 		n1 = (union node *)stalloc(sizeof (struct nfor));
 		n1->type = NFOR;
 		n1->nfor.var = wordtext;
-		if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
+		checkkwd = CHKKWD | CHKALIAS;
+		if (readtoken() == TIN) {
 			app = &ap;
 			while (readtoken() == TWORD) {
 				n2 = (union node *)stalloc(sizeof (struct narg));
@@ -382,11 +363,9 @@
 			if (lasttoken != TNL && lasttoken != TSEMI)
 				synexpect(-1);
 		} else {
-			static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
-								   '@', '=', '\0'};
 			n2 = (union node *)stalloc(sizeof (struct narg));
 			n2->type = NARG;
-			n2->narg.text = argvars;
+			n2->narg.text = (char *)dolatstr;
 			n2->narg.backquote = NULL;
 			n2->narg.next = NULL;
 			n1->nfor.args = n2;
@@ -397,17 +376,11 @@
 			if (lasttoken != TNL && lasttoken != TSEMI)
 				tokpushback++;
 		}
-		checkkwd = 2;
-		if ((t = readtoken()) == TDO)
-			t = TDONE;
-		else if (t == TBEGIN)
-			t = TEND;
-		else
-			synexpect(-1);
+		checkkwd = CHKNL | CHKKWD | CHKALIAS;
+		if (readtoken() != TDO)
+			synexpect(TDO);
 		n1->nfor.body = list(0);
-		if (readtoken() != t)
-			synexpect(t);
-		checkkwd = 1;
+		t = TDONE;
 		break;
 	case TCASE:
 		n1 = (union node *)stalloc(sizeof (struct ncase));
@@ -419,13 +392,18 @@
 		n2->narg.text = wordtext;
 		n2->narg.backquote = backquotelist;
 		n2->narg.next = NULL;
-		while (readtoken() == TNL);
-		if (lasttoken != TWORD || ! equal(wordtext, "in"))
-			synerror("expecting \"in\"");
-		cpp = &n1->ncase.cases;
-		noalias = 1;
-		checkkwd = 2, readtoken();
 		do {
+			checkkwd = CHKKWD | CHKALIAS;
+		} while (readtoken() == TNL);
+		if (lasttoken != TIN)
+			synexpect(TIN);
+		cpp = &n1->ncase.cases;
+next_case:
+		checkkwd = CHKNL | CHKKWD;
+		t = readtoken();
+		while(t != TESAC) {
+			if (lasttoken == TLP)
+				readtoken();
 			*cpp = cp = (union node *)stalloc(sizeof (struct nclist));
 			cp->type = NCLIST;
 			app = &cp->nclist.pattern;
@@ -434,73 +412,52 @@
 				ap->type = NARG;
 				ap->narg.text = wordtext;
 				ap->narg.backquote = backquotelist;
-				if (checkkwd = 2, readtoken() != TPIPE)
+				if (readtoken() != TPIPE)
 					break;
 				app = &ap->narg.next;
 				readtoken();
 			}
 			ap->narg.next = NULL;
-			noalias = 0;
-			if (lasttoken != TRP) {
+			if (lasttoken != TRP)
 				synexpect(TRP);
-			}
-			cp->nclist.body = list(0);
+			cp->nclist.body = list(2);
 
-			checkkwd = 2;
-			if ((t = readtoken()) != TESAC) {
-				if (t != TENDCASE) {
-					noalias = 0;
-					synexpect(TENDCASE);
-				} else {
-					noalias = 1;
-					checkkwd = 2;
-					readtoken();
-				}
-			}
 			cpp = &cp->nclist.next;
-		} while(lasttoken != TESAC);
-		noalias = 0;
+
+			checkkwd = CHKNL | CHKKWD;
+			if ((t = readtoken()) != TESAC) {
+				if (t != TENDCASE)
+					synexpect(TENDCASE);
+				else
+					goto next_case;
+			}
+		}
 		*cpp = NULL;
-		checkkwd = 1;
-		break;
+		goto redir;
 	case TLP:
 		n1 = (union node *)stalloc(sizeof (struct nredir));
 		n1->type = NSUBSHELL;
 		n1->nredir.n = list(0);
 		n1->nredir.redirect = NULL;
-		if (readtoken() != TRP)
-			synexpect(TRP);
-		checkkwd = 1;
+		t = TRP;
 		break;
 	case TBEGIN:
 		n1 = list(0);
-		if (readtoken() != TEND)
-			synexpect(TEND);
-		checkkwd = 1;
+		t = TEND;
 		break;
-	/* Handle an empty command like other simple commands.  */
-	case TSEMI:
-		/*
-		 * An empty command before a ; doesn't make much sense, and
-		 * should certainly be disallowed in the case of `if ;'.
-		 */
-		if (!redir)
-			synexpect(-1);
-	case TAND:
-	case TOR:
-	case TNL:
-	case TEOF:
 	case TWORD:
-	case TRP:
+	case TREDIR:
 		tokpushback++;
-		n1 = simplecmd(rpp, redir);
-		goto checkneg;
-	default:
-		synexpect(-1);
-		/* NOTREACHED */
+		return simplecmd();
 	}
 
+	if (readtoken() != t)
+		synexpect(t);
+
+redir:
 	/* Now check for redirection which may follow command */
+	checkkwd = CHKKWD | CHKALIAS;
+	rpp = rpp2;
 	while (readtoken() == TREDIR) {
 		*rpp = n2 = redirnode;
 		rpp = &n2->nfile.next;
@@ -518,92 +475,88 @@
 		n1->nredir.redirect = redir;
 	}
 
-checkneg:
-	if (negate) {
-		n2 = (union node *)stalloc(sizeof (struct nnot));
-		n2->type = NNOT;
-		n2->nnot.com = n1;
-		return n2;
-	}
-	else
-		return n1;
+	return n1;
 }
 
 
 STATIC union node *
-simplecmd(union node **rpp, union node *redir)
-{
+simplecmd(void) {
 	union node *args, **app;
-	union node **orig_rpp = rpp;
-	union node *n = NULL, *n2;
-	int negate = 0;
-
-	/* If we don't have any redirections already, then we must reset */
-	/* rpp to be the address of the local redir variable.  */
-	if (redir == 0)
-		rpp = &redir;
+	union node *n = NULL;
+	union node *vars, **vpp;
+	union node **rpp, *redir;
+	int savecheckkwd;
 
 	args = NULL;
 	app = &args;
-	/*
-	 * We save the incoming value, because we need this for shell
-	 * functions.  There can not be a redirect or an argument between
-	 * the function name and the open parenthesis.
-	 */
-	orig_rpp = rpp;
+	vars = NULL;
+	vpp = &vars;
+	redir = NULL;
+	rpp = &redir;
 
-	while (readtoken() == TNOT) {
-		TRACE(("command: TNOT recognized\n"));
-		negate = !negate;
-	}
-	tokpushback++;
-
+	savecheckkwd = CHKALIAS;
 	for (;;) {
-		if (readtoken() == TWORD) {
+		checkkwd = savecheckkwd;
+		switch (readtoken()) {
+		case TWORD:
 			n = (union node *)stalloc(sizeof (struct narg));
 			n->type = NARG;
 			n->narg.text = wordtext;
 			n->narg.backquote = backquotelist;
-			*app = n;
-			app = &n->narg.next;
-		} else if (lasttoken == TREDIR) {
+			if (savecheckkwd && isassignment(wordtext)) {
+				*vpp = n;
+				vpp = &n->narg.next;
+			} else {
+				*app = n;
+				app = &n->narg.next;
+				savecheckkwd = 0;
+			}
+			break;
+		case TREDIR:
 			*rpp = n = redirnode;
 			rpp = &n->nfile.next;
 			parsefname();	/* read name of redirection file */
-		} else if (lasttoken == TLP && app == &args->narg.next
-					    && rpp == orig_rpp) {
-			/* We have a function */
-			if (readtoken() != TRP)
-				synexpect(TRP);
-#ifdef notdef
-			if (! goodname(n->narg.text))
-				synerror("Bad function name");
-#endif
-			n->type = NDEFUN;
-			n->narg.next = command();
-			goto checkneg;
-		} else {
-			tokpushback++;
 			break;
+		case TLP:
+			if (
+				args && app == &args->narg.next &&
+				!vars && !redir
+			) {
+				struct builtincmd *bcmd;
+				const char *name;
+
+				/* We have a function */
+				if (readtoken() != TRP)
+					synexpect(TRP);
+				name = n->narg.text;
+				if (
+					!goodname(name) || (
+						(bcmd = find_builtin(name)) &&
+						bcmd->flags & BUILTIN_SPECIAL
+					)
+				)
+					synerror("Bad function name");
+				n->type = NDEFUN;
+				checkkwd = CHKNL | CHKKWD | CHKALIAS;
+				n->narg.next = command();
+				return n;
+			}
+			/* fall through */
+		default:
+			tokpushback++;
+			goto out;
 		}
 	}
+out:
 	*app = NULL;
+	*vpp = NULL;
 	*rpp = NULL;
 	n = (union node *)stalloc(sizeof (struct ncmd));
 	n->type = NCMD;
-	n->ncmd.backgnd = 0;
 	n->ncmd.args = args;
+	n->ncmd.assign = vars;
 	n->ncmd.redirect = redir;
-
-checkneg:
-	if (negate) {
-		n2 = (union node *)stalloc(sizeof (struct nnot));
-		n2->type = NNOT;
-		n2->nnot.com = n;
-		return n2;
-	}
-	else
-		return n;
+	return n;
 }
 
 STATIC union node *
@@ -654,10 +607,6 @@
 		if (quoteflag == 0)
 			n->type = NXHERE;
 		TRACE(("Here document %d\n", n->type));
-		if (here->striptabs) {
-			while (*wordtext == '\t')
-				wordtext++;
-		}
 		if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
 			synerror("Illegal eof marker for << redirection");
 		rmescapes(wordtext);
@@ -687,12 +636,12 @@
 	struct heredoc *here;
 	union node *n;
 
-	while (heredoclist) {
-		here = heredoclist;
-		heredoclist = here->next;
+	here = heredoclist;
+	heredoclist = 0;
+
+	while (here) {
 		if (needprompt) {
 			setprompt(2);
-			needprompt = 0;
 		}
 		readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
 				here->eofmark, here->striptabs);
@@ -702,6 +651,7 @@
 		n->narg.text = wordtext;
 		n->narg.backquote = backquotelist;
 		here->here->nhere.doc = n;
+		here = here->next;
 	}
 }
 
@@ -719,55 +669,51 @@
 readtoken(void)
 {
 	int t;
-	int savecheckkwd = checkkwd;
 #ifdef DEBUG
 	int alreadyseen = tokpushback;
 #endif
-	struct alias *ap;
 
-	top:
+top:
 	t = xxreadtoken();
 
-	if (checkkwd) {
-		/*
-		 * eat newlines
-		 */
-		if (checkkwd == 2) {
-			checkkwd = 0;
-			while (t == TNL) {
-				parseheredoc();
-				t = xxreadtoken();
-			}
-		} else
-			checkkwd = 0;
-		/*
-		 * check for keywords and aliases
-		 */
-		if (t == TWORD && !quoteflag)
-		{
-			const char *const *pp;
-
-			for (pp = parsekwd; *pp; pp++) {
-				if (**pp == *wordtext && equal(*pp, wordtext))
-				{
-					lasttoken = t = pp - 
-					    parsekwd + KWDOFFSET;
-					TRACE(("keyword %s recognized\n", tokname[t]));
-					goto out;
-				}
-			}
-#ifdef KLIBC_SH_ALIAS
-			if(!noalias &&
-			    (ap = lookupalias(wordtext, 1)) != NULL) {
-				pushstring(ap->val, strlen(ap->val), ap);
-				checkkwd = savecheckkwd;
-				goto top;
-			}
-#endif
+	/*
+	 * eat newlines
+	 */
+	if (checkkwd & CHKNL) {
+		while (t == TNL) {
+			parseheredoc();
+			t = xxreadtoken();
 		}
-out:
-		checkkwd = (t == TNOT) ? savecheckkwd : 0;
 	}
+
+	if (t != TWORD || quoteflag) {
+		goto out;
+	}
+
+	/*
+	 * check for keywords
+	 */
+	if (checkkwd & CHKKWD) {
+		const char *const *pp;
+
+		if ((pp = findkwd(wordtext))) {
+			lasttoken = t = pp - parsekwd + KWDOFFSET;
+			TRACE(("keyword %s recognized\n", tokname[t]));
+			goto out;
+		}
+	}
+
+	if (checkkwd & CHKALIAS) {
+		struct alias *ap;
+		if ((ap = lookupalias(wordtext, 1)) != NULL) {
+			if (*ap->val) {
+				pushstring(ap->val, ap);
+			}
+			goto top;
+		}
+	}
+out:
+	checkkwd = 0;
 #ifdef DEBUG
 	if (!alreadyseen)
 	    TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
@@ -809,15 +755,13 @@
 	}
 	if (needprompt) {
 		setprompt(2);
-		needprompt = 0;
 	}
 	startlinno = plinno;
 	for (;;) {	/* until token or start of word found */
 		c = pgetc_macro();
-		if (c == ' ' || c == '\t')
-			continue;		/* quick check for white space first */
 		switch (c) {
 		case ' ': case '\t':
+		case PEOA:
 			continue;
 		case '#':
 			while ((c = pgetc()) != '\n' && c != PEOF);
@@ -828,8 +772,6 @@
 				startlinno = ++plinno;
 				if (doprompt)
 					setprompt(2);
-				else
-					setprompt(0);
 				continue;
 			}
 			pungetc();
@@ -889,28 +831,6 @@
 #define PARSEBACKQNEW()	{oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
 #define	PARSEARITH()	{goto parsearith; parsearith_return:;}
 
-/*
- * Keep track of nested doublequotes in dblquote and doublequotep.
- * We use dblquote for the first 32 levels, and we expand to a malloc'ed
- * region for levels above that. Usually we never need to malloc.
- * This code assumes that an int is 32 bits. We don't use uint32_t,
- * because the rest of the code does not.
- */
-#define ISDBLQUOTE() ((varnest < 32) ? (dblquote & (1 << varnest)) : \
-    (dblquotep[(varnest / 32) - 1] & (1 << (varnest % 32))))
-
-#define SETDBLQUOTE() \
-    if (varnest < 32) \
-	dblquote |= (1 << varnest); \
-    else \
-	dblquotep[(varnest / 32) - 1] |= (1 << (varnest % 32))
-
-#define CLRDBLQUOTE() \
-    if (varnest < 32) \
-	dblquote &= ~(1 << varnest); \
-    else \
-	dblquotep[(varnest / 32) - 1] &= ~(1 << (varnest % 32))
-
 STATIC int
 readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs)
 {
@@ -920,24 +840,22 @@
 	char line[EOFMARKLEN + 1];
 	struct nodelist *bqlist;
 	int quotef;
-	int *dblquotep = NULL;
-	size_t maxnest = 32;
 	int dblquote;
 	int varnest;	/* levels of variables expansion */
 	int arinest;	/* levels of arithmetic expansion */
 	int parenlevel;	/* levels of parens in arithmetic */
+	int dqvarnest;	/* levels of variables expansion within double quotes */
 	int oldstyle;
 	char const *prevsyntax;	/* syntax before arithmetic */
 #if __GNUC__
 	/* Avoid longjmp clobbering */
-	(void) &maxnest;
-	(void) &dblquotep;
 	(void) &out;
 	(void) &quotef;
 	(void) &dblquote;
 	(void) &varnest;
 	(void) &arinest;
 	(void) &parenlevel;
+	(void) &dqvarnest;
 	(void) &oldstyle;
 	(void) &prevsyntax;
 	(void) &syntax;
@@ -945,14 +863,14 @@
 
 	startlinno = plinno;
 	dblquote = 0;
-	varnest = 0;
-	if (syntax == DQSYNTAX) {
-		SETDBLQUOTE();
-	}
+	if (syntax == DQSYNTAX)
+		dblquote = 1;
 	quotef = 0;
 	bqlist = NULL;
+	varnest = 0;
 	arinest = 0;
 	parenlevel = 0;
+	dqvarnest = 0;
 
 	STARTSTACKSTR(out);
 	loop: {	/* for each line, until end of word */
@@ -968,7 +886,7 @@
 #endif
 		CHECKEND();	/* set c to PEOF if at end of here document */
 		for (;;) {	/* until end of line or end of word */
-			CHECKSTRSPACE(3, out);	/* permit 3 calls to USTPUTC */
+			CHECKSTRSPACE(4, out);	/* permit 4 calls to USTPUTC */
 			switch(syntax[c]) {
 			case CNL:	/* '\n' */
 				if (syntax == BASESYNTAX)
@@ -977,82 +895,76 @@
 				plinno++;
 				if (doprompt)
 					setprompt(2);
-				else
-					setprompt(0);
 				c = pgetc();
 				goto loop;		/* continue outer loop */
 			case CWORD:
 				USTPUTC(c, out);
 				break;
 			case CCTL:
-				if (eofmark == NULL || ISDBLQUOTE())
+				if (eofmark == NULL || dblquote)
 					USTPUTC(CTLESC, out);
 				USTPUTC(c, out);
 				break;
 			case CBACK:	/* backslash */
-				c = pgetc();
+				c = pgetc2();
 				if (c == PEOF) {
+					USTPUTC(CTLESC, out);
 					USTPUTC('\\', out);
 					pungetc();
 				} else if (c == '\n') {
 					if (doprompt)
 						setprompt(2);
-					else
-						setprompt(0);
 				} else {
-					if (ISDBLQUOTE() && c != '\\' &&
-					    c != '`' && c != '$' &&
-					    (c != '"' || eofmark != NULL))
+					if (
+						dblquote &&
+						c != '\\' && c != '`' &&
+						c != '$' && (
+							c != '"' ||
+							eofmark != NULL
+						)
+					) {
+						USTPUTC(CTLESC, out);
 						USTPUTC('\\', out);
+					}
 					if (SQSYNTAX[c] == CCTL)
 						USTPUTC(CTLESC, out);
-					else if (eofmark == NULL)
-						USTPUTC(CTLQUOTEMARK, out);
 					USTPUTC(c, out);
 					quotef++;
 				}
 				break;
 			case CSQUOTE:
-				if (syntax != SQSYNTAX) {
-				    if (eofmark == NULL)
-					    USTPUTC(CTLQUOTEMARK, out);
-				    syntax = SQSYNTAX;
-				    break;
+				syntax = SQSYNTAX;
+quotemark:
+				if (eofmark == NULL) {
+					USTPUTC(CTLQUOTEMARK, out);
 				}
-				/* FALLTHROUGH */
+				break;
 			case CDQUOTE:
+				syntax = DQSYNTAX;
+				dblquote = 1;
+				goto quotemark;
+			case CENDQUOTE:
 				if (eofmark != NULL && arinest == 0 &&
 				    varnest == 0) {
 					USTPUTC(c, out);
 				} else {
-					if (arinest) {
-						if (c != '"' || ISDBLQUOTE()) {
-							syntax = ARISYNTAX;
-							CLRDBLQUOTE();
-						} else {
-							syntax = DQSYNTAX;
-							SETDBLQUOTE();
-							USTPUTC(CTLQUOTEMARK, out);
-						}
-					} else if (eofmark == NULL) {
-						if (c != '"' || ISDBLQUOTE()) {
-							syntax = BASESYNTAX;
-							CLRDBLQUOTE();
-						} else {
-							syntax = DQSYNTAX;
-							SETDBLQUOTE();
-							USTPUTC(CTLQUOTEMARK, out);
-						}
+					if (dqvarnest == 0) {
+						syntax = BASESYNTAX;
+						dblquote = 0;
 					}
 					quotef++;
+					goto quotemark;
 				}
 				break;
 			case CVAR:	/* '$' */
 				PARSESUB();		/* parse substitution */
 				break;
-			case CENDVAR:	/* CLOSEBRACE */
-				if (varnest > 0 && !ISDBLQUOTE()) {
+			case CENDVAR:	/* '}' */
+				if (varnest > 0) {
 					varnest--;
+					if (dqvarnest > 0) {
+						dqvarnest--;
+					}
 					USTPUTC(CTLENDVAR, out);
 				} else {
 					USTPUTC(c, out);
@@ -1072,9 +984,9 @@
 							USTPUTC(CTLENDARI, out);
 							syntax = prevsyntax;
 							if (syntax == DQSYNTAX)
-								SETDBLQUOTE();
+								dblquote = 1;
 							else
-								CLRDBLQUOTE();
+								dblquote = 0;
 						} else
 							USTPUTC(')', out);
 					} else {
@@ -1092,10 +1004,14 @@
 				break;
 			case CEOF:
 				goto endword;		/* exit outer loop */
+			case CIGN:
+				break;
 			default:
 				if (varnest == 0)
 					goto endword;	/* exit outer loop */
-				USTPUTC(c, out);
+				if (c != PEOA) {
+					USTPUTC(c, out);
+				}
 			}
 			c = pgetc_macro();
 		}
@@ -1111,7 +1027,7 @@
 		synerror("Missing '}'");
 	}
 	USTPUTC('\0', out);
-	len = out - stackblock();
+	len = out - (char *)stackblock();
 	out = stackblock();
 	if (eofmark == NULL) {
 		if ((c == '>' || c == '<')
@@ -1128,8 +1044,6 @@
 	backquotelist = bqlist;
 	grabstackblock(len);
 	wordtext = out;
-	if (dblquotep != NULL)
-	    ckfree(dblquotep);
 	return lasttoken = TWORD;
 /* end of readtoken routine */
 
@@ -1143,9 +1057,13 @@
 
 checkend: {
 	if (eofmark) {
+		if (c == PEOA) {
+			c = pgetc2();
+		}
 		if (striptabs) {
-			while (c == '\t')
-				c = pgetc();
+			while (c == '\t') {
+				c = pgetc2();
+			}
 		}
 		if (c == *eofmark) {
 			if (pfgets(line, sizeof line) != NULL) {
@@ -1158,7 +1076,7 @@
 					plinno++;
 					needprompt = doprompt;
 				} else {
-					pushstring(line, strlen(line), NULL);
+					pushstring(line, NULL);
 				}
 			}
 		}
@@ -1244,7 +1162,10 @@
 	static const char types[] = "}-+?=";
 
 	c = pgetc();
-	if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) {
+	if (
+		c <= PEOA  ||
+		(c != '(' && c != '{' && !is_name(c) && !is_special(c))
+	) {
 		USTPUTC('$', out);
 		pungetc();
 	} else if (c == '(') {	/* $(command) or $((arith)) */
@@ -1256,13 +1177,13 @@
 		}
 	} else {
 		USTPUTC(CTLVAR, out);
-		typeloc = out - stackblock();
+		typeloc = out - (char *)stackblock();
 		USTPUTC(VSNORMAL, out);
 		subtype = VSNORMAL;
-		if (c == OPENBRACE) {
+		if (c == '{') {
 			c = pgetc();
 			if (c == '#') {
-				if ((c = pgetc()) == CLOSEBRACE)
+				if ((c = pgetc()) == '}')
 					c = '#';
 				else
 					subtype = VSLENGTH;
@@ -1270,14 +1191,14 @@
 			else
 				subtype = 0;
 		}
-		if (is_name(c)) {
+		if (c > PEOA && is_name(c)) {
 			do {
 				STPUTC(c, out);
 				c = pgetc();
-			} while (is_in_name(c));
+			} while (c > PEOA && is_in_name(c));
 		} else if (is_digit(c)) {
 			do {
-				USTPUTC(c, out);
+				STPUTC(c, out);
 				c = pgetc();
 			} while (is_digit(c));
 		}
@@ -1319,15 +1240,13 @@
 		} else {
 			pungetc();
 		}
-		if (ISDBLQUOTE() || arinest)
+		if (dblquote || arinest)
 			flags |= VSQUOTE;
-		*(stackblock() + typeloc) = subtype | flags;
+		*((char *)stackblock() + typeloc) = subtype | flags;
 		if (subtype != VSNORMAL) {
 			varnest++;
-			if (varnest >= maxnest) {
-				dblquotep = ckrealloc(dblquotep, maxnest / 8);
-				dblquotep[(maxnest / 32) - 1] = 0;
-				maxnest += 32;
+			if (dblquote || arinest) {
+				dqvarnest++;
 			}
 		}
 	}
@@ -1349,7 +1268,7 @@
 	char *volatile str;
 	struct jmploc jmploc;
 	struct jmploc *volatile savehandler;
-	int savelen;
+	size_t savelen;
 	int saveprompt;
 #ifdef __GNUC__
 	(void) &saveprompt;
@@ -1365,7 +1284,7 @@
 	}
 	INTOFF;
 	str = NULL;
-	savelen = out - stackblock();
+	savelen = out - (char *)stackblock();
 	if (savelen > 0) {
 		str = ckmalloc(savelen);
 		memcpy(str, stackblock(), savelen);
@@ -1379,7 +1298,7 @@
                    reread it as input, interpreting it normally.  */
                 char *pout;
                 int pc;
-                int psavelen;
+                size_t psavelen;
                 char *pstr;
 
 
@@ -1387,7 +1306,6 @@
 		for (;;) {
 			if (needprompt) {
 				setprompt(2);
-				needprompt = 0;
 			}
 			switch (pc = pgetc()) {
 			case '`':
@@ -1398,8 +1316,6 @@
 					plinno++;
 					if (doprompt)
 						setprompt(2);
-					else
-						setprompt(0);
 					/*
 					 * If eating a newline, avoid putting
 					 * the newline into the new character
@@ -1409,20 +1325,23 @@
 					continue;
 				}
                                 if (pc != '\\' && pc != '`' && pc != '$'
-                                    && (!ISDBLQUOTE() || pc != '"'))
+                                    && (!dblquote || pc != '"'))
                                         STPUTC('\\', pout);
-				break;
+				if (pc > PEOA) {
+					break;
+				}
+				/* fall through */
+
+			case PEOF:
+			case PEOA:
+			        startlinno = plinno;
+				synerror("EOF in backquote substitution");
 
 			case '\n':
 				plinno++;
 				needprompt = doprompt;
 				break;
 
-			case PEOF:
-			        startlinno = plinno;
-				synerror("EOF in backquote substitution");
- 				break;
-
 			default:
 				break;
 			}
@@ -1430,10 +1349,10 @@
                 }
 done:
                 STPUTC('\0', pout);
-                psavelen = pout - stackblock();
+                psavelen = pout - (char *)stackblock();
                 if (psavelen > 0) {
 			pstr = grabstackstr(pout);
-			setinputstring(pstr, 1);
+			setinputstring(pstr);
                 }
         }
 	nlpp = &bqlist;
@@ -1448,7 +1367,7 @@
 		doprompt = 0;
 	}
 
-	n = list(0);
+	n = list(2);
 
 	if (oldstyle)
 		doprompt = saveprompt;
@@ -1479,7 +1398,7 @@
 	}
 	parsebackquote = savepbq;
 	handler = savehandler;
-	if (arinest || ISDBLQUOTE())
+	if (arinest || dblquote)
 		USTPUTC(CTLBACKQ | CTLQUOTE, out);
 	else
 		USTPUTC(CTLBACKQ, out);
@@ -1498,7 +1417,7 @@
 		prevsyntax = syntax;
 		syntax = ARISYNTAX;
 		USTPUTC(CTLARI, out);
-		if (ISDBLQUOTE())
+		if (dblquote)
 			USTPUTC('"',out);
 		else
 			USTPUTC(' ',out);
@@ -1517,6 +1436,7 @@
 
 
 #ifdef mkinit
+INCLUDE "parser.h"
 RESET {
 	tokpushback = 0;
 	checkkwd = 0;
@@ -1548,23 +1468,23 @@
 
 
 /*
- * Return true if the argument is a legal variable name (a letter or
- * underscore followed by zero or more letters, underscores, and digits).
+ * Return of a legal variable name (a letter or underscore followed by zero or
+ * more letters, underscores, and digits).
  */
 
-int
-goodname(char *name)
+char *
+endofname(const char *name)
 	{
 	char *p;
 
-	p = name;
+	p = (char *) name;
 	if (! is_name(*p))
-		return 0;
+		return p;
 	while (*++p) {
 		if (! is_in_name(*p))
-			return 0;
+			break;
 	}
-	return 1;
+	return p;
 }
 
 
@@ -1593,22 +1513,49 @@
 STATIC void
 synerror(const char *msg)
 {
-	if (commandname)
-		outfmt(&errout, "%s: %d: ", commandname, startlinno);
-	outfmt(&errout, "Syntax error: %s\n", msg);
-	error((char *)NULL);
+	sh_error("Syntax error: %s", msg);
 	/* NOTREACHED */
 }
 
 STATIC void
 setprompt(int which)
 {
+	struct stackmark smark;
+	int show;
+
+	needprompt = 0;
 	whichprompt = which;
 
-#ifdef KLIBC_SH_HISTORY
-	if (!el)
+#ifdef SMALL
+	show = 1;
+#else
+	show = !el;
 #endif
+	if (show) {
+		setstackmark(&smark);
+		stalloc(stackblocksize());
 		out2str(getprompt(NULL));
+		popstackmark(&smark);
+	}
+}
+
+const char *
+expandstr(const char *ps)
+{
+	union node n;
+
+	/* XXX Fix (char *) cast. */
+	setinputstring((char *)ps);
+	readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
+	popfile();
+
+	n.narg.type = NARG;
+	n.narg.next = NULL;
+	n.narg.text = wordtext;
+	n.narg.backquote = backquotelist;
+
+	expandarg(&n, NULL, 0);
+	return stackblock();
 }
 
 /*
@@ -1617,15 +1564,31 @@
  */
 const char *
 getprompt(void *unused)
-	{
+{
+	const char *prompt;
+
 	switch (whichprompt) {
-	case 0:
-		return "";
-	case 1:
-		return ps1val();
-	case 2:
-		return ps2val();
 	default:
+#ifdef DEBUG
 		return "<internal prompt error>";
+#endif
+	case 0:
+		return nullstr;
+	case 1:
+		prompt = ps1val();
+		break;
+	case 2:
+		prompt = ps2val();
+		break;
 	}
+
+	return expandstr(prompt);
+}
+
+const char *const *
+findkwd(const char *s)
+{
+	return findstring(
+		s, parsekwd, sizeof(parsekwd) / sizeof(const char *)
+	);
 }
diff --git a/ash/parser.h b/dash/parser.h
similarity index 82%
rename from ash/parser.h
rename to dash/parser.h
index 5181cec..fa58ed7 100644
--- a/ash/parser.h
+++ b/dash/parser.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: parser.h,v 1.16 2003/08/07 09:05:37 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -58,12 +58,17 @@
 #define VSPLUS		0x3		/* ${var+text} */
 #define VSQUESTION	0x4		/* ${var?message} */
 #define VSASSIGN	0x5		/* ${var=text} */
-#define VSTRIMLEFT	0x6		/* ${var#pattern} */
-#define VSTRIMLEFTMAX	0x7		/* ${var##pattern} */
-#define VSTRIMRIGHT	0x8		/* ${var%pattern} */
-#define VSTRIMRIGHTMAX 	0x9		/* ${var%%pattern} */
+#define VSTRIMRIGHT	0x6		/* ${var%pattern} */
+#define VSTRIMRIGHTMAX 	0x7		/* ${var%%pattern} */
+#define VSTRIMLEFT	0x8		/* ${var#pattern} */
+#define VSTRIMLEFTMAX	0x9		/* ${var##pattern} */
 #define VSLENGTH	0xa		/* ${#var} */
 
+/* values of checkkwd variable */
+#define CHKALIAS	0x1
+#define CHKKWD		0x2
+#define CHKNL		0x4
+
 
 /*
  * NEOF is returned by parsecmd when it encounters an end of file.  It
@@ -73,9 +78,19 @@
 extern int tokpushback;
 #define NEOF ((union node *)&tokpushback)
 extern int whichprompt;		/* 1 == PS1, 2 == PS2 */
+extern int checkkwd;
+extern int startlinno;		/* line # where last token started */
 
 
 union node *parsecmd(int);
 void fixredir(union node *, const char *, int);
-int goodname(char *);
 const char *getprompt(void *);
+const char *const *findkwd(const char *);
+char *endofname(const char *);
+const char *expandstr(const char *);
+
+static inline int
+goodname(const char *p)
+{
+	return !*endofname(p);
+}
diff --git a/dash/redir.c b/dash/redir.c
new file mode 100644
index 0000000..2bd8e9f
--- /dev/null
+++ b/dash/redir.c
@@ -0,0 +1,475 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>	/* PIPE_BUF */
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/*
+ * Code for dealing with input/output redirection.
+ */
+
+#include "main.h"
+#include "shell.h"
+#include "nodes.h"
+#include "jobs.h"
+#include "options.h"
+#include "expand.h"
+#include "redir.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+
+
+#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
+
+
+MKINIT
+struct redirtab {
+	struct redirtab *next;
+	int renamed[10];
+	int nullredirs;
+};
+
+
+MKINIT struct redirtab *redirlist;
+MKINIT int nullredirs;
+
+STATIC int openredirect(union node *);
+#ifdef notyet
+STATIC void dupredirect(union node *, int, char[10]);
+#else
+STATIC void dupredirect(union node *, int);
+#endif
+STATIC int openhere(union node *);
+STATIC int noclobberopen(const char *);
+
+
+/*
+ * 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.
+ */
+
+void
+redirect(union node *redir, int flags)
+{
+	union node *n;
+	struct redirtab *sv;
+	int i;
+	int fd;
+	int newfd;
+	int *p;
+#if notyet
+	char memory[10];	/* file descriptors to write to memory */
+
+	for (i = 10 ; --i >= 0 ; )
+		memory[i] = 0;
+	memory[1] = flags & REDIR_BACKQ;
+#endif
+	nullredirs++;
+	if (!redir) {
+		return;
+	}
+	sv = NULL;
+	INTOFF;
+	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) {
+			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
+		dupredirect(n, newfd);
+#endif
+	} while ((n = n->nfile.next));
+	INTON;
+#ifdef notyet
+	if (memory[1])
+		out1 = &memout;
+	if (memory[2])
+		out2 = &memout;
+#endif
+	if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
+		preverrout.fd = sv->renamed[2];
+}
+
+
+STATIC int
+openredirect(union node *redir)
+{
+	char *fname;
+	int f;
+
+	switch (redir->nfile.type) {
+	case NFROM:
+		fname = redir->nfile.expfname;
+		if ((f = open64(fname, O_RDONLY)) < 0)
+			goto eopen;
+		break;
+	case NFROMTO:
+		fname = redir->nfile.expfname;
+		if ((f = open64(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
+			goto ecreate;
+		break;
+	case NTO:
+		/* Take care of noclobber mode. */
+		if (Cflag) {
+			fname = redir->nfile.expfname;
+			if ((f = noclobberopen(fname)) < 0)
+				goto ecreate;
+			break;
+		}
+		/* FALLTHROUGH */
+	case NCLOBBER:
+		fname = redir->nfile.expfname;
+		if ((f = open64(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+			goto ecreate;
+		break;
+	case NAPPEND:
+		fname = redir->nfile.expfname;
+		if ((f = open64(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
+			goto ecreate;
+		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);
+		break;
+	}
+
+	return f;
+ecreate:
+	sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+eopen:
+	sh_error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
+}
+
+
+STATIC void
+#ifdef notyet
+dupredirect(redir, f, memory)
+#else
+dupredirect(redir, f)
+#endif
+	union node *redir;
+	int f;
+#ifdef notyet
+	char memory[10];
+#endif
+	{
+	int fd = redir->nfile.fd;
+
+#ifdef notyet
+	memory[fd] = 0;
+#endif
+	if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
+		if (redir->ndup.dupfd >= 0) {	/* if not ">&-" */
+#ifdef notyet
+			if (memory[redir->ndup.dupfd])
+				memory[fd] = 1;
+			else
+#endif
+				copyfd(redir->ndup.dupfd, fd);
+		}
+		return;
+	}
+
+	if (f != fd) {
+		copyfd(f, fd);
+		close(f);
+	}
+	return;
+}
+
+
+/*
+ * 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)
+		sh_error("Pipe call failed");
+	if (redir->type == NHERE) {
+		len = strlen(redir->nhere.doc->narg.text);
+		if (len <= PIPESIZE) {
+			xwrite(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)
+			xwrite(pip[1], redir->nhere.doc->narg.text, len);
+		else
+			expandhere(redir->nhere.doc, pip[1]);
+		_exit(0);
+	}
+out:
+	close(pip[1]);
+	return pip[0];
+}
+
+
+
+/*
+ * Undo the effects of the last redirection.
+ */
+
+void
+popredir(int drop)
+{
+	struct redirtab *rp;
+	int i;
+
+	if (--nullredirs >= 0)
+		return;
+	INTOFF;
+	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;
+	ckfree(rp);
+	INTON;
+}
+
+/*
+ * Undo all redirections.  Called on error or interrupt.
+ */
+
+#ifdef mkinit
+
+INCLUDE "redir.h"
+
+RESET {
+	clearredir(0);
+}
+
+#endif
+
+/*
+ * Discard all saved file descriptors.
+ */
+
+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.
+ */
+
+int
+copyfd(int from, int to)
+{
+	int newfd;
+
+	newfd = fcntl(from, F_DUPFD, to);
+	if (newfd < 0) {
+		int errno2 = errno;
+		if (errno2 == EMFILE)
+			return EMPTY;
+		else
+			sh_error("%d: %s", from, strerror(errno2));
+	}
+	return newfd;
+}
+
+
+/*
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
+ */
+int
+noclobberopen(fname)
+	const char *fname;
+{
+	int r, fd;
+	struct stat64 finfo, finfo2;
+
+	/*
+	 * If the file exists and is a regular file, return an error
+	 * immediately.
+	 */
+	r = stat64(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 open64(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
+	fd = open64(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 (fstat64(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;
+}
+
+
+int
+redirectsafe(union node *redir, int flags)
+{
+	int err;
+	volatile int saveint;
+	struct jmploc *volatile savehandler = handler;
+	struct jmploc jmploc;
+
+	SAVEINT(saveint);
+	if (!(err = setjmp(jmploc.loc) * 2)) {
+		handler = &jmploc;
+		redirect(redir, flags);
+	}
+	handler = savehandler;
+	if (err && exception != EXERROR)
+		longjmp(handler->loc, 1);
+	RESTOREINT(saveint);
+	return err;
+}
diff --git a/ash/redir.h b/dash/redir.h
similarity index 89%
rename from ash/redir.h
rename to dash/redir.h
index c9709e9..f4347ce 100644
--- a/ash/redir.h
+++ b/dash/redir.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: redir.h,v 1.15 2003/08/07 09:05:37 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -36,13 +36,15 @@
 
 /* flags passed to redirect */
 #define REDIR_PUSH 01		/* save previous values of file descriptors */
+#ifdef notyet
 #define REDIR_BACKQ 02		/* save the command output in memory */
-#define REDIR_VFORK 04		/* running under vfork(2), be careful */
+#endif
+#define REDIR_SAVEFD2 03	/* set preverrout */
 
 union node;
 void redirect(union node *, int);
-void popredir(void);
-int fd0_redirected_p(void);
+void popredir(int);
 void clearredir(int);
 int copyfd(int, int);
+int redirectsafe(union node *, int);
 
diff --git a/ash/sh.1 b/dash/sh.1
similarity index 77%
rename from ash/sh.1
rename to dash/sh.1
index 3ef55b4..00f40bd 100644
--- a/ash/sh.1
+++ b/dash/sh.1
@@ -1,6 +1,7 @@
-.\"	$NetBSD: sh.1,v 1.78 2004/06/03 19:54:37 hubertf Exp $
 .\" Copyright (c) 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
+.\" Copyright (c) 1997-2005
+.\"	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
 .\"
 .\" This code is derived from software contributed to Berkeley by
 .\" Kenneth Almquist.
@@ -31,7 +32,7 @@
 .\"
 .\"	@(#)sh.1	8.6 (Berkeley) 5/4/95
 .\"
-.Dd April 17, 2004
+.Dd January 19, 2003
 .Os
 .Dt SH 1
 .Sh NAME
@@ -92,9 +93,9 @@
 Only features designated by
 .Tn POSIX ,
 plus a few Berkeley extensions, are being incorporated into this shell.
-.\" We expect
-.\" .Tn POSIX
-.\" conformance by the time 4.4 BSD is released.
+We expect
+.Tn POSIX
+conformance by the time 4.4 BSD is released.
 This man page is not intended
 to be a tutorial or a complete specification of the shell.
 .Ss Overview
@@ -139,7 +140,7 @@
 if they exist.
 If the environment variable
 .Ev ENV
-is set on entry to a shell, or is set in the
+is set on entry to an interactive shell, or is set in the
 .Pa .profile
 of a login shell, the shell next reads
 commands from the file named in
@@ -147,7 +148,7 @@
 Therefore, a user should place commands that are to be executed only at
 login time in the
 .Pa .profile
-file, and commands that are executed for every shell inside the
+file, and commands that are executed for every interactive shell inside the
 .Ev ENV
 file.
 To set the
@@ -161,31 +162,6 @@
 substituting for
 .Dq .shinit
 any filename you wish.
-Since the
-.Ev ENV
-file is read for every invocation of the shell, including shell scripts
-and non-interactive shells, the following paradigm is useful for
-restricting commands in the
-.Ev ENV
-file to interactive invocations.
-Place commands within the
-.Dq case
-and
-.Dq esac
-below (these commands are described later):
-.Pp
-.Bl -item -compact -offset indent
-.It
-.Li case $- in *i*)
-.Bl -item -compact -offset indent
-.It
-.Li # commands for interactive use only
-.It
-.Li ...
-.El
-.It
-.Li esac
-.El
 .Pp
 If command line arguments besides the options have been specified, then
 the shell treats the first argument as the name of a file from which to
@@ -235,7 +211,7 @@
 or
 .Ic until ;
 or if the command is the left hand operand of an
-.Dq \*[Am]\*[Am]
+.Dq &&
 or
 .Dq ||
 operator.
@@ -255,18 +231,6 @@
 .Sq +\  )
 before it is executed.
 Useful for debugging.
-.It Fl q Em quietprofile
-If the
-.Fl v
-or
-.Fl x
-options have been set, do not apply them when reading
-initialization files, these being
-.Pa /etc/profile ,
-.Pa .profile ,
-and the file specified by the
-.Ev ENV
-environment variable.
 .It Fl I Em ignoreeof
 Ignore EOF's from input when interactive.
 .It Fl i Em interactive
@@ -285,25 +249,15 @@
 command line editor (disables
 .Fl E
 if it has been set).
-(See the
-.Sx Command Line Editing
-section below.)
 .It Fl E Em emacs
-Enable the built-in emacs style
+Enable the built-in
+.Xr emacs 1
 command line editor (disables
 .Fl V
 if it has been set).
-(See the
-.Sx Command Line Editing
-section below.)
 .It Fl b Em notify
 Enable asynchronous notification of background job completion.
 (UNIMPLEMENTED for 4.4alpha)
-.It "\ \ " Em cdprint
-Make an interactive shell always print the new directory name when
-changed by the
-.Ic cd
-command.
 .El
 .Ss Lexical Structure
 The shell reads input in terms of lines from a file and breaks it up into
@@ -315,9 +269,9 @@
 Following is a list of operators:
 .Bl -ohang -offset indent
 .It "Control operators:"
-.Dl \*[Am]  \*[Am]\*[Am]  \&(  \&)  \&;  ;; | || \*[Lt]newline\*[Gt]
+.Dl &  &&  \&(  \&)  \&;  ;; | || \*[Lt]newline\*[Gt]
 .It "Redirection operators:"
-.Dl \*[Lt]  \*[Gt]  \*[Gt]|  \*[Lt]\*[Lt]  \*[Gt]\*[Gt]  \*[Lt]\*[Am]  \*[Gt]\*[Am]  \*[Lt]\*[Lt]-  \*[Lt]\*[Gt]
+.Dl \*[Lt]  \*[Gt]  \*[Gt]|  \*[Lt]\*[Lt]  \*[Gt]\*[Gt]  \*[Lt]&  \*[Gt]&  \*[Lt]\*[Lt]-  \*[Lt]\*[Gt]
 .El
 .Ss Quoting
 Quoting is used to remove the special meaning of certain characters or
@@ -361,7 +315,7 @@
 Their meaning is discussed later.
 .Ss Aliases
 An alias is a name and corresponding value set using the
-.Ic alias
+.Xr alias 1
 builtin command.
 Whenever a reserved word may occur (see above),
 and after checking for reserved words, the shell
@@ -435,7 +389,7 @@
 is an optional number, as in
 .Sq 3
 (not
-.Sq Bq 3 ) ,
+.Sq Bq 3 ,
 that refers to a file descriptor.
 .Bl -tag -width aaabsfiles -offset indent
 .It [n] Ns \*[Gt] file
@@ -448,13 +402,13 @@
 Append standard output (or n) to file.
 .It [n] Ns \*[Lt] file
 Redirect standard input (or n) from file.
-.It [n1] Ns \*[Lt]\*[Am] Ns n2
+.It [n1] Ns \*[Lt]& Ns n2
 Duplicate standard input (or n1) from file descriptor n2.
-.It [n] Ns \*[Lt]\*[Am]-
+.It [n] Ns \*[Lt]&-
 Close standard input (or n).
-.It [n1] Ns \*[Gt]\*[Am] Ns n2
+.It [n1] Ns \*[Gt]& Ns n2
 Duplicate standard output (or n1) to n2.
-.It [n] Ns \*[Gt]\*[Am]-
+.It [n] Ns \*[Gt]&-
 Close standard output (or n).
 .It [n] Ns \*[Lt]\*[Gt] file
 Open file for reading and writing on standard input (or n).
@@ -544,7 +498,7 @@
 implicitly by an empty directory name, or explicitly by a single period.
 .El
 .Ss Command Exit Status
-Each command has an exit status that can influence the behavior
+Each command has an exit status that can influence the behaviour
 of other shell commands.
 The paradigm is that a command exits
 with zero for normal or success, and non-zero for failure,
@@ -610,7 +564,7 @@
 takes place before redirection, it can be modified by redirection.
 For example:
 .Pp
-.Dl $ command1 2\*[Gt]\*[Am]1 | command2
+.Dl $ command1 2\*[Gt]&1 | command2
 .Pp
 sends both the standard output and standard error of command1
 to the standard input of command2.
@@ -618,21 +572,21 @@
 A ; or
 .Aq newline
 terminator causes the preceding AND-OR-list (described
-next) to be executed sequentially; a \*[Am] causes asynchronous execution of
+next) to be executed sequentially; a & causes asynchronous execution of
 the preceding AND-OR-list.
 .Pp
 Note that unlike some other shells, each process in the pipeline is a
 child of the invoking shell (unless it is a shell builtin, in which case
 it executes in the current shell -- but any effect it has on the
 environment is wiped).
-.Ss Background Commands -- \*[Am]
-If a command is terminated by the control operator ampersand (\*[Am]), the
+.Ss Background Commands -- &
+If a command is terminated by the control operator ampersand (&), the
 shell executes the command asynchronously -- that is, the shell does not
 wait for the command to finish before executing the next command.
 .Pp
 The format for running a command in background is:
 .Pp
-.Dl command1 \*[Am] [command2 \*[Am] ...]
+.Dl command1 & [command2 & ...]
 .Pp
 If the shell is not interactive, the standard input of an asynchronous
 command is set to
@@ -646,26 +600,20 @@
 command and immediately proceed onto the next command; otherwise it waits
 for the command to terminate before proceeding to the next one.
 .Ss Short-Circuit List Operators
-.Dq \*[Am]\*[Am]
+.Dq &&
 and
 .Dq ||
 are AND-OR list operators.
-.Dq \*[Am]\*[Am]
-executes the first command, and then executes the second command if and only
-if the exit status of the first command is zero.
+.Dq &&
+executes the first command, and then executes the second command iff the
+exit status of the first command is zero.
 .Dq ||
-is similar, but executes the second command if and only if the exit status
-of the first command is nonzero.
-.Dq \*[Am]\*[Am]
+is similar, but executes the second command iff the exit status of the first
+command is nonzero.
+.Dq &&
 and
 .Dq ||
 both have the same priority.
-Note that these operators are left-associative, so
-.Dq true || echo bar && echo baz
-writes
-.Dq baz
-and nothing else.
-This is not the way it works in C.
 .Ss Flow-Control Constructs -- if, while, for, case
 The syntax of the if command is
 .Bd -literal -offset indent
@@ -743,7 +691,7 @@
 their output as though they were one program:
 .Pp
 .Bd -literal -offset indent
-{ echo -n \*q hello \*q ; echo \*q world" ; } \*[Gt] greeting
+{ printf \*q hello \*q ; printf \*q world\\n" ; } \*[Gt] greeting
 .Ed
 .Pp
 Note that
@@ -854,7 +802,7 @@
 .Sm on
 .It #
 Expands to the number of positional parameters.
-.It \&?
+.It ?
 Expands to the exit status of the most recent pipeline.
 .It - (Hyphen.)
 Expands to the current option flags (the single-letter
@@ -864,7 +812,7 @@
 .It $
 Expands to the process ID of the invoked shell.
 A subshell retains the same value of $ as its parent.
-.It \&!
+.It !
 Expands to the process ID of the most recent background
 command executed from the current shell.
 For a pipeline, the process ID is that of the last command in the pipeline.
@@ -1066,7 +1014,7 @@
 .Pp
 The shell treats each character of the
 .Ev IFS
-as a delimiter and use the delimiters to split the results of parameter
+as a delimiter and uses the delimiters to split the results of parameter
 expansion and command substitution into fields.
 .Ss Pathname Expansion (File Name Generation)
 Unless the
@@ -1140,6 +1088,7 @@
 etc).
 .Bl -tag -width 5n
 .It :
+.It true
 A null command that returns a 0 (true) exit value.
 .It \&. file
 The commands in the specified file are read and executed by the shell.
@@ -1189,17 +1138,13 @@
 search for the command and print the absolute pathname
 of utilities, the name for builtins or the expansion of aliases.
 .El
-.It cd Op Ar directory Op Ar replace
+.It cd Ar -
+.It Xo cd Op Fl LP
+.Op Ar directory
+.Xc
 Switch to the specified directory (default
-.Ev $HOME ) .
-If
-.Ar replace
-is specified, then the new directory name is generated by replacing
-the first occurrence of
-.Ar directory
-in the current directory name with
-.Ar replace .
-Otherwise if an entry for
+.Ev HOME ) .
+If an entry for
 .Ev CDPATH
 appears in the environment of the
 .Ic cd
@@ -1213,14 +1158,62 @@
 .Ev CDPATH
 is the same as that of
 .Ev PATH .
-In an interactive shell, the
+If a single dash is specified as the argument, it will be replaced by the
+value of
+.Ev OLDPWD .
+The
 .Ic cd
 command will print out the name of the
 directory that it actually switched to if this is different from the name
 that the user gave.
 These may be different either because the
 .Ev CDPATH
-mechanism was used or because a symbolic link was crossed.
+mechanism was used or because the argument is a single dash.
+The
+.Fl P
+option causes the physical directory structure to be used, that is, all
+symbolic links are resolved to their respective values.  The
+.Fl L
+option turns off the effect of any preceding
+.Fl P
+options.
+.It Xo echo Op Fl n
+.Ar args... 
+.Xc
+Print the arguments on the standard output, separated by spaces.
+Unless the
+.Fl n
+option is present, a newline is output following the arguments.
+.Pp
+If any of the following sequences of characters is encountered during
+output, the sequence is not output.  Instead, the specified action is
+performed:
+.Bl -tag -width indent
+.It Li \eb
+A backspace character is output.
+.It Li \ec
+Subsequent output is suppressed.  This is normally used at the end of the
+last argument to suppress the trailing newline that
+.Ic echo
+would otherwise output.
+.It Li \ef
+Output a form feed.
+.It Li \en
+Output a newline character.
+.It Li \er
+Output a carriage return.
+.It Li \et
+Output a (horizontal) tab character.
+.It Li \ev
+Output a vertical tab.
+.It Li \e0 Ns Ar digits
+Output the character whose value is given by zero to three octal digits.
+If there are zero digits, a nul character is output.
+.It Li \e\e
+Output a backslash.
+.El
+.Pp
+All other backslash sequences elicit undefined behaviour.
 .It eval Ar string ...
 Concatenate all the arguments with spaces.
 Then re-parse and execute the command.
@@ -1468,61 +1461,21 @@
 .Fl r
 option causes the hash command to delete all the entries in the hash table
 except for functions.
-.It inputrc Ar file
-Read the
-.Va file
-to set keybindings as defined by
-.Xr editrc 5 .
-.It jobid Op Ar job
-Print the process id's of the processes in the job.
-If the
-.Ar job
-argument is omitted, the current job is used.
-.It jobs
-This command lists out all the background processes
-which are children of the current shell process.
 .It pwd Op Fl LP
-Print the current directory.
-If 
-.Fl L
-is specified the cached value (initially set from
-.Ev PWD )
-is checked to see if it refers to the current directory, if it does
-the value is printed.
-Otherwise the current directory name is found using
-.Xr getcwd(3) .
-The environment variable
-.Ev PWD
-is set to printed value.
-.Pp
-The default is
+builtin command remembers what the current directory
+is rather than recomputing it each time.
+This makes it faster.
+However, if the current directory is renamed, the builtin version of
 .Ic pwd
-.Fl L ,
-but note that the builtin
-.Ic cd
-command doesn't currently support
-.Fl L
-or
+will continue to print the old name for the directory.
+The
 .Fl P
-and will cache (almost) the absolute path.
-If
-.Ic cd
-is changed,
-.Ic pwd
-may be changed to default to
-.Ic pwd
-.Fl P .
-.Pp
-If the current directory is renamed and replaced by a symlink to the
-same directory, or the initial
-.Ev PWD
-value followed a symbolic link, then the cached value may not
-be the absolute path.
-.Pp
-The builtin command may differ from the program of the same name because
-the program will use
-.Ev PWD
-and the builtin uses a separately cached value.
+option causes the physical value of the current working directory to be shown,
+that is, all symbolic links are resolved to their respective values.  The
+.Fl L
+option turns off the effect of any preceding
+.Fl P
+options.
 .It Xo read Op Fl p Ar prompt
 .Op Fl r
 .Ar variable
@@ -1535,6 +1488,7 @@
 The trailing newline is deleted from the
 line and the line is split as described in the section on word splitting
 above, and the pieces are assigned to the variables in order.
+At least one variable must be specified.
 If there are more pieces than variables, the remaining pieces
 (along with the characters in
 .Ev IFS
@@ -1569,6 +1523,248 @@
 .Fl p
 option specified the output will be formatted suitably for non-interactive use.
 .Pp
+.It Xo printf Ar format
+.Op Ar arguments  ...
+.Xc
+.Ic printf
+formats and prints its arguments, after the first, under control
+of the
+.Ar format  .
+The
+.Ar format
+is a character string which contains three types of objects: plain characters,
+which are simply copied to standard output, character escape sequences which
+are converted and copied to the standard output, and format specifications,
+each of which causes printing of the next successive
+.Ar argument  .
+.Pp
+The
+.Ar arguments
+after the first are treated as strings if the corresponding format is
+either
+.Cm b ,
+.Cm c
+or
+.Cm s ;
+otherwise it is evaluated as a C constant, with the following extensions:
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+A leading plus or minus sign is allowed.
+.It
+If the leading character is a single or double quote, the value is the
+.Tn ASCII
+code of the next character.
+.El
+.Pp
+The format string is reused as often as necessary to satisfy the
+.Ar arguments  .
+Any extra format specifications are evaluated with zero or the null
+string.
+.Pp
+Character escape sequences are in backslash notation as defined in
+.St -ansiC .
+The characters and their meanings are as follows:
+.Bl -tag -width Ds -offset indent
+.It Cm \ea
+Write a \*[Lt]bell\*[Gt] character.
+.It Cm \eb
+Write a \*[Lt]backspace\*[Gt] character.
+.It Cm \ef
+Write a \*[Lt]form-feed\*[Gt] character.
+.It Cm \en
+Write a \*[Lt]new-line\*[Gt] character.
+.It Cm \er
+Write a \*[Lt]carriage return\*[Gt] character.
+.It Cm \et
+Write a \*[Lt]tab\*[Gt] character.
+.It Cm \ev
+Write a \*[Lt]vertical tab\*[Gt] character.
+.It Cm \e\e
+Write a backslash character.
+.It Cm \e Ns Ar num
+Write an 8\-bit character whose
+.Tn ASCII
+value is the 1\-, 2\-, or 3\-digit
+octal number
+.Ar num .
+.El
+.Pp
+Each format specification is introduced by the percent character
+(``%'').
+The remainder of the format specification includes,
+in the following order:
+.Bl -tag -width Ds
+.It "Zero or more of the following flags:"
+.Bl -tag -width Ds
+.It Cm #
+A `#' character
+specifying that the value should be printed in an ``alternative form''.
+For
+.Cm b ,
+.Cm c ,
+.Cm d ,
+and
+.Cm s
+formats, this option has no effect.
+For the
+.Cm o
+format the precision of the number is increased to force the first
+character of the output string to a zero.
+For the
+.Cm x
+.Pq Cm X
+format, a non-zero result has the string
+.Li 0x
+.Pq Li 0X
+prepended to it.
+For
+.Cm e  ,
+.Cm E ,
+.Cm f  ,
+.Cm g ,
+and
+.Cm G
+formats, the result will always contain a decimal point, even if no
+digits follow the point (normally, a decimal point only appears in the
+results of those formats if a digit follows the decimal point).
+For
+.Cm g
+and
+.Cm G
+formats, trailing zeros are not removed from the result as they
+would otherwise be.
+.It Cm \&\-
+A minus sign `\-' which specifies
+.Em left adjustment
+of the output in the indicated field;
+.It Cm \&+
+A `+' character specifying that there should always be
+a sign placed before the number when using signed formats.
+.It Sq \&\ \&
+A space specifying that a blank should be left before a positive number
+for a signed format.
+A `+' overrides a space if both are used;
+.It Cm \&0
+A zero `0' character indicating that zero-padding should be used
+rather than blank-padding.
+A `\-' overrides a `0' if both are used;
+.El
+.It "Field Width:"
+An optional digit string specifying a
+.Em field width ;
+if the output string has fewer characters than the field width it will
+be blank-padded on the left (or right, if the left-adjustment indicator
+has been given) to make up the field width (note that a leading zero
+is a flag, but an embedded zero is part of a field width);
+.It Precision :
+An optional period,
+.Sq Cm \&.\& ,
+followed by an optional digit string giving a
+.Em precision
+which specifies the number of digits to appear after the decimal point,
+for
+.Cm e
+and
+.Cm f
+formats, or the maximum number of characters to be printed
+from a string
+.Sm off
+.Pf ( Cm b
+.Sm on
+and
+.Cm s
+formats); if the digit string is missing, the precision is treated
+as zero;
+.It Format :
+A character which indicates the type of format to use (one of
+.Cm diouxXfwEgGbcs ) .
+.El
+.Pp
+A field width or precision may be
+.Sq Cm \&*
+instead of a digit string.
+In this case an
+.Ar argument
+supplies the field width or precision.
+.Pp
+The format characters and their meanings are:
+.Bl -tag -width Fl
+.It Cm diouXx
+The
+.Ar argument
+is printed as a signed decimal (d or i), unsigned octal, unsigned decimal,
+or unsigned hexadecimal (X or x), respectively.
+.It Cm f
+The
+.Ar argument
+is printed in the style
+.Sm off
+.Pf [\-]ddd Cm \&. No ddd
+.Sm on
+where the number of d's
+after the decimal point is equal to the precision specification for
+the argument.
+If the precision is missing, 6 digits are given; if the precision
+is explicitly 0, no digits and no decimal point are printed.
+.It Cm eE
+The
+.Ar argument
+is printed in the style
+.Sm off
+.Pf [\-]d Cm \&. No ddd Cm e No \\*(Pmdd
+.Sm on
+where there
+is one digit before the decimal point and the number after is equal to
+the precision specification for the argument; when the precision is
+missing, 6 digits are produced.
+An upper-case E is used for an `E' format.
+.It Cm gG
+The
+.Ar argument
+is printed in style
+.Cm f
+or in style
+.Cm e
+.Pq Cm E
+whichever gives full precision in minimum space.
+.It Cm b
+Characters from the string
+.Ar argument
+are printed with backslash-escape sequences expanded.
+.br
+The following additional backslash-escape sequences are supported:
+.Bl -tag -width Ds
+.It Cm \ec
+Causes
+.Nm
+to ignore any remaining characters in the string operand containing it,
+any remaining string operands, and any additional characters in
+the format operand.
+.It Cm \e0 Ns Ar num
+Write an 8\-bit character whose
+.Tn ASCII
+value is the 1\-, 2\-, or 3\-digit
+octal number
+.Ar num .
+.El
+.It Cm c
+The first character of
+.Ar argument
+is printed.
+.It Cm s
+Characters from the string
+.Ar argument
+are printed until the end is reached or until the number of characters
+indicated by the precision specification is reached; if the
+precision is omitted, all characters in the string are printed.
+.It Cm \&%
+Print a `%'; no argument is used.
+.El
+.Pp
+In no case does a non-existent or small field width cause truncation of
+a field; padding takes place only if the specified field width exceeds
+the actual width.
 .It Xo set
 .Oo {
 .Fl options | Cm +options | Cm -- }
@@ -1593,15 +1789,6 @@
 If no args are present, the set command
 will clear all the positional parameters (equivalent to executing
 .Dq shift $# . )
-.It setvar Ar variable Ar value
-Assigns value to variable.
-(In general it is better to write
-variable=value rather than using
-.Ic setvar .
-.Ic setvar
-is intended to be used in
-functions that assign values to variables whose names are passed as
-parameters.)
 .It shift Op Ar n
 Shift the positional parameters n times.
 A
@@ -1618,15 +1805,249 @@
 the value of
 .Va $#
 by one.
-If there are zero positional parameters,
+If n is greater than the number of positional parameters,
 .Ic shift
-does nothing.
+will issue an error message, and exit with return status 2.
+.It test Ar expression
+.It \&[ Ar expression Cm ]
+The
+.Ic test
+utility evaluates the expression and, if it evaluates
+to true, returns a zero (true) exit status; otherwise
+it returns 1 (false).
+If there is no expression, test also
+returns 1 (false).
+.Pp
+All operators and flags are separate arguments to the
+.Ic test
+utility.
+.Pp
+The following primaries are used to construct expression:
+.Bl -tag -width Ar
+.It Fl b Ar file
+True if
+.Ar file
+exists and is a block special
+file.
+.It Fl c Ar file
+True if
+.Ar file
+exists and is a character
+special file.
+.It Fl d Ar file
+True if
+.Ar file
+exists and is a directory.
+.It Fl e Ar file
+True if
+.Ar file
+exists (regardless of type).
+.It Fl f Ar file
+True if
+.Ar file
+exists and is a regular file.
+.It Fl g Ar file
+True if
+.Ar file
+exists and its set group ID flag
+is set.
+.It Fl h Ar file
+True if
+.Ar file
+exists and is a symbolic link.
+.It Fl k Ar file
+True if
+.Ar file
+exists and its sticky bit is set.
+.It Fl n Ar string
+True if the length of
+.Ar string
+is nonzero.
+.It Fl p Ar file
+True if
+.Ar file
+is a named pipe
+.Po Tn FIFO Pc .
+.It Fl r Ar file
+True if
+.Ar file
+exists and is readable.
+.It Fl s Ar file
+True if
+.Ar file
+exists and has a size greater
+than zero.
+.It Fl t Ar file_descriptor
+True if the file whose file descriptor number
+is
+.Ar file_descriptor
+is open and is associated with a terminal.
+.It Fl u Ar file
+True if
+.Ar file
+exists and its set user ID flag
+is set.
+.It Fl w Ar file
+True if
+.Ar file
+exists and is writable.
+True
+indicates only that the write flag is on.
+The file is not writable on a read-only file
+system even if this test indicates true.
+.It Fl x Ar file
+True if
+.Ar file
+exists and is executable.
+True
+indicates only that the execute flag is on.
+If
+.Ar file
+is a directory, true indicates that
+.Ar file
+can be searched.
+.It Fl z Ar string
+True if the length of
+.Ar string
+is zero.
+.It Fl L Ar file
+True if
+.Ar file
+exists and is a symbolic link.
+This operator is retained for compatibility with previous versions of
+this program.
+Do not rely on its existence; use
+.Fl h
+instead.
+.It Fl O Ar file
+True if
+.Ar file
+exists and its owner matches the effective user id of this process.
+.It Fl G Ar file
+True if
+.Ar file
+exists and its group matches the effective group id of this process.
+.It Fl S Ar file
+True if
+.Ar file
+exists and is a socket.
+.It Ar file1 Fl nt Ar file2
+True if
+.Ar file1
+exists and is newer than
+.Ar file2 .
+.It Ar file1 Fl ot Ar file2
+True if
+.Ar file1
+exists and is older than
+.Ar file2 .
+.It Ar file1 Fl ef Ar file2
+True if
+.Ar file1
+and
+.Ar file2
+exist and refer to the same file.
+.It Ar string
+True if
+.Ar string
+is not the null
+string.
+.It Ar \&s\&1 Cm \&= Ar \&s\&2
+True if the strings
+.Ar \&s\&1
+and
+.Ar \&s\&2
+are identical.
+.It Ar \&s\&1 Cm \&!= Ar \&s\&2
+True if the strings
+.Ar \&s\&1
+and
+.Ar \&s\&2
+are not identical.
+.It Ar \&s\&1 Cm \&\*[Lt] Ar \&s\&2
+True if string
+.Ar \&s\&1
+comes before
+.Ar \&s\&2
+based on the ASCII value of their characters.
+.It Ar \&s\&1 Cm \&\*[Gt] Ar \&s\&2
+True if string
+.Ar \&s\&1
+comes after
+.Ar \&s\&2
+based on the ASCII value of their characters.
+.It Ar \&n\&1 Fl \&eq Ar \&n\&2
+True if the integers
+.Ar \&n\&1
+and
+.Ar \&n\&2
+are algebraically
+equal.
+.It Ar \&n\&1 Fl \&ne Ar \&n\&2
+True if the integers
+.Ar \&n\&1
+and
+.Ar \&n\&2
+are not
+algebraically equal.
+.It Ar \&n\&1 Fl \&gt Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically
+greater than the integer
+.Ar \&n\&2 .
+.It Ar \&n\&1 Fl \&ge Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically
+greater than or equal to the integer
+.Ar \&n\&2 .
+.It Ar \&n\&1 Fl \&lt Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically less
+than the integer
+.Ar \&n\&2 .
+.It Ar \&n\&1 Fl \&le Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically less
+than or equal to the integer
+.Ar \&n\&2 .
+.El
+.Pp
+These primaries can be combined with the following operators:
+.Bl -tag -width Ar
+.It Cm \&! Ar expression
+True if
+.Ar expression
+is false.
+.It Ar expression1 Fl a Ar expression2
+True if both
+.Ar expression1
+and
+.Ar expression2
+are true.
+.It Ar expression1 Fl o Ar expression2
+True if either
+.Ar expression1
+or
+.Ar expression2
+are true.
+.It Cm \&( Ns Ar expression Ns Cm \&)
+True if expression is true.
+.El
+.Pp
+The
+.Fl a
+operator has higher precedence than the
+.Fl o
+operator.
+.It times
+Print the accumulated user and system times for the shell and for processes
+run from the shell.  The return status is 0.
 .It Xo trap
-.Op Fl l
-.Xc
-.It Xo trap
-.Op Ar action
-.Ar signal ...
+.Op Ar action Ar signal ...
 .Xc
 Cause the shell to parse and execute action when any of the specified
 signals are received.
@@ -1647,11 +2068,6 @@
 .Ic trap
 command has no effect on signals that were
 ignored on entry to the shell.
-Issuing
-.Ic trap
-with option
-.Ar -l
-will print a list of valid signal names.
 .Ic trap
 without any arguments cause it to write a list of signals and their
 associated action to the standard output in a format that is suitable
@@ -1663,10 +2079,6 @@
 .Pp
 List trapped signals and their corresponding action
 .Pp
-.Dl trap -l
-.Pp
-Print a list of valid signals
-.Pp
 .Dl trap '' INT QUIT tstp 30
 .Pp
 Ignore signals INT QUIT TSTP USR1
@@ -1711,8 +2123,6 @@
 any one of these flags:
 .It Fl a
 show all the current limits
-.It Fl b
-show or set the limit on the socket buffer size of a process (in bytes)
 .It Fl t
 show or set the limit on CPU time (in seconds)
 .It Fl f
@@ -1736,7 +2146,7 @@
 show or set the limit on the number of processes this user can
 have at one time
 .It Fl n
-show or set the limit on the number of files a process can have open at once
+show or set the limit on the number files a process can have open at once
 .El
 .Pp
 If none of these is specified, it is the limit on file size that is shown
@@ -1763,15 +2173,23 @@
 If
 .Fl a
 is specified, all aliases are removed.
-.It unset Ar name ...
+.It unset Xo
+.Op Fl fv
+.Ar name ...
+.Xc
 The specified variables and functions are unset and unexported.
-If a given name corresponds to both a variable and a function, both
-the variable and the function are unset.
+If
+.Fl f
+or
+.Fl v
+is specified, the corresponding function or variable is unset, respectively.
+If a given name corresponds to both a variable and a function, and no
+options are given, only the variable is unset.
 .It wait Op Ar job
 Wait for the specified job to complete and return the exit status of the
 last process in the job.
 If the argument is omitted, wait for all jobs to
-complete and then return an exit status of zero.
+complete and the return an exit status of zero.
 .El
 .Ss Command Line Editing
 When
@@ -1781,38 +2199,21 @@
 .Ic fc
 in
 .Sx Builtins )
-can be edited using emacs-mode or vi-mode command-line editing.
-The command
-.Ql set -o emacs
-enables emacs-mode editing.
+can be edited using vi-mode command-line editing.
+This mode uses commands, described below,
+similar to a subset of those described in the vi man page.
 The command
 .Ql set -o vi
-enables vi-mode editing and places sh into vi insert mode.
-(See the
-.Sx Argument List Processing
-section above.)
-.Pp
-The vi mode uses commands similar to a subset of those described in the
-.Xr vi 1
-man page.
+enables vi-mode editing and place sh into vi insert mode.
 With vi-mode
 enabled, sh can be switched between insert mode and command mode.
+The editor is not described in full here, but will be in a later document.
 It's similar to vi: typing
 .Aq ESC
 will throw you into command VI command mode.
 Hitting
 .Aq return
 while in command mode will pass the line to the shell.
-.Pp
-The emacs mode uses commands similar to a subset available in
-the emacs editor.
-With emacs-mode enabled, special keys can be used to modify the text
-in the buffer using the control key.
-.Pp
-.Nm
-uses the
-.Xr editline 3
-library.
 .Sh EXIT STATUS
 Errors that are detected by the shell, such as a syntax error, will cause the
 shell to exit with a non-zero exit status.
@@ -1828,7 +2229,7 @@
 Set automatically by
 .Xr login 1
 from the user's login directory in the password file
-.Pq Xr passwd 5 .
+.Pq Xr passwd 4 .
 This environment variable also functions as the default argument for the
 cd builtin.
 .It Ev PATH
@@ -1837,11 +2238,6 @@
 .Sx Path Search .
 .It Ev CDPATH
 The search path used with the cd builtin.
-.It Ev LANG
-The string used to specify localization information that allows users
-to work with different culture-specific and language conventions.
-See
-.Xr nls 7 .
 .It Ev MAIL
 The name of a mail file, that will be checked for the arrival of new mail.
 Overridden by
@@ -1890,6 +2286,17 @@
 children of the shell, and is used in the history editing modes.
 .It Ev HISTSIZE
 The number of lines in the history buffer for the shell.
+.It Ev PWD
+The logical value of the current working directory.  This is set by the
+.Ic cd
+command.
+.It Ev OLDPWD
+The previous logical value of the current working directory.  This is set by
+the
+.Ic cd
+command.
+.It Ev PPID
+The process ID of the parent process of the shell.
 .El
 .Sh FILES
 .Bl -item -width HOMEprofilexxxx
@@ -1906,13 +2313,10 @@
 .Xr login 1 ,
 .Xr printf 1 ,
 .Xr test 1 ,
-.Xr editline 3 ,
 .Xr getopt 3 ,
-.\" .Xr profile 4 ,
-.Xr editrc 5 ,
 .Xr passwd 5 ,
+.\" .Xr profile 4 ,
 .Xr environ 7 ,
-.Xr nls 7 ,
 .Xr sysctl 8
 .Sh HISTORY
 A
diff --git a/ash/shell.h b/dash/shell.h
similarity index 86%
rename from ash/shell.h
rename to dash/shell.h
index e2966fc..9b67696 100644
--- a/ash/shell.h
+++ b/dash/shell.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: shell.h,v 1.17 2003/08/07 09:05:38 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -51,6 +51,9 @@
 
 #include <sys/param.h>
 
+#ifndef JOBS
+#define JOBS 1
+#endif
 #ifndef BSD
 #define BSD 1
 #endif
@@ -65,18 +68,11 @@
 #ifndef NULL
 #define NULL (void *)0
 #endif
-#define STATIC	/* empty */
+#define STATIC static
 #define MKINIT	/* empty */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-
 extern char nullstr[1];		/* null string */
 
-#if 0
-#define DEBUG 2
-#endif
 
 #ifdef DEBUG
 #define TRACE(param)	trace param
@@ -85,3 +81,14 @@
 #define TRACE(param)
 #define TRACEV(param)
 #endif
+
+#if defined(__GNUC__) && __GNUC__ < 3
+#define va_copy __va_copy
+#endif
+
+#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
+#define __builtin_expect(x, expected_value) (x)
+#endif
+
+#define likely(x)	__builtin_expect(!!(x),1)
+#define unlikely(x)	__builtin_expect(!!(x),0)
diff --git a/ash/show.c b/dash/show.c
similarity index 91%
rename from ash/show.c
rename to dash/show.c
index fa020bf..05af328 100644
--- a/ash/show.c
+++ b/dash/show.c
@@ -1,8 +1,8 @@
-/*	$NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -32,23 +32,8 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __KLIBC__
-#include <sys/cdefs.h>
-#endif
-#ifndef __RCSID
-#define __RCSID(arg)
-#endif
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)show.c	8.3 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl Exp $");
-#endif
-#endif /* not lint */
-
 #include <stdio.h>
 #include <stdarg.h>
-#include <stdlib.h>
 
 #include "shell.h"
 #include "parser.h"
@@ -262,7 +247,6 @@
 		putc('\t', fp);
 	}
 }
-#endif
 
 
 
@@ -274,7 +258,6 @@
 FILE *tracefile;
 
 
-#ifdef DEBUG
 void
 trputc(int c)
 {
@@ -282,12 +265,10 @@
 		return;
 	putc(c, tracefile);
 }
-#endif
 
 void
 trace(const char *fmt, ...)
 {
-#ifdef DEBUG
 	va_list va;
 
 	if (debug != 1)
@@ -295,21 +276,17 @@
 	va_start(va, fmt);
 	(void) vfprintf(tracefile, fmt, va);
 	va_end(va);
-#endif
 }
 
 void
 tracev(const char *fmt, va_list va)
 {
-#ifdef DEBUG
 	if (debug != 1)
 		return;
 	(void) vfprintf(tracefile, fmt, va);
-#endif
 }
 
 
-#ifdef DEBUG
 void
 trputs(const char *s)
 {
@@ -357,13 +334,11 @@
 	}
 	putc('"', tracefile);
 }
-#endif
 
 
 void
 trargs(char **ap)
 {
-#ifdef DEBUG
 	if (debug != 1)
 		return;
 	while (*ap) {
@@ -373,15 +348,13 @@
 		else
 			putc('\n', tracefile);
 	}
-#endif
 }
 
 
-#ifdef DEBUG
 void
 opentrace(void)
 {
-	char s[100], *p;
+	char s[100];
 #ifdef O_APPEND
 	int flags;
 #endif
@@ -395,7 +368,7 @@
 #ifdef not_this_way
 	{
 		char *p;
-		if ((p = getenv("HOME")) == NULL) {
+		if ((p = getenv(homestr)) == NULL) {
 			if (geteuid() == 0)
 				p = "/";
 			else
@@ -405,11 +378,10 @@
 		strcat(s, "/trace");
 	}
 #else
-	p = getenv("KLIBC_ASH_DEBUGTRACE");
-	scopy(p ? p : "/tmp/trace", s);
+	scopy("./trace", s);
 #endif /* not_this_way */
 	if (tracefile) {
-		if (!(!fclose(tracefile) && (tracefile = fopen(s, "a")))) {
+		if (!freopen(s, "a", tracefile)) {
 			fprintf(stderr, "Can't re-open %s\n", s);
 			debug = 0;
 			return;
@@ -425,6 +397,7 @@
 	if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
 		fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
 #endif
+	setlinebuf(tracefile);
 	fputs("\nTracing started.\n", tracefile);
 }
 #endif /* DEBUG */
diff --git a/ash/show.h b/dash/show.h
similarity index 94%
rename from ash/show.h
rename to dash/show.h
index 3152ff2..d0ccac7 100644
--- a/ash/show.h
+++ b/dash/show.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: show.h,v 1.7 2003/08/07 09:05:38 agc Exp $	*/
-
 /*-
  * Copyright (c) 1995
  *      The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -33,12 +33,12 @@
 
 #include <stdarg.h>
 
+#ifdef DEBUG
 union node;
 void showtree(union node *);
 void trace(const char *, ...);
 void tracev(const char *, va_list);
 void trargs(char **);
-#ifdef DEBUG
 void trputc(int);
 void trputs(const char *);
 void opentrace(void);
diff --git a/dash/system.c b/dash/system.c
new file mode 100644
index 0000000..5fd6062
--- /dev/null
+++ b/dash/system.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2004
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef HAVE_ISALPHA
+#define isalnum _isalnum
+#define iscntrl _iscntrl
+#define islower _islower
+#define isspace _isspace
+#define isalpha _isalpha
+#define isdigit _isdigit
+#define isprint _isprint
+#define isupper _isupper
+#define isblank _isblank
+#define isgraph _isgraph
+#define ispunct _ispunct
+#define isxdigit _isxdigit
+#include <ctype.h>
+#undef isalnum
+#undef iscntrl
+#undef islower
+#undef isspace
+#undef isalpha
+#undef isdigit
+#undef isprint
+#undef isupper
+#undef isblank
+#undef isgraph
+#undef ispunct
+#undef isxdigit
+#endif
+
+#include <signal.h>
+#include <string.h>
+
+#include "error.h"
+#include "output.h"
+#include "system.h"
+
+#ifndef HAVE_MEMPCPY
+void *mempcpy(void *dest, const void *src, size_t n)
+{
+	return memcpy(dest, src, n) + n;
+}
+#endif
+
+#ifndef HAVE_STPCPY
+char *stpcpy(char *dest, const char *src)
+{
+	size_t len = strlen(src);
+	dest[len] = 0;
+	return mempcpy(dest, src, len);
+}
+#endif
+
+#ifndef HAVE_STRCHRNUL
+char *strchrnul(const char *s, int c)
+{
+	char *p = strchr(s, c);
+	if (!p)
+		p = (char *)s + strlen(s);
+	return p;
+}
+#endif
+
+#ifndef HAVE_STRSIGNAL
+char *strsignal(int sig)
+{
+	static char buf[19];
+
+	if ((unsigned)sig < NSIG && sys_siglist[sig])
+		return (char *)sys_siglist[sig];
+	fmtstr(buf, sizeof(buf), "Signal %d", sig); 
+	return buf;
+}
+#endif
+
+#ifndef HAVE_BSEARCH
+void *bsearch(const void *key, const void *base, size_t nmemb,
+	      size_t size, int (*cmp)(const void *, const void *))
+{
+	while (nmemb) {
+		size_t mididx = nmemb / 2;
+		const void *midobj = base + mididx * size;
+		int diff = cmp(key, midobj);
+
+		if (diff == 0)
+			return (void *)midobj;
+
+		if (diff > 0) {
+			base = midobj + size;
+			nmemb -= mididx + 1;
+		} else
+			nmemb = mididx;
+	}
+
+	return 0;
+}
+#endif
+
+#ifndef HAVE_SYSCONF
+long sysconf(int name)
+{
+	sh_error("no sysconf for: %d", name);
+}
+#endif
+
+#ifndef HAVE_ISALPHA
+int isalnum(int c) {
+	return _isalnum(c);
+}
+
+
+int iscntrl(int c) {
+	return _iscntrl(c);
+}
+
+
+int islower(int c) {
+	return _islower(c);
+}
+
+
+int isspace(int c) {
+	return _isspace(c);
+}
+
+
+int isalpha(int c) {
+	return _isalpha(c);
+}
+
+
+int isdigit(int c) {
+	return _isdigit(c);
+}
+
+
+int isprint(int c) {
+	return _isprint(c);
+}
+
+
+int isupper(int c) {
+	return _isupper(c);
+}
+
+
+int isblank(int c) {
+	return _isblank(c);
+}
+
+
+int isgraph(int c) {
+	return _isgraph(c);
+}
+
+
+int ispunct(int c) {
+	return _ispunct(c);
+}
+
+
+int isxdigit(int c) {
+	return _isxdigit(c);
+}
+#endif
diff --git a/dash/system.h b/dash/system.h
new file mode 100644
index 0000000..b8853e6
--- /dev/null
+++ b/dash/system.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2004
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <limits.h>
+#include <signal.h>
+#include <sys/types.h>
+
+#ifndef SSIZE_MAX
+#define SSIZE_MAX ((ssize_t)((size_t)-1 >> 1))
+#endif
+
+static inline void sigclearmask(void)
+{
+#ifdef HAVE_SIGSETMASK
+	sigsetmask(0);
+#else
+	sigset_t set;
+	sigemptyset(&set);
+	sigprocmask(SIG_SETMASK, &set, 0);
+#endif
+}
+
+#ifndef HAVE_MEMPCPY
+void *mempcpy(void *, const void *, size_t);
+#endif
+
+#ifndef HAVE_STPCPY
+char *stpcpy(char *, const char *);
+#endif
+
+#ifndef HAVE_STRCHRNUL
+char *strchrnul(const char *, int);
+#endif
+
+#ifndef HAVE_STRSIGNAL
+char *strsignal(int);
+#endif
+
+#ifndef HAVE_STRTOD
+static inline double strtod(const char *nptr, char **endptr)
+{
+	*endptr = (char *)nptr;
+	return 0;
+}
+#endif
+
+#ifndef HAVE_STRTOIMAX
+#define strtoimax strtoll
+#endif
+
+#ifndef HAVE_STRTOUMAX
+#define strtoumax strtoull
+#endif
+
+#ifndef HAVE_BSEARCH
+void *bsearch(const void *, const void *, size_t, size_t,
+	      int (*)(const void *, const void *));
+#endif
+
+#ifndef HAVE_KILLPG
+static inline int killpg(pid_t pid, int signal)
+{
+#ifdef DEBUG
+	if (pid < 0)
+		abort();
+#endif
+	return kill(-pid, signal);
+}
+#endif
+
+#ifndef HAVE_SYSCONF
+#define _SC_CLK_TCK 2
+long sysconf(int) __attribute__((__noreturn__));
+#endif
diff --git a/dash/trap.c b/dash/trap.c
new file mode 100644
index 0000000..bbff81a
--- /dev/null
+++ b/dash/trap.c
@@ -0,0 +1,394 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "shell.h"
+#include "main.h"
+#include "nodes.h"	/* for other headers */
+#include "eval.h"
+#include "jobs.h"
+#include "show.h"
+#include "options.h"
+#include "syntax.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "trap.h"
+#include "mystring.h"
+
+#ifdef HETIO
+#include "hetio.h"
+#endif
+
+/*
+ * Sigmode records the current value of the signal handlers for the various
+ * modes.  A value of zero means that the current handler is not known.
+ * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
+ */
+
+#define S_DFL 1			/* default signal handling (SIG_DFL) */
+#define S_CATCH 2		/* signal is caught */
+#define S_IGN 3			/* signal is ignored (SIG_IGN) */
+#define S_HARD_IGN 4		/* signal is ignored permenantly */
+#define S_RESET 5		/* temporary - to reset a hard ignored sig */
+
+
+/* trap handler commands */
+char *trap[NSIG];
+/* current value of signal */
+static char sigmode[NSIG - 1];
+/* indicates specified signal received */
+char gotsig[NSIG - 1];
+/* last pending signal */
+volatile sig_atomic_t pendingsigs;
+/* do we generate EXSIG events */
+int exsig;
+
+extern char *signal_names[];
+
+#ifdef mkinit
+INCLUDE <signal.h>
+INIT {
+	signal(SIGCHLD, SIG_DFL);
+}
+#endif
+
+/*
+ * The trap builtin.
+ */
+
+int
+trapcmd(int argc, char **argv)
+{
+	char *action;
+	char **ap;
+	int signo;
+
+	nextopt(nullstr);
+	ap = argptr;
+	if (!*ap) {
+		for (signo = 0 ; signo < NSIG ; signo++) {
+			if (trap[signo] != NULL) {
+				out1fmt(
+					"trap -- %s %s\n",
+					single_quote(trap[signo]),
+					signal_names[signo]
+				);
+			}
+		}
+		return 0;
+	}
+	if (!ap[1])
+		action = NULL;
+	else
+		action = *ap++;
+	while (*ap) {
+		if ((signo = decode_signal(*ap, 0)) < 0)
+			sh_error("%s: bad trap", *ap);
+		INTOFF;
+		if (action) {
+			if (action[0] == '-' && action[1] == '\0')
+				action = NULL;
+			else
+				action = savestr(action);
+		}
+		if (trap[signo])
+			ckfree(trap[signo]);
+		trap[signo] = action;
+		if (signo != 0)
+			setsignal(signo);
+		INTON;
+		ap++;
+	}
+	return 0;
+}
+
+
+
+/*
+ * Clear traps on a fork.
+ */
+
+void
+clear_traps(void)
+{
+	char **tp;
+
+	for (tp = trap ; tp < &trap[NSIG] ; tp++) {
+		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
+			INTOFF;
+			ckfree(*tp);
+			*tp = NULL;
+			if (tp != &trap[0])
+				setsignal(tp - trap);
+			INTON;
+		}
+	}
+}
+
+
+
+/*
+ * Set the signal handler for the specified signal.  The routine figures
+ * out what it should be set to.
+ */
+
+void
+setsignal(int signo)
+{
+	int action;
+	char *t, tsig;
+	struct sigaction act;
+
+	if ((t = trap[signo]) == NULL)
+		action = S_DFL;
+	else if (*t != '\0')
+		action = S_CATCH;
+	else
+		action = S_IGN;
+	if (rootshell && action == S_DFL) {
+		switch (signo) {
+		case SIGINT:
+			if (iflag || minusc || sflag == 0)
+				action = S_CATCH;
+			break;
+		case SIGQUIT:
+#ifdef DEBUG
+			if (debug)
+				break;
+#endif
+			/* FALLTHROUGH */
+		case SIGTERM:
+			if (iflag)
+				action = S_IGN;
+			break;
+#if JOBS
+		case SIGTSTP:
+		case SIGTTOU:
+			if (mflag)
+				action = S_IGN;
+			break;
+#endif
+		}
+	}
+
+	t = &sigmode[signo - 1];
+	tsig = *t;
+	if (tsig == 0) {
+		/*
+		 * current setting unknown
+		 */
+		if (sigaction(signo, 0, &act) == -1) {
+			/*
+			 * Pretend it worked; maybe we should give a warning
+			 * here, but other shells don't. We don't alter
+			 * sigmode, so that we retry every time.
+			 */
+			return;
+		}
+		if (act.sa_handler == SIG_IGN) {
+			if (mflag && (signo == SIGTSTP ||
+			     signo == SIGTTIN || signo == SIGTTOU)) {
+				tsig = S_IGN;	/* don't hard ignore these */
+			} else
+				tsig = S_HARD_IGN;
+		} else {
+			tsig = S_RESET;	/* force to be set */
+		}
+	}
+	if (tsig == S_HARD_IGN || tsig == action)
+		return;
+	switch (action) {
+	case S_CATCH:
+		act.sa_handler = onsig;
+		break;
+	case S_IGN:
+		act.sa_handler = SIG_IGN;
+		break;
+	default:
+		act.sa_handler = SIG_DFL;
+	}
+	*t = action;
+	act.sa_flags = 0;
+	sigfillset(&act.sa_mask);
+	sigaction(signo, &act, 0);
+}
+
+/*
+ * Ignore a signal.
+ */
+
+void
+ignoresig(int signo)
+{
+	if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
+		signal(signo, SIG_IGN);
+	}
+	sigmode[signo - 1] = S_HARD_IGN;
+}
+
+
+
+/*
+ * Signal handler.
+ */
+
+void
+onsig(int signo)
+{
+	gotsig[signo - 1] = 1;
+	pendingsigs = signo;
+
+	if (exsig || (signo == SIGINT && !trap[SIGINT])) {
+		if (!suppressint)
+			onint();
+		intpending = 1;
+	}
+}
+
+
+
+/*
+ * Called to execute a trap.  Perhaps we should avoid entering new trap
+ * handlers while we are executing a trap handler.
+ */
+
+int
+dotrap(void)
+{
+	char *p;
+	char *q;
+	int i;
+	int savestatus;
+	int skip = 0;
+
+	savestatus = exitstatus;
+	pendingsigs = 0;
+	barrier();
+
+	for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
+		if (!*q)
+			continue;
+		*q = 0;
+
+		p = trap[i + 1];
+		if (!p)
+			continue;
+		skip = evalstring(p, SKIPEVAL);
+		exitstatus = savestatus;
+		if (skip)
+			break;
+	}
+
+	return skip;
+}
+
+
+
+/*
+ * Controls whether the shell is interactive or not.
+ */
+
+
+void
+setinteractive(int on)
+{
+	static int is_interactive;
+
+	if (++on == is_interactive)
+		return;
+	is_interactive = on;
+	setsignal(SIGINT);
+	setsignal(SIGQUIT);
+	setsignal(SIGTERM);
+}
+
+
+
+/*
+ * Called to exit the shell.
+ */
+
+void
+exitshell(void)
+{
+	struct jmploc loc;
+	char *p;
+	int status;
+
+#ifdef HETIO
+	hetio_reset_term();
+#endif
+	status = exitstatus;
+	TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
+	if (setjmp(loc.loc)) {
+		if (exception == EXEXIT)
+			_exit(exitstatus);
+		goto out;
+	}
+	handler = &loc;
+	if ((p = trap[0])) {
+		trap[0] = NULL;
+		evalstring(p, 0);
+	}
+	flushall();
+out:
+	_exit(status);
+	/* NOTREACHED */
+}
+
+int decode_signal(const char *string, int minsig)
+{
+	int signo;
+
+	if (is_number(string)) {
+		signo = atoi(string);
+		if (signo >= NSIG) {
+			return -1;
+		}
+		return signo;
+	}
+
+	for (signo = minsig; signo < NSIG; signo++) {
+		if (!strcasecmp(string, signal_names[signo])) {
+			return signo;
+		}
+	}
+
+	return -1;
+}
diff --git a/ash/trap.h b/dash/trap.h
similarity index 81%
rename from ash/trap.h
rename to dash/trap.h
index c0d5cd9..e889136 100644
--- a/ash/trap.h
+++ b/dash/trap.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: trap.h,v 1.17 2003/08/07 09:05:39 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -34,14 +34,18 @@
  *	@(#)trap.h	8.3 (Berkeley) 6/5/95
  */
 
-extern int pendingsigs;
+#include <signal.h>
+
+extern char *trap[];
+extern char gotsig[];
+extern volatile sig_atomic_t pendingsigs;
 
 int trapcmd(int, char **);
-void clear_traps(int);
-long setsignal(int, int);
-void ignoresig(int, int);
-/* See definition for explanation of the __cdecl */
-__cdecl void onsig(int);
-void dotrap(void);
+void clear_traps(void);
+void setsignal(int);
+void ignoresig(int);
+void onsig(int);
+int dotrap(void);
 void setinteractive(int);
-void exitshell(int) __attribute__((__noreturn__));
+void exitshell(void) __attribute__((__noreturn__));
+int decode_signal(const char *, int);
diff --git a/dash/var.c b/dash/var.c
new file mode 100644
index 0000000..3263dc5
--- /dev/null
+++ b/dash/var.c
@@ -0,0 +1,676 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <paths.h>
+
+/*
+ * Shell variables.
+ */
+
+#include "shell.h"
+#include "output.h"
+#include "expand.h"
+#include "nodes.h"	/* for other headers */
+#include "eval.h"	/* defines cmdenviron */
+#include "exec.h"
+#include "syntax.h"
+#include "options.h"
+#include "mail.h"
+#include "var.h"
+#include "memalloc.h"
+#include "error.h"
+#include "mystring.h"
+#include "parser.h"
+#include "show.h"
+#ifndef SMALL
+#include "myhistedit.h"
+#endif
+#include "system.h"
+
+
+#define VTABSIZE 39
+
+
+struct localvar *localvars;
+
+const char defpathvar[] =
+	"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
+#ifdef IFS_BROKEN
+const char defifsvar[] = "IFS= \t\n";
+#else
+const char defifs[] = " \t\n";
+#endif
+
+struct var varinit[] = {
+#if ATTY
+	{ 0,	VSTRFIXED|VTEXTFIXED|VUNSET,	"ATTY\0",	0 },
+#endif
+#ifdef IFS_BROKEN
+	{ 0,	VSTRFIXED|VTEXTFIXED,		defifsvar,	0 },
+#else
+	{ 0,	VSTRFIXED|VTEXTFIXED|VUNSET,	"IFS\0",	0 },
+#endif
+	{ 0,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL\0",	changemail },
+	{ 0,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH\0",	changemail },
+	{ 0,	VSTRFIXED|VTEXTFIXED,		defpathvar,	changepath },
+	{ 0,	VSTRFIXED|VTEXTFIXED,		"PS1=$ ",	0 },
+	{ 0,	VSTRFIXED|VTEXTFIXED,		"PS2=> ",	0 },
+	{ 0,	VSTRFIXED|VTEXTFIXED,		"PS4=+ ",	0 },
+	{ 0,	VSTRFIXED|VTEXTFIXED,		"OPTIND=1",	getoptsreset },
+#ifndef SMALL
+	{ 0,	VSTRFIXED|VTEXTFIXED|VUNSET,	"TERM\0",	0 },
+	{ 0,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE\0",	sethistsize },
+#endif
+};
+
+STATIC struct var *vartab[VTABSIZE];
+
+STATIC void mklocal(char *);
+STATIC struct var **hashvar(const char *);
+STATIC int vpcmp(const void *, const void *);
+STATIC struct var **findvar(struct var **, const char *);
+
+/*
+ * Initialize the varable symbol tables and import the environment
+ */
+
+#ifdef mkinit
+INCLUDE <unistd.h>
+INCLUDE <sys/types.h>
+INCLUDE <sys/stat.h>
+INCLUDE "cd.h"
+INCLUDE "output.h"
+INCLUDE "var.h"
+MKINIT char **environ;
+INIT {
+	char **envp;
+	static char ppid[32] = "PPID=";
+	const char *p;
+	struct stat st1, st2;
+
+	initvar();
+	for (envp = environ ; *envp ; envp++) {
+		if (strchr(*envp, '=')) {
+			setvareq(*envp, VEXPORT|VTEXTFIXED);
+		}
+	}
+
+	fmtstr(ppid + 5, sizeof(ppid) - 5, "%ld", (long) getppid());
+	setvareq(ppid, VTEXTFIXED);
+
+	p = lookupvar("PWD");
+	if (p)
+		if (*p != '/' || stat(p, &st1) || stat(".", &st2) ||
+		    st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
+			p = 0;
+	setpwd(p, 0);
+}
+#endif
+
+
+/*
+ * This routine initializes the builtin variables.  It is called when the
+ * shell is initialized.
+ */
+
+void
+initvar(void)
+{
+	struct var *vp;
+	struct var *end;
+	struct var **vpp;
+
+	vp = varinit;
+	end = vp + sizeof(varinit) / sizeof(varinit[0]);
+	do {
+		vpp = hashvar(vp->text);
+		vp->next = *vpp;
+		*vpp = vp;
+	} while (++vp < end);
+	/*
+	 * PS1 depends on uid
+	 */
+	if (!geteuid())
+		vps1.text = "PS1=# ";
+}
+
+/*
+ * Safe version of setvar, returns 1 on success 0 on failure.
+ */
+
+int
+setvarsafe(const char *name, const char *val, int flags)
+{
+	int err;
+	volatile int saveint;
+	struct jmploc *volatile savehandler = handler;
+	struct jmploc jmploc;
+
+	SAVEINT(saveint);
+	if (setjmp(jmploc.loc))
+		err = 1;
+	else {
+		handler = &jmploc;
+		setvar(name, val, flags);
+		err = 0;
+	}
+	handler = savehandler;
+	RESTOREINT(saveint);
+	return err;
+}
+
+/*
+ * Set the value of a variable.  The flags argument is ored with the
+ * flags of the variable.  If val is NULL, the variable is unset.
+ */
+
+void
+setvar(const char *name, const char *val, int flags)
+{
+	char *p, *q;
+	size_t namelen;
+	char *nameeq;
+	size_t vallen;
+
+	q = endofname(name);
+	p = strchrnul(q, '=');
+	namelen = p - name;
+	if (!namelen || p != q)
+		sh_error("%.*s: bad variable name", namelen, name);
+	vallen = 0;
+	if (val == NULL) {
+		flags |= VUNSET;
+	} else {
+		vallen = strlen(val);
+	}
+	INTOFF;
+	p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen);
+	if (val) {
+		*p++ = '=';
+		p = mempcpy(p, val, vallen);
+	}
+	*p = '\0';
+	setvareq(nameeq, flags | VNOSAVE);
+	INTON;
+}
+
+
+
+/*
+ * Same as setvar except that the variable and value are passed in
+ * the first argument as name=value.  Since the first argument will
+ * be actually stored in the table, it should not be a string that
+ * will go away.
+ * Called with interrupts off.
+ */
+
+void
+setvareq(char *s, int flags)
+{
+	struct var *vp, **vpp;
+
+	vpp = hashvar(s);
+	flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
+	vp = *findvar(vpp, s);
+	if (vp) {
+		if (vp->flags & VREADONLY) {
+			const char *n;
+
+			if (flags & VNOSAVE)
+				free(s);
+			n = vp->text;
+			sh_error("%.*s: is read only", strchrnul(n, '=') - n,
+				 n);
+		}
+
+		if (flags & VNOSET)
+			return;
+
+		if (vp->func && (flags & VNOFUNC) == 0)
+			(*vp->func)(strchrnul(s, '=') + 1);
+
+		if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+			ckfree(vp->text);
+
+		flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
+	} else {
+		if (flags & VNOSET)
+			return;
+		/* not found */
+		vp = ckmalloc(sizeof (*vp));
+		vp->next = *vpp;
+		vp->func = NULL;
+		*vpp = vp;
+	}
+	if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
+		s = savestr(s);
+	vp->text = s;
+	vp->flags = flags;
+}
+
+
+
+/*
+ * Process a linked list of variable assignments.
+ */
+
+void
+listsetvar(struct strlist *list, int flags)
+{
+	struct strlist *lp;
+
+	lp = list;
+	if (!lp)
+		return;
+	INTOFF;
+	do {
+		setvareq(lp->text, flags);
+	} while ((lp = lp->next));
+	INTON;
+}
+
+
+/*
+ * Find the value of a variable.  Returns NULL if not set.
+ */
+
+char *
+lookupvar(const char *name)
+{
+	struct var *v;
+
+	if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
+		return strchrnul(v->text, '=') + 1;
+	}
+	return NULL;
+}
+
+
+
+/*
+ * Search the environment of a builtin command.
+ */
+
+char *
+bltinlookup(const char *name)
+{
+	struct strlist *sp;
+
+	for (sp = cmdenviron ; sp ; sp = sp->next) {
+		if (varequal(sp->text, name))
+			return strchrnul(sp->text, '=') + 1;
+	}
+	return lookupvar(name);
+}
+
+
+
+/*
+ * Generate a list of variables satisfying the given conditions.
+ */
+
+char **
+listvars(int on, int off, char ***end)
+{
+	struct var **vpp;
+	struct var *vp;
+	char **ep;
+	int mask;
+
+	STARTSTACKSTR(ep);
+	vpp = vartab;
+	mask = on | off;
+	do {
+		for (vp = *vpp ; vp ; vp = vp->next)
+			if ((vp->flags & mask) == on) {
+				if (ep == stackstrend())
+					ep = growstackstr();
+				*ep++ = (char *) vp->text;
+			}
+	} while (++vpp < vartab + VTABSIZE);
+	if (ep == stackstrend())
+		ep = growstackstr();
+	if (end)
+		*end = ep;
+	*ep++ = NULL;
+	return grabstackstr(ep);
+}
+
+
+
+/*
+ * POSIX requires that 'set' (but not export or readonly) output the
+ * variables in lexicographic order - by the locale's collating order (sigh).
+ * Maybe we could keep them in an ordered balanced binary tree
+ * instead of hashed lists.
+ * For now just roll 'em through qsort for printing...
+ */
+
+int
+showvars(const char *prefix, int on, int off)
+{
+	const char *sep;
+	char **ep, **epend;
+
+	ep = listvars(on, off, &epend);
+	qsort(ep, epend - ep, sizeof(char *), vpcmp);
+
+	sep = *prefix ? spcstr : prefix;
+
+	for (; ep < epend; ep++) {
+		const char *p;
+		const char *q;
+
+		p = strchrnul(*ep, '=');
+		q = nullstr;
+		if (*p)
+			q = single_quote(++p);
+
+		out1fmt("%s%s%.*s%s\n", prefix, sep, (int)(p - *ep), *ep, q);
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * The export and readonly commands.
+ */
+
+int
+exportcmd(int argc, char **argv)
+{
+	struct var *vp;
+	char *name;
+	const char *p;
+	char **aptr;
+	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
+	int notp;
+
+	notp = nextopt("p") - 'p';
+	if (notp && ((name = *(aptr = argptr)))) {
+		do {
+			if ((p = strchr(name, '=')) != NULL) {
+				p++;
+			} else {
+				if ((vp = *findvar(hashvar(name), name))) {
+					vp->flags |= flag;
+					continue;
+				}
+			}
+			setvar(name, p, flag);
+		} while ((name = *++aptr) != NULL);
+	} else {
+		showvars(argv[0], flag, 0);
+	}
+	return 0;
+}
+
+
+/*
+ * The "local" command.
+ */
+
+int
+localcmd(int argc, char **argv)
+{
+	char *name;
+
+	argv = argptr;
+	while ((name = *argv++) != NULL) {
+		mklocal(name);
+	}
+	return 0;
+}
+
+
+/*
+ * Make a variable a local variable.  When a variable is made local, it's
+ * value and flags are saved in a localvar structure.  The saved values
+ * will be restored when the shell function returns.  We handle the name
+ * "-" as a special case.
+ */
+
+STATIC void
+mklocal(char *name)
+{
+	struct localvar *lvp;
+	struct var **vpp;
+	struct var *vp;
+
+	INTOFF;
+	lvp = ckmalloc(sizeof (struct localvar));
+	if (name[0] == '-' && name[1] == '\0') {
+		char *p;
+		p = ckmalloc(sizeof(optlist));
+		lvp->text = memcpy(p, optlist, sizeof(optlist));
+		vp = NULL;
+	} else {
+		char *eq;
+
+		vpp = hashvar(name);
+		vp = *findvar(vpp, name);
+		eq = strchr(name, '=');
+		if (vp == NULL) {
+			if (eq)
+				setvareq(name, VSTRFIXED);
+			else
+				setvar(name, NULL, VSTRFIXED);
+			vp = *vpp;	/* the new variable */
+			lvp->flags = VUNSET;
+		} else {
+			lvp->text = vp->text;
+			lvp->flags = vp->flags;
+			vp->flags |= VSTRFIXED|VTEXTFIXED;
+			if (eq)
+				setvareq(name, 0);
+		}
+	}
+	lvp->vp = vp;
+	lvp->next = localvars;
+	localvars = lvp;
+	INTON;
+}
+
+
+/*
+ * Called after a function returns.
+ * Interrupts must be off.
+ */
+
+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));
+			ckfree(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)
+				ckfree(vp->text);
+			vp->flags = lvp->flags;
+			vp->text = lvp->text;
+		}
+		ckfree(lvp);
+	}
+}
+
+
+/*
+ * The unset builtin command.  We unset the function before we unset the
+ * variable to allow a function to be unset when there is a readonly variable
+ * with the same name.
+ */
+
+int
+unsetcmd(int argc, char **argv)
+{
+	char **ap;
+	int i;
+	int flag = 0;
+	int ret = 0;
+
+	while ((i = nextopt("vf")) != '\0') {
+		flag = i;
+	}
+
+	for (ap = argptr; *ap ; ap++) {
+		if (flag != 'f') {
+			i = unsetvar(*ap);
+			ret |= i;
+			if (!(i & 2))
+				continue;
+		}
+		if (flag != 'v')
+			unsetfunc(*ap);
+	}
+	return ret & 1;
+}
+
+
+/*
+ * Unset the specified variable.
+ */
+
+int
+unsetvar(const char *s)
+{
+	struct var **vpp;
+	struct var *vp;
+	int retval;
+
+	vpp = findvar(hashvar(s), s);
+	vp = *vpp;
+	retval = 2;
+	if (vp) {
+		int flags = vp->flags;
+
+		retval = 1;
+		if (flags & VREADONLY)
+			goto out;
+		if (flags & VUNSET)
+			goto ok;
+		if ((flags & VSTRFIXED) == 0) {
+			INTOFF;
+			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
+				ckfree(vp->text);
+			*vpp = vp->next;
+			ckfree(vp);
+			INTON;
+		} else {
+			setvar(s, 0, 0);
+			vp->flags &= ~VEXPORT;
+		}
+ok:
+		retval = 0;
+	}
+
+out:
+	return retval;
+}
+
+
+
+/*
+ * Find the appropriate entry in the hash table from the name.
+ */
+
+STATIC struct var **
+hashvar(const char *p)
+{
+	unsigned int hashval;
+
+	hashval = ((unsigned char) *p) << 4;
+	while (*p && *p != '=')
+		hashval += (unsigned char) *p++;
+	return &vartab[hashval % VTABSIZE];
+}
+
+
+
+/*
+ * Compares two strings up to the first = or '\0'.  The first
+ * string must be terminated by '='; the second may be terminated by
+ * either '=' or '\0'.
+ */
+
+int
+varcmp(const char *p, const char *q)
+{
+	int c, d;
+
+	while ((c = *p) == (d = *q)) {
+		if (!c || c == '=')
+			goto out;
+		p++;
+		q++;
+	}
+	if (c == '=')
+		c = 0;
+	if (d == '=')
+		d = 0;
+out:
+	return c - d;
+}
+
+STATIC int
+vpcmp(const void *a, const void *b)
+{
+	return varcmp(*(const char **)a, *(const char **)b);
+}
+
+STATIC struct var **
+findvar(struct var **vpp, const char *name)
+{
+	for (; *vpp; vpp = &(*vpp)->next) {
+		if (varequal((*vpp)->text, name)) {
+			break;
+		}
+	}
+	return vpp;
+}
diff --git a/ash/var.h b/dash/var.h
similarity index 77%
rename from ash/var.h
rename to dash/var.h
index bc3a837..c3c2ca7 100644
--- a/ash/var.h
+++ b/dash/var.h
@@ -1,8 +1,8 @@
-/*	$NetBSD: var.h,v 1.22 2003/08/07 09:05:39 agc Exp $	*/
-
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1997-2005
+ *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -47,12 +47,13 @@
 #define VUNSET		0x20	/* the variable is not set */
 #define VNOFUNC		0x40	/* don't call the callback function */
 #define VNOSET		0x80	/* do not set variable - just readonly test */
+#define VNOSAVE		0x100	/* when text is on the heap before setvareq */
 
 
 struct var {
 	struct var *next;		/* next entry in hash list */
 	int flags;			/* flags are defined above */
-	char *text;			/* name=value */
+	const char *text;		/* name=value */
 	void (*func)(const char *);
 					/* function to be called when  */
 					/* the variable gets set/unset */
@@ -63,28 +64,40 @@
 	struct localvar *next;		/* next local variable in list */
 	struct var *vp;			/* the variable that was made local */
 	int flags;			/* saved flags */
-	char *text;			/* saved text */
+	const char *text;		/* saved text */
 };
 
 
-struct localvar *localvars;
+extern struct localvar *localvars;
+extern struct var varinit[];
 
 #if ATTY
-extern struct var vatty;
+#define vatty varinit[0]
+#define vifs varinit[1]
+#else
+#define vifs varinit[0]
 #endif
-extern struct var vifs;
-extern struct var vmail;
-extern struct var vmpath;
-extern struct var vpath;
-extern struct var vps1;
-extern struct var vps2;
-extern struct var vps4;
-#ifdef KLIBC_SH_HISTORY
-extern struct var vterm;
-extern struct var vtermcap;
-extern struct var vhistsize;
+#define vmail (&vifs)[1]
+#define vmpath (&vmail)[1]
+#define vpath (&vmpath)[1]
+#define vps1 (&vpath)[1]
+#define vps2 (&vps1)[1]
+#define vps4 (&vps2)[1]
+#define voptind (&vps4)[1]
+#ifndef SMALL
+#define vterm (&voptind)[1]
+#define vhistsize (&vterm)[1]
 #endif
 
+#ifdef IFS_BROKEN
+extern const char defifsvar[];
+#define defifs (defifsvar + 4)
+#else
+extern const char defifs[];
+#endif
+extern const char defpathvar[];
+#define defpath (defpathvar + 5)
+
 /*
  * The following macros access the values of the above variables.
  * They have to skip over the name.  They return the null string
@@ -100,7 +113,7 @@
 #define ps2val()	(vps2.text + 4)
 #define ps4val()	(vps4.text + 4)
 #define optindval()	(voptind.text + 7)
-#ifdef KLIBC_SH_HISTORY
+#ifndef SMALL
 #define histsizeval()	(vhistsize.text + 9)
 #define termval()	(vterm.text + 5)
 #endif
@@ -116,17 +129,18 @@
 struct strlist;
 void listsetvar(struct strlist *, int);
 char *lookupvar(const char *);
-char *bltinlookup(const char *, int);
-char **environment(void);
-void shprocvar(void);
+char *bltinlookup(const char *);
+char **listvars(int, int, char ***);
+#define environment() listvars(VEXPORT, VUNSET, 0)
 int showvars(const char *, int, int);
 int exportcmd(int, char **);
 int localcmd(int, char **);
-void mklocal(const char *, int);
-void listmklocal(struct strlist *, int);
 void poplocalvars(void);
-int setvarcmd(int, char **);
 int unsetcmd(int, char **);
-int unsetvar(const char *, int);
+int unsetvar(const char *);
 int setvarsafe(const char *, const char *, int);
-void print_quoted(const char *);
+int varcmp(const char *, const char *);
+
+static inline int varequal(const char *a, const char *b) {
+	return !varcmp(a, b);
+}