summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Turk <satai@gentoo.org>2003-02-09 09:36:31 +0000
committerMatthew Turk <satai@gentoo.org>2003-02-09 09:36:31 +0000
commitbb490d1e512c36fba93fefe22ce06a5ac84ae186 (patch)
treef103e8fa495b349fc6891d09c9dd972f9fe8c9cb /app-shells
parentNew pre-release version. (diff)
downloadhistorical-bb490d1e512c36fba93fefe22ce06a5ac84ae186.tar.gz
historical-bb490d1e512c36fba93fefe22ce06a5ac84ae186.tar.bz2
historical-bb490d1e512c36fba93fefe22ce06a5ac84ae186.zip
Ash, the netbsd shell. Closes 15319 -- thanks to Tavis Ormandy!
Diffstat (limited to 'app-shells')
-rw-r--r--app-shells/ash/ChangeLog8
-rw-r--r--app-shells/ash/ash-1.6.ebuild46
-rw-r--r--app-shells/ash/files/dash-ash-hetio-yacc.diff17336
-rw-r--r--app-shells/ash/files/digest-ash-1.61
4 files changed, 17391 insertions, 0 deletions
diff --git a/app-shells/ash/ChangeLog b/app-shells/ash/ChangeLog
new file mode 100644
index 000000000000..90d71f3d179d
--- /dev/null
+++ b/app-shells/ash/ChangeLog
@@ -0,0 +1,8 @@
+# ChangeLog for <CATEGORY>/<PACKAGE_NAME>
+# Copyright 2002-2003 Gentoo Technologies, Inc.; Distributed under the GPL v2
+# $Header: /var/cvsroot/gentoo-x86/app-shells/ash/ChangeLog,v 1.1 2003/02/09 09:36:30 satai Exp $
+
+*ash-1.6 (09 Feb 2003)
+
+ 09 Feb 2003; Matthew Turk <satai@gentoo.org> :
+ Initial import. Ebuild submitted by Tavis Ormandy <taviso@sdf.lonestar.org>
diff --git a/app-shells/ash/ash-1.6.ebuild b/app-shells/ash/ash-1.6.ebuild
new file mode 100644
index 000000000000..cd510ba8880e
--- /dev/null
+++ b/app-shells/ash/ash-1.6.ebuild
@@ -0,0 +1,46 @@
+# Copyright 1999-2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/cvsroot/gentoo-x86/app-shells/ash/ash-1.6.ebuild,v 1.1 2003/02/09 09:36:31 satai Exp $
+
+inherit eutils
+
+DESCRIPTION="NetBSD's lightweight bourne shell"
+HOMEPAGE="http://cvsweb.netbsd.org/bsdweb.cgi/src/bin/sh/"
+SRC_URI="ftp://ftp.netbsd.org/pub/NetBSD/NetBSD-release-1-6/tar_files/src/bin.tar.gz"
+LICENSE="BSD"
+SLOT="0"
+KEYWORDS="~alpha ~x86"
+IUSE=""
+DEPEND="virtual/glibc
+ sys-devel/pmake
+ sys-apps/sed
+ dev-util/yacc"
+S=${WORKDIR}/bin_NetBSD-1.6release/src/bin/sh
+
+src_unpack() {
+ mkdir ${WORKDIR}/bin_NetBSD-1.6release
+ (cd ${WORKDIR}/bin_NetBSD-1.6release; tar zxv --no-same-owner \
+ -f ${DISTDIR}/bin.tar.gz src/bin/sh)
+ epatch ${FILESDIR}/dash-ash-hetio-yacc.diff
+}
+src_compile() {
+ cd ${S}
+ # pmake name conflicts, use full path
+ /usr/bin/pmake CFLAGS:="-Wall -DBSD=1 -D_GNU_SOURCE -DGLOB_BROKEN \
+ -DHAVE_VASPRINTF=1 -DIFS_BROKEN -DGCC_BROKEN_NG -D__COPYRIGHT\(x\)=\
+ -D__RCSID\(x\)= -D_DIAGASSERT\(x\)= -g -O2 -fstrict-aliasing ${CFLAGS}" \
+ YACC:="sh ${S}/yaccfe.sh" || die "pmake failed"
+ cd -
+}
+
+src_install() {
+ install -D -g root -m 0755 -o root -s ${S}/sh ${D}/bin/ash || {
+ die "install failed."
+ }
+ install -D -g root -m 0644 -o root ${S}/sh.1 ${D}/usr/man/man1/ash.1 || {
+ die "install failed."
+ }
+ gzip ${D}/usr/man/man1/ash.1
+ dosym /usr/man/man1/ash.1.gz /usr/man/man1/sh.1.gz
+}
+
diff --git a/app-shells/ash/files/dash-ash-hetio-yacc.diff b/app-shells/ash/files/dash-ash-hetio-yacc.diff
new file mode 100644
index 000000000000..f9513d835fc4
--- /dev/null
+++ b/app-shells/ash/files/dash-ash-hetio-yacc.diff
@@ -0,0 +1,17336 @@
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/alias.c bin_NetBSD-1.6release/src/bin/sh/alias.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/alias.c 1998-05-20 12:07:30.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/alias.c 2003-02-08 14:35:42.000000000 +0000
+@@ -60,8 +60,9 @@
+ struct alias *atab[ATABSIZE];
+
+ STATIC void setalias __P((char *, char *));
+-STATIC int unalias __P((char *));
+-STATIC struct alias **hashalias __P((char *));
++STATIC struct alias **hashalias __P((const char *));
++STATIC struct alias *freealias __P((struct alias *));
++STATIC struct alias **__lookupalias __P((const char *));
+
+ STATIC
+ void
+@@ -70,110 +71,58 @@
+ {
+ struct alias *ap, **app;
+
+- app = hashalias(name);
+- for (ap = *app; ap; ap = ap->next) {
+- if (equal(name, ap->name)) {
++ app = __lookupalias(name);
++ ap = *app;
+ INTOFF;
++ if (ap) {
++ if (!(ap->flag & ALIASINUSE)) {
+ ckfree(ap->val);
+- ap->val = savestr(val);
+- INTON;
+- return;
+- }
+ }
++ ap->val = savestr(val);
++ ap->flag &= ~ALIASDEAD;
++ } else {
+ /* 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;
++ ap->flag = 0;
++ ap->next = 0;
+ *app = ap;
++ }
+ INTON;
+ }
+
+-STATIC int
++int
+ unalias(name)
+ char *name;
+ {
+- struct alias *ap, **app;
++ struct alias **app;
+
+- app = hashalias(name);
++ app = __lookupalias(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 {
++ if (*app) {
+ INTOFF;
+- *app = ap->next;
+- ckfree(ap->name);
+- ckfree(ap->val);
+- ckfree(ap);
++ *app = freealias(*app);
+ INTON;
+- }
+ return (0);
+ }
+- }
+
+ return (1);
+ }
+
+-#ifdef mkinit
+-MKINIT void rmaliases __P((void));
+-
+-SHELLPROC {
+- rmaliases();
+-}
+-#endif
+-
+ void
+ rmaliases() {
+- struct alias *ap, *tmp;
++ struct alias *ap, **app;
+ 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);
++ app = &atab[i];
++ for (ap = *app; ap; ap = *app) {
++ *app = freealias(*app);
++ if (ap == *app) {
++ app = &ap->next;
++ }
+ }
+ }
+ INTON;
+@@ -181,20 +130,14 @@
+
+ struct alias *
+ lookupalias(name, check)
+- char *name;
++ const char *name;
+ int check;
+ {
+- struct alias *ap = *hashalias(name);
++ struct alias *ap = *__lookupalias(name);
+
+- for (; ap; ap = ap->next) {
+- if (equal(name, ap->name)) {
+- if (check && (ap->flag & ALIASINUSE))
++ if (check && ap && (ap->flag & ALIASINUSE))
+ return (NULL);
+ return (ap);
+- }
+- }
+-
+- return (NULL);
+ }
+
+ /*
+@@ -214,18 +157,17 @@
+
+ for (i = 0; i < ATABSIZE; i++)
+ for (ap = atab[i]; ap; ap = ap->next) {
+- if (*ap->name != '\0')
+- out1fmt("alias %s=%s\n", ap->name, ap->val);
++ printalias(ap);
+ }
+ 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);
++ if ((ap = *__lookupalias(n)) == NULL) {
++ outfmt(out2, "%s: %s not found\n", "alias", n);
+ ret = 1;
+ } else
+- out1fmt("alias %s=%s\n", n, ap->val);
++ printalias(ap);
+ }
+ else {
+ *v++ = '\0';
+@@ -249,15 +191,19 @@
+ return (0);
+ }
+ }
+- for (i = 0; *argptr; argptr++)
+- i = unalias(*argptr);
++ for (i = 0; *argptr; argptr++) {
++ if (unalias(*argptr)) {
++ outfmt(out2, "%s: %s not found\n", "unalias", *argptr);
++ i = 1;
++ }
++ }
+
+ return (i);
+ }
+
+ STATIC struct alias **
+ hashalias(p)
+- char *p;
++ const char *p;
+ {
+ unsigned int hashval;
+
+@@ -266,3 +212,37 @@
+ hashval+= *p++;
+ return &atab[hashval % ATABSIZE];
+ }
++
++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) {
++ struct alias **app = hashalias(name);
++
++ for (; *app; app = &(*app)->next) {
++ if (equal(name, (*app)->name)) {
++ break;
++ }
++ }
++
++ return app;
++}
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/alias.h bin_NetBSD-1.6release/src/bin/sh/alias.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/alias.h 1995-10-14 00:43:54.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/alias.h 2003-02-08 14:35:42.000000000 +0000
+@@ -39,6 +39,7 @@
+ */
+
+ #define ALIASINUSE 1
++#define ALIASDEAD 2
+
+ struct alias {
+ struct alias *next;
+@@ -47,7 +48,9 @@
+ int flag;
+ };
+
+-struct alias *lookupalias __P((char *, int));
++struct alias *lookupalias __P((const char *, int));
+ int aliascmd __P((int, char **));
+ int unaliascmd __P((int, char **));
+ void rmaliases __P((void));
++int unalias __P((char *));
++void printalias __P((const struct alias *));
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/arith_lex.l bin_NetBSD-1.6release/src/bin/sh/arith_lex.l
+--- bin_NetBSD-1.6release.orig/src/bin/sh/arith_lex.l 1999-02-05 12:04:50.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/arith_lex.l 2003-02-08 14:35:42.000000000 +0000
+@@ -52,7 +52,7 @@
+ #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;
+@@ -84,7 +84,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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/arith.y bin_NetBSD-1.6release/src/bin/sh/arith.y
+--- bin_NetBSD-1.6release.orig/src/bin/sh/arith.y 2001-02-05 11:15:29.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/arith.y 2003-02-08 14:35:42.000000000 +0000
+@@ -55,6 +55,7 @@
+
+ const char *arith_buf, *arith_startbuf;
+
++int yyparse __P((void));
+ void yyerror __P((const char *));
+ #ifdef TESTARITH
+ int main __P((int , char *[]));
+@@ -84,8 +85,8 @@
+
+
+ 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; }
+@@ -166,7 +167,7 @@
+ p = grabstackstr(concat);
+ }
+ } else
+- p = "";
++ p = nullstr;
+
+ i = arith(p);
+
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/arith_yylex.c bin_NetBSD-1.6release/src/bin/sh/arith_yylex.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/arith_yylex.c 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/arith_yylex.c 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,162 @@
++/* $Id: dash-ash-hetio-yacc.diff,v 1.1 2003/02/09 09:36:31 satai Exp $ */
++
++/*-
++ * 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:
++ 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 = strtol(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;
++ }
++ break;
++ }
++
++ buf++;
++out:
++ arith_buf = buf;
++ return value;
++}
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/assignbltins.def bin_NetBSD-1.6release/src/bin/sh/assignbltins.def
+--- bin_NetBSD-1.6release.orig/src/bin/sh/assignbltins.def 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/assignbltins.def 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,4 @@
++alias
++export
++local
++readonly
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/bltin/bltin.h bin_NetBSD-1.6release/src/bin/sh/bltin/bltin.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/bltin/bltin.h 1997-07-05 12:12:37.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/bltin/bltin.h 2003-02-08 14:35:42.000000000 +0000
+@@ -47,7 +47,10 @@
+ #include "../shell.h"
+ #include "../mystring.h"
+ #ifdef SHELL
++#include "../error.h"
++#include "../memalloc.h"
+ #include "../output.h"
++#ifndef USE_GLIBC_STDIO
+ #define stdout out1
+ #define stderr out2
+ #define printf out1fmt
+@@ -56,12 +59,9 @@
+ #define fprintf outfmt
+ #define fputs outstr
+ #define fflush flushout
++#define ferror outerr
++#endif
+ #define INITARGS(argv)
+-#define warnx(a, b, c) { \
+- char buf[64]; \
+- (void)snprintf(buf, sizeof(buf), a, b, c); \
+- error("%s", buf); \
+-}
+
+ #else
+ #undef NULL
+@@ -70,8 +70,6 @@
+ #define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
+ #endif
+
+-pointer stalloc __P((int));
+-void error __P((char *, ...));
+ int echocmd __P((int, char **));
+
+
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/bltin/echo.c bin_NetBSD-1.6release/src/bin/sh/bltin/echo.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/bltin/echo.c 1996-11-03 12:06:22.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/bltin/echo.c 2003-02-08 14:35:42.000000000 +0000
+@@ -44,64 +44,48 @@
+
+ #define main echocmd
+
++#ifdef USE_GLIBC_STDIO
++#include <stdio.h>
++
++#include "../mystring.h"
++#else
+ #include "bltin.h"
++#endif
+
+-/* #define eflag 1 */
++int print_escape_str(const char *);
+
+ int
+-main(argc, argv) char **argv; {
+- register char **ap;
+- register char *p;
+- register char c;
+- int count;
++main(int argc, char **argv) {
+ int nflag = 0;
+-#ifndef eflag
+- int eflag = 0;
+-#endif
++ int c = ' ';
+
+- ap = argv;
+- if (argc)
+- ap++;
+- if ((p = *ap) != NULL) {
+- if (equal(p, "-n")) {
+- nflag++;
+- ap++;
+- } else if (equal(p, "-e")) {
+-#ifndef eflag
+- eflag++;
+-#endif
+- ap++;
++ argv++;
++ if (*argv && equal(*argv, "-n")) {
++ argv++;
++ nflag = 1;
+ }
++
++ if (!*argv) {
++ goto end;
+ }
+- while ((p = *ap++) != NULL) {
+- while ((c = *p++) != '\0') {
+- if (c == '\\' && eflag) {
+- switch (*p++) {
+- case 'b': c = '\b'; break;
+- case 'c': return 0; /* exit */
+- 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');
++
++ do {
++ if (print_escape_str(*argv)) {
+ break;
+- default:
+- p--;
++ }
++ if (!*++argv) {
++end:
++ if (nflag) {
+ break;
+ }
++ c = '\n';
+ }
+ putchar(c);
+- }
+- if (*ap)
+- putchar(' ');
+- }
+- if (! nflag)
+- putchar('\n');
++ } while (c == ' ');
++#ifdef SHELL
+ return 0;
++#else
++ fflush(stdout);
++ return ferror(stdout);
++#endif
+ }
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/bltin/printf.1 bin_NetBSD-1.6release/src/bin/sh/bltin/printf.1
+--- bin_NetBSD-1.6release.orig/src/bin/sh/bltin/printf.1 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/bltin/printf.1 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,281 @@
++.\" $NetBSD: printf.1,v 1.15 2002/02/08 01:36:31 ross Exp $
++.\"
++.\" Copyright (c) 1989, 1990, 1993
++.\" The Regents of the University of California. 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. 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: @(#)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 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\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 c ,
++.Cm d ,
++and
++.Cm s ,
++formats, this option has no effect. For the
++.Cm o
++formats 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; 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.
++.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; however if the
++precision is 0 or missing, 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
++.Sh STANDARDS
++The
++.Nm
++utility conforms to
++.St -p1003.2-92 .
++.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.
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/bltin/printf.c bin_NetBSD-1.6release/src/bin/sh/bltin/printf.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/bltin/printf.c 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/bltin/printf.c 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,532 @@
++/* $NetBSD: printf.c,v 1.24 2002/06/14 11:32:15 tron 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. 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.
++ */
++
++#include <sys/cdefs.h>
++#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.24 2002/06/14 11:32:15 tron Exp $");
++#endif
++#endif /* not lint */
++
++#include <sys/types.h>
++
++#include <ctype.h>
++#include <err.h>
++#include <errno.h>
++#include <inttypes.h>
++#include <limits.h>
++#include <locale.h>
++#include <stdarg.h>
++#ifndef SHELL
++#include <stdio.h>
++#endif
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++
++int print_escape_str(const char *);
++static size_t print_escape(const char *);
++
++static int getchr(void);
++static double getdouble(void);
++static intmax_t getintmax(void);
++static uintmax_t getuintmax __P ((void));
++static char *getstr(void);
++static char *mklong(const char *, int);
++static void check_conversion(const char *, const char *);
++static void usage(void);
++
++static int rval;
++static char **gargv;
++
++#ifdef BUILTIN
++int progprintf(int, char **);
++#else
++int main(int, char **);
++#endif
++
++#define isodigit(c) ((c) >= '0' && (c) <= '7')
++#define octtobin(c) ((c) - '0')
++#define hextobin(c) ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
++
++#ifdef SHELL
++#define main printfcmd
++#include "bltin.h"
++#else
++#define nullstr ""
++#endif
++
++#define PF(f, func) { \
++ switch (param - array) { \
++ case 0: \
++ (void)printf(f, func); \
++ break; \
++ case 1: \
++ (void)printf(f, array[0], func); \
++ break; \
++ default: \
++ (void)printf(f, array[0], array[1], func); \
++ break; \
++ } \
++}
++
++int
++#ifdef BUILTIN
++progprintf(int argc, char **argv)
++#else
++main(int argc, char **argv)
++#endif
++{
++ char *fmt;
++ char *format;
++
++#if !defined(SHELL) && !defined(BUILTIN)
++ (void)setlocale (LC_ALL, "");
++#endif
++
++ 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; *fmt; fmt++) {
++ switch (*fmt) {
++ case '%': {
++ char *start;
++ char convch, nextch;
++ int array[2];
++ int *param;
++
++ start = fmt++;
++
++ if (*fmt == '%') {
++ (void)putchar('%');
++ break;
++ } else if (*fmt == 'b') {
++ char *p = getstr();
++ if (print_escape_str(p)) {
++ return (rval);
++ }
++ break;
++ }
++
++ 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);
++ if (!*fmt) {
++ warnx ("missing format character");
++ return(1);
++ }
++
++ convch = *fmt;
++ nextch = *(fmt + 1);
++ *(fmt + 1) = '\0';
++ switch(convch) {
++ case 'c': {
++ char p = getchr();
++ PF(start, p);
++ break;
++ }
++ case 's': {
++ char *p = getstr();
++ PF(start, p);
++ break;
++ }
++ case 'd':
++ case 'i': {
++ char *f = mklong(start, convch);
++ intmax_t p = getintmax();
++ PF(f, p);
++ break;
++ }
++ case 'o':
++ case 'u':
++ case 'x':
++ case 'X': {
++ char *f = mklong(start, convch);
++ uintmax_t p = getuintmax();
++ 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);
++ return(1);
++ }
++ *(fmt + 1) = nextch;
++ break;
++ }
++
++ case '\\':
++ fmt += print_escape(fmt);
++ break;
++
++ default:
++ (void)putchar(*fmt);
++ break;
++ }
++ }
++ } while (gargv > argv && *gargv);
++
++#ifdef SHELL
++ return (rval);
++#else
++ fflush(stdout);
++ return (rval ? rval : ferror(stdout));
++#endif
++}
++
++
++/*
++ * Print SysV echo(1) style escape string
++ * Halts processing string and returns 1 if a \c escape is encountered.
++ */
++int
++print_escape_str(const char *str)
++{
++ char value;
++ char c;
++
++ for (; (value = *str); str++) {
++ if (value == '\\') {
++ c = *++str;
++ /*
++ * %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 (c == '0') {
++ unsigned j;
++ unsigned char i;
++ i = 3;
++ j = 0;
++ do {
++ unsigned k = octtobin(*++str);
++ if (k > 7) {
++ str--;
++ break;
++ }
++ j <<= 3;
++ j += k;
++ } while (--i);
++ value = j;
++ } else if (c == 'c') {
++ return 1;
++ } else {
++ str--;
++ str += print_escape(str);
++ continue;
++ }
++ }
++ putchar(value);
++ }
++
++ return 0;
++}
++
++/*
++ * Print "standard" escape characters
++ */
++static size_t
++print_escape(const char *str)
++{
++ const char *start = str;
++ int value;
++ size_t c = 1;
++
++ str++;
++
++ switch (*str) {
++ case '0': case '1': case '2': case '3':
++ case '4': case '5': case '6': case '7':
++ for (c = 4, value = 0; --c && isodigit(*str); str++) {
++ value <<= 3;
++ value += octtobin(*str);
++ }
++ c = str - start - 1;
++ break;
++
++#ifdef notrequired
++ case 'x':
++ str++;
++ for (value = 0; isxdigit((unsigned char)*str); str++) {
++ value <<= 4;
++ value += hextobin(*str);
++ }
++ if (value > UCHAR_MAX) {
++ warnx ("escape sequence out of range for character");
++ rval = 1;
++ }
++ c = str - start - 1;
++ break;
++#endif
++
++ case '\\': /* backslash */
++ value = '\\';
++ break;
++
++#ifdef notrequired
++ case '\'': /* single quote */
++ value = '\'';
++ break;
++
++ case '"': /* double quote */
++ value = '"';
++ break;
++#endif
++
++ case 'a': /* alert */
++ value = '\a';
++ break;
++
++ case 'b': /* backspace */
++ value = '\b';
++ break;
++
++#ifdef notrequired
++ case 'e': /* escape */
++#ifdef __GNUC__
++ value = '\e';
++#else
++ value = 033;
++#endif
++ break;
++#endif
++
++ case 'f': /* form-feed */
++ value = '\f';
++ break;
++
++ case 'n': /* newline */
++ value = '\n';
++ break;
++
++ case 'r': /* carriage-return */
++ value = '\r';
++ break;
++
++ case 't': /* tab */
++ value = '\t';
++ break;
++
++ case 'v': /* vertical-tab */
++ value = '\v';
++ break;
++
++ default:
++#if 0
++ value = *str;
++ warnx("unknown escape sequence `\\%c'", *str);
++ rval = 1;
++#else
++ value = '\\';
++ c = 0;
++#endif
++ break;
++ }
++
++ putchar(value);
++ return c;
++}
++
++static char *
++mklong(const char *str, int ch)
++{
++ static char copy[64];
++ size_t len;
++
++ len = strlen(str) + 2;
++ (void)memmove(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 *ep;
++ const char *arg = *gargv;
++
++ if (!arg)
++ goto out;
++
++ gargv++;
++
++ val = (unsigned char) arg[1];
++ if (*arg == '\"' || *arg == '\'')
++ goto out;
++
++ errno = 0;
++ val = strtoimax(arg, &ep, 0);
++ check_conversion(arg, ep);
++out:
++ return val;
++}
++
++static uintmax_t
++getuintmax(void)
++{
++ uintmax_t val = 0;
++ char *ep;
++ const char *arg = *gargv;
++
++ if (!arg)
++ goto out;
++
++ gargv++;
++
++ val = (unsigned char) arg[1];
++ if (*arg == '\"' || *arg == '\'')
++ goto out;
++
++ errno = 0;
++ val = strtoumax(arg, &ep, 0);
++ check_conversion(arg, ep);
++out:
++ return val;
++}
++
++static double
++getdouble(void)
++{
++ double val = 0;
++ char *ep;
++ const char *arg = *gargv;
++
++ if (!arg)
++ goto out;
++
++ gargv++;
++
++ if (*arg == '\"' || *arg == '\'') {
++ val = (unsigned char) arg[1];
++ goto out;
++ }
++
++ errno = 0;
++ val = strtod(arg, &ep);
++ check_conversion(arg, ep);
++out:
++ 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;
++ }
++}
++
++static void
++usage(void)
++{
++ (void)fprintf(stderr, "usage: printf format [arg ...]\n");
++}
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/bltin/test.1 bin_NetBSD-1.6release/src/bin/sh/bltin/test.1
+--- bin_NetBSD-1.6release.orig/src/bin/sh/bltin/test.1 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/bltin/test.1 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,312 @@
++.\" $NetBSD: test.1,v 1.17 2002/02/08 01:22:01 ross 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
++.\" 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. 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.
++.\"
++.\" @(#)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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/bltin/test.c bin_NetBSD-1.6release/src/bin/sh/bltin/test.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/bltin/test.c 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/bltin/test.c 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,545 @@
++/* $NetBSD: test.c,v 1.25 2002/05/25 23:12:16 wiz Exp $ */
++
++/*
++ * test(1); version 7-like -- author Erik Baalbergen
++ * modified by Eric Gisin to be used as built-in.
++ * modified by Arnold Robbins to add SVR3 compatibility
++ * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
++ * modified by J.T. Conklin for NetBSD.
++ *
++ * This program is in the Public Domain.
++ */
++
++#include <sys/cdefs.h>
++#ifndef lint
++__RCSID("$NetBSD: test.c,v 1.25 2002/05/25 23:12:16 wiz Exp $");
++#endif
++
++#include <sys/stat.h>
++#include <sys/types.h>
++
++#include <ctype.h>
++#include <err.h>
++#include <errno.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include <stdarg.h>
++#include "bltin.h"
++
++/* test(1) accepts the following grammar:
++ oexpr ::= aexpr | aexpr "-o" oexpr ;
++ aexpr ::= nexpr | nexpr "-a" aexpr ;
++ nexpr ::= primary | "!" primary
++ primary ::= unary-operator operand
++ | operand binary-operator operand
++ | operand
++ | "(" oexpr ")"
++ ;
++ unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
++ "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
++
++ binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
++ "-nt"|"-ot"|"-ef";
++ operand ::= <any legal UNIX file name>
++*/
++
++enum token {
++ EOI,
++ FILRD,
++ FILWR,
++ FILEX,
++ FILEXIST,
++ FILREG,
++ FILDIR,
++ FILCDEV,
++ FILBDEV,
++ FILFIFO,
++ FILSOCK,
++ FILSYM,
++ FILGZ,
++ FILTT,
++ FILSUID,
++ FILSGID,
++ FILSTCK,
++ FILNT,
++ FILOT,
++ FILEQ,
++ FILUID,
++ FILGID,
++ STREZ,
++ STRNZ,
++ STREQ,
++ STRNE,
++ STRLT,
++ STRGT,
++ INTEQ,
++ INTNE,
++ INTGE,
++ INTGT,
++ INTLE,
++ INTLT,
++ UNOT,
++ BAND,
++ BOR,
++ LPAREN,
++ RPAREN,
++ OPERAND
++};
++
++enum token_types {
++ UNOP,
++ BINOP,
++ BUNOP,
++ BBINOP,
++ PAREN
++};
++
++static struct t_op {
++ const char *op_text;
++ short op_num, op_type;
++} const ops [] = {
++ {"-r", FILRD, UNOP},
++ {"-w", FILWR, UNOP},
++ {"-x", FILEX, UNOP},
++ {"-e", FILEXIST,UNOP},
++ {"-f", FILREG, UNOP},
++ {"-d", FILDIR, UNOP},
++ {"-c", FILCDEV,UNOP},
++ {"-b", FILBDEV,UNOP},
++ {"-p", FILFIFO,UNOP},
++ {"-u", FILSUID,UNOP},
++ {"-g", FILSGID,UNOP},
++ {"-k", FILSTCK,UNOP},
++ {"-s", FILGZ, UNOP},
++ {"-t", FILTT, UNOP},
++ {"-z", STREZ, UNOP},
++ {"-n", STRNZ, UNOP},
++ {"-h", FILSYM, UNOP}, /* for backwards compat */
++ {"-O", FILUID, UNOP},
++ {"-G", FILGID, UNOP},
++ {"-L", FILSYM, UNOP},
++ {"-S", FILSOCK,UNOP},
++ {"=", STREQ, BINOP},
++ {"!=", STRNE, BINOP},
++ {"<", STRLT, BINOP},
++ {">", STRGT, BINOP},
++ {"-eq", INTEQ, BINOP},
++ {"-ne", INTNE, BINOP},
++ {"-ge", INTGE, BINOP},
++ {"-gt", INTGT, BINOP},
++ {"-le", INTLE, BINOP},
++ {"-lt", INTLT, BINOP},
++ {"-nt", FILNT, BINOP},
++ {"-ot", FILOT, BINOP},
++ {"-ef", FILEQ, BINOP},
++ {"!", UNOT, BUNOP},
++ {"-a", BAND, BBINOP},
++ {"-o", BOR, BBINOP},
++ {"(", LPAREN, PAREN},
++ {")", RPAREN, PAREN},
++ {0, 0, 0}
++};
++
++static char **t_wp;
++static struct t_op const *t_wp_op;
++
++static void syntax(const char *, const char *);
++static int oexpr(enum token);
++static int aexpr(enum token);
++static int nexpr(enum token);
++static int primary(enum token);
++static int binop(void);
++static int filstat(char *, enum token);
++static enum token t_lex(char *);
++static int isoperand(void);
++static int getn(const char *);
++static int newerf(const char *, const char *);
++static int olderf(const char *, const char *);
++static int equalf(const char *, const char *);
++static int test_eaccess(const char *, int);
++static int bash_group_member(gid_t);
++
++#ifndef SHELL
++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 **);
++
++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 ]");
++ argv[argc] = NULL;
++ }
++
++ if (argc < 2)
++ return 1;
++
++ t_wp = &argv[1];
++ res = !oexpr(t_lex(*t_wp));
++
++ if (*t_wp != NULL && *++t_wp != NULL)
++ syntax(*t_wp, "unexpected operator");
++
++ return res;
++}
++
++static void
++syntax(const char *op, const char *msg)
++{
++ if (op && *op)
++ error("%s: %s", op, msg);
++ else
++ error("%s", msg);
++}
++
++static int
++oexpr(enum token n)
++{
++ int res;
++
++ res = aexpr(n);
++ if (t_lex(*++t_wp) == BOR)
++ return oexpr(t_lex(*++t_wp)) || res;
++ t_wp--;
++ return res;
++}
++
++static int
++aexpr(enum token n)
++{
++ int res;
++
++ res = nexpr(n);
++ if (t_lex(*++t_wp) == BAND)
++ return aexpr(t_lex(*++t_wp)) && res;
++ t_wp--;
++ return res;
++}
++
++static int
++nexpr(enum token n)
++{
++ if (n == UNOT)
++ return !nexpr(t_lex(*++t_wp));
++ return primary(n);
++}
++
++static int
++primary(enum token n)
++{
++ enum token nn;
++ int res;
++
++ if (n == EOI)
++ return 0; /* missing expression */
++ if (n == LPAREN) {
++ if ((nn = t_lex(*++t_wp)) == RPAREN)
++ return 0; /* missing expression */
++ res = oexpr(nn);
++ if (t_lex(*++t_wp) != RPAREN)
++ syntax(NULL, "closing paren expected");
++ return res;
++ }
++ if (t_wp_op && t_wp_op->op_type == UNOP) {
++ /* unary expression */
++ if (*++t_wp == NULL)
++ syntax(t_wp_op->op_text, "argument expected");
++ switch (n) {
++ case STREZ:
++ return strlen(*t_wp) == 0;
++ case STRNZ:
++ return strlen(*t_wp) != 0;
++ case FILTT:
++ return isatty(getn(*t_wp));
++ default:
++ return filstat(*t_wp, n);
++ }
++ }
++
++ if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
++ return binop();
++ }
++
++ return strlen(*t_wp) > 0;
++}
++
++static int
++binop(void)
++{
++ const char *opnd1, *opnd2;
++ struct t_op const *op;
++
++ opnd1 = *t_wp;
++ (void) t_lex(*++t_wp);
++ op = t_wp_op;
++
++ if ((opnd2 = *++t_wp) == (char *)0)
++ syntax(op->op_text, "argument expected");
++
++ switch (op->op_num) {
++ case STREQ:
++ return strcmp(opnd1, opnd2) == 0;
++ case STRNE:
++ return strcmp(opnd1, opnd2) != 0;
++ case STRLT:
++ return strcmp(opnd1, opnd2) < 0;
++ case STRGT:
++ return strcmp(opnd1, opnd2) > 0;
++ case INTEQ:
++ return getn(opnd1) == getn(opnd2);
++ case INTNE:
++ return getn(opnd1) != getn(opnd2);
++ case INTGE:
++ return getn(opnd1) >= getn(opnd2);
++ case INTGT:
++ return getn(opnd1) > getn(opnd2);
++ case INTLE:
++ return getn(opnd1) <= getn(opnd2);
++ case INTLT:
++ return getn(opnd1) < getn(opnd2);
++ case FILNT:
++ return newerf (opnd1, opnd2);
++ case FILOT:
++ return olderf (opnd1, opnd2);
++ case FILEQ:
++ return equalf (opnd1, opnd2);
++ default:
++ abort();
++ /* NOTREACHED */
++ }
++}
++
++static int
++filstat(char *nm, enum token mode)
++{
++ struct stat64 s;
++
++ if (mode == FILSYM ? lstat64(nm, &s) : stat64(nm, &s))
++ return 0;
++
++ switch (mode) {
++ case FILRD:
++ return test_eaccess(nm, R_OK) == 0;
++ case FILWR:
++ return test_eaccess(nm, W_OK) == 0;
++ case FILEX:
++ return test_eaccess(nm, X_OK) == 0;
++ case FILEXIST:
++ return 1;
++ case FILREG:
++ return S_ISREG(s.st_mode);
++ case FILDIR:
++ return S_ISDIR(s.st_mode);
++ case FILCDEV:
++ return S_ISCHR(s.st_mode);
++ case FILBDEV:
++ return S_ISBLK(s.st_mode);
++ case FILFIFO:
++ return S_ISFIFO(s.st_mode);
++ case FILSOCK:
++ return S_ISSOCK(s.st_mode);
++ case FILSYM:
++ return S_ISLNK(s.st_mode);
++ case FILSUID:
++ return (s.st_mode & S_ISUID) != 0;
++ case FILSGID:
++ return (s.st_mode & S_ISGID) != 0;
++ case FILSTCK:
++ return (s.st_mode & S_ISVTX) != 0;
++ case FILGZ:
++ return s.st_size > (off_t)0;
++ case FILUID:
++ return s.st_uid == geteuid();
++ case FILGID:
++ return s.st_gid == getegid();
++ default:
++ return 1;
++ }
++}
++
++static enum token
++t_lex(char *s)
++{
++ struct t_op const *op;
++
++ op = ops;
++
++ if (s == 0) {
++ t_wp_op = (struct t_op *)0;
++ return EOI;
++ }
++ while (op->op_text) {
++ if (strcmp(s, op->op_text) == 0) {
++ if ((op->op_type == UNOP && isoperand()) ||
++ (op->op_num == LPAREN && *(t_wp+1) == 0))
++ break;
++ t_wp_op = op;
++ return op->op_num;
++ }
++ op++;
++ }
++ t_wp_op = (struct t_op *)0;
++ return OPERAND;
++}
++
++static int
++isoperand(void)
++{
++ struct t_op const *op;
++ char *s, *t;
++
++ op = ops;
++ if ((s = *(t_wp+1)) == 0)
++ return 1;
++ if ((t = *(t_wp+2)) == 0)
++ return 0;
++ while (op->op_text) {
++ if (strcmp(s, op->op_text) == 0)
++ return op->op_type == BINOP &&
++ (t[0] != ')' || t[1] != '\0');
++ op++;
++ }
++ return 0;
++}
++
++/* atoi with error detection */
++static int
++getn(const char *s)
++{
++ char *p;
++ long r;
++
++ errno = 0;
++ r = strtol(s, &p, 10);
++
++ if (errno != 0)
++ error("%s: out of range", s);
++
++ while (isspace((unsigned char)*p))
++ p++;
++
++ if (*p)
++ error("%s: bad number", s);
++
++ return (int) r;
++}
++
++static int
++newerf (const char *f1, const char *f2)
++{
++ struct stat b1, b2;
++
++ return (stat (f1, &b1) == 0 &&
++ stat (f2, &b2) == 0 &&
++ b1.st_mtime > b2.st_mtime);
++}
++
++static int
++olderf (const char *f1, const char *f2)
++{
++ struct stat b1, b2;
++
++ return (stat (f1, &b1) == 0 &&
++ stat (f2, &b2) == 0 &&
++ b1.st_mtime < b2.st_mtime);
++}
++
++static int
++equalf (const char *f1, const char *f2)
++{
++ struct stat b1, b2;
++
++ return (stat (f1, &b1) == 0 &&
++ stat (f2, &b2) == 0 &&
++ b1.st_dev == b2.st_dev &&
++ b1.st_ino == b2.st_ino);
++}
++
++/* Do the same thing access(2) does, but use the effective uid and gid,
++ and don't make the mistake of telling root that any file is
++ executable. */
++static int
++test_eaccess(const char *path, int mode)
++{
++ struct stat64 st;
++ int euid = geteuid();
++
++ if (stat64(path, &st) < 0)
++ return (-1);
++
++ if (euid == 0) {
++ /* Root can read or write any file. */
++ if (mode != X_OK)
++ return (0);
++
++ /* Root can execute any file that has any one of the execute
++ bits set. */
++ if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
++ return (0);
++ }
++
++ if (st.st_uid == euid) /* owner */
++ mode <<= 6;
++ else if (bash_group_member(st.st_gid))
++ mode <<= 3;
++
++ if (st.st_mode & mode)
++ return (0);
++
++ return (-1);
++}
++
++/* 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);
++#ifdef SHELL
++ group_array = stalloc(ngroups * sizeof(gid_t));
++#else
++ group_array = alloca(ngroups * sizeof(gid_t));
++#endif
++ 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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/bltin/times.c bin_NetBSD-1.6release/src/bin/sh/bltin/times.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/bltin/times.c 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/bltin/times.c 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,32 @@
++/*
++ * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
++ * This file contains code for the times builtin.
++ * $Id: dash-ash-hetio-yacc.diff,v 1.1 2003/02/09 09:36:31 satai Exp $
++ */
++
++#include <sys/times.h>
++#include <unistd.h>
++#ifdef USE_GLIBC_STDIO
++#include <stdio.h>
++#else
++#include "bltin.h"
++#endif
++
++#define main timescmd
++
++int main() {
++ 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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/builtins.def bin_NetBSD-1.6release/src/bin/sh/builtins.def
+--- bin_NetBSD-1.6release.orig/src/bin/sh/builtins.def 2000-04-10 12:02:58.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/builtins.def 2003-02-08 14:35:42.000000000 +0000
+@@ -49,12 +49,13 @@
+ #
+ # NOTE: bltincmd must come first!
+
+-bltincmd command
++bltincmd builtin
+ #alloccmd alloc
+ bgcmd -j bg
+ breakcmd break continue
+ #catfcmd catf
+ cdcmd cd chdir
++commandcmd command
+ dotcmd .
+ echocmd echo
+ evalcmd eval
+@@ -68,12 +69,13 @@
+ fgcmd -j fg
+ getoptscmd getopts
+ hashcmd hash
+-jobidcmd jobid
++#jobidcmd jobid
+ jobscmd jobs
++killcmd -j kill
+ #linecmd line
+ localcmd local
+ #nlechocmd nlecho
+-#printfcmd printf
++printfcmd printf
+ pwdcmd pwd
+ readcmd read
+ returncmd return
+@@ -91,3 +93,4 @@
+ aliascmd alias
+ ulimitcmd ulimit
+ testcmd test [
++timescmd times
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/cd.c bin_NetBSD-1.6release/src/bin/sh/cd.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/cd.c 2001-11-15 11:08:17.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/cd.c 2003-02-08 14:35:42.000000000 +0000
+@@ -70,13 +70,33 @@
+ #include "show.h"
+ #include "cd.h"
+
+-STATIC int docd __P((char *, int));
+-STATIC char *getcomponent __P((void));
+-STATIC void updatepwd __P((char *));
++#define CD_PHYSICAL 1
++#define CD_PRINT 2
+
+-char *curdir = NULL; /* current working directory */
+-char *prevdir; /* previous working directory */
+-STATIC char *cdcomppath;
++STATIC int docd __P((const char *, int));
++STATIC const char *updatepwd __P((const char *));
++STATIC char *getpwd __P((void));
++STATIC int cdopt __P((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(argc, argv)
+@@ -85,296 +105,218 @@
+ {
+ const char *dest;
+ const char *path;
+- char *p;
++ const char *p;
++ char c;
+ struct stat statb;
+- int print = 0;
++ int flags;
+
+- nextopt(nullstr);
+- if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
+- error("HOME not set");
+- if (*dest == '\0')
+- dest = ".";
+- if (dest[0] == '-' && dest[1] == '\0') {
+- dest = prevdir ? prevdir : curdir;
+- print = 1;
+- if (dest)
+- print = 1;
+- else
+- dest = ".";
++ flags = cdopt();
++ dest = *argptr;
++ if (!dest)
++ dest = bltinlookup(homestr);
++ else if (dest[0] == '-' && dest[1] == '\0') {
++ dest = bltinlookup("OLDPWD");
++ flags |= CD_PRINT;
++ goto step7;
+ }
+- 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 (!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 (docd(p, print) >= 0)
+- return 0;
+-
+ }
++ 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);
+ error("can't cd to %s", dest);
+ /* NOTREACHED */
++out:
++ if (flags & CD_PRINT)
++ out1fmt(snlfmt, curdir);
++ return 0;
+ }
+
+
+ /*
+- * Actually do the chdir. In an interactive shell, print the
+- * directory name if "print" is nonzero.
++ * 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(dest, print)
+- char *dest;
+- int print;
++docd(const char *dest, int flags)
+ {
+- char *p;
+- char *q;
+- char *component;
+- struct stat statb;
+- int first;
+- int badstat;
+-
+- TRACE(("docd(\"%s\", %d) called\n", dest, print));
++ const char *dir = 0;
++ int err;
+
+- /*
+- * 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;
+- }
+- }
++ TRACE(("docd(\"%s\", %d) called\n", dest, flags));
+
+ INTOFF;
+- if (chdir(dest) < 0) {
+- INTON;
+- return -1;
++ if (!(flags & CD_PHYSICAL)) {
++ dir = updatepwd(dest);
++ if (dir)
++ dest = dir;
+ }
+- updatepwd(badstat ? NULL : dest);
++ err = chdir(dest);
++ if (err)
++ goto out;
++ setpwd(dir, 1);
++ hashcd();
++out:
+ 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;
++ return err;
+ }
+
+
+-
+ /*
+ * 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.
++ * cd command.
+ */
+
+-STATIC void
+-updatepwd(dir)
+- char *dir;
+- {
++STATIC const char *
++updatepwd(const char *dir)
++{
+ char *new;
+ char *p;
++ char *cdcomppath;
++ const char *lim;
+
+- 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();
+- setvar("PWD", curdir, VEXPORT);
+- INTON;
+- return;
+- }
+- cdcomppath = stalloc(strlen(dir) + 1);
+- scopy(dir, cdcomppath);
++ cdcomppath = sstrdup(dir);
+ STARTSTACKSTR(new);
+ if (*dir != '/') {
+- p = curdir;
+- while (*p)
+- STPUTC(*p++, new);
+- if (p[-1] == '/')
++ 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;
+ }
+- 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);
++ break;
++ } else if (p[1] == '\0')
++ break;
++ /* fall through */
++ default:
++ new = stputs(p, new);
++ USTPUTC('/', new);
+ }
++ p = strtok(0, "/");
+ }
+- if (new == stackblock())
+- STPUTC('/', new);
+- STACKSTRNUL(new);
+- INTOFF;
+- if (prevdir)
+- ckfree(prevdir);
+- prevdir = curdir;
+- curdir = savestr(stackblock());
+- setvar("PWD", curdir, VEXPORT);
+- INTON;
++ 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(argc, argv)
+ int argc;
+ char **argv;
+ {
+- getpwd();
+- out1str(curdir);
+- out1c('\n');
++ int flags;
++ const char *dir = curdir;
++
++ flags = cdopt();
++ if (flags) {
++ if (physdir == nullstr)
++ setpwd(dir, 0);
++ dir = physdir;
++ }
++ out1fmt(snlfmt, dir);
+ return 0;
+ }
+
+-
+-
+-
+-#define MAXPWD 256
+-
+-/*
+- * Find out what the current directory is. If we already know the current
+- * directory, this routine returns immediately.
+- */
+ void
+-getpwd()
++setpwd(const char *val, int setold)
+ {
+- char buf[MAXPWD];
+-
+- if (curdir)
+- return;
+- /*
+- * 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)
++ char *oldcur, *dir;
+
+- if (getcwd(buf, sizeof(buf)) == NULL) {
+- char *pwd = getenv("PWD");
+- struct stat stdot, stpwd;
++ oldcur = dir = curdir;
+
+- 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;
+- }
+- error("getcwd() failed: %s", strerror(errno));
++ if (setold) {
++ setvar("OLDPWD", oldcur, VEXPORT);
+ }
+- curdir = savestr(buf);
+-#else
+- {
+- char *p;
+- int i;
+- int status;
+- struct job *jp;
+- int pip[2];
+-
+ 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 = buf;
+- while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
+- || (i == -1 && errno == EINTR)) {
+- if (i > 0)
+- p += i;
++ if (physdir != nullstr) {
++ if (physdir != oldcur)
++ free(physdir);
++ physdir = nullstr;
+ }
+- (void) close(pip[0]);
+- pip[0] = -1;
+- status = waitforjob(jp);
+- if (status != 0)
+- error((char *)0);
+- if (i < 0 || p == buf || p[-1] != '\n')
+- error("pwd command failed");
+- p[-1] = '\0';
++ 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 = savestr(buf);
++ curdir = dir;
+ INTON;
+-#endif
++ setvar("PWD", dir, VEXPORT);
+ }
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/cd.h bin_NetBSD-1.6release/src/bin/sh/cd.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/cd.h 1997-07-05 12:12:27.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/cd.h 2003-02-08 14:35:42.000000000 +0000
+@@ -34,6 +34,6 @@
+ *
+ */
+
+-void getpwd __P((void));
+ int cdcmd __P((int, char **));
+ int pwdcmd __P((int, char **));
++void setpwd __P((const char *, int));
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/ash.dirs bin_NetBSD-1.6release/src/bin/sh/debian/ash.dirs
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/ash.dirs 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/ash.dirs 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,2 @@
++bin
++usr/share/man/man1
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/ash.postinst bin_NetBSD-1.6release/src/bin/sh/debian/ash.postinst
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/ash.postinst 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/ash.postinst 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,54 @@
++#!/bin/sh
++#
++# post-install script for the Debian GNU/Linux ash package
++#
++# $Id: dash-ash-hetio-yacc.diff,v 1.1 2003/02/09 09:36:31 satai Exp $
++
++set -e
++
++check_divert() {
++ div=$(dpkg-divert --list $1)
++ distrib=${3:-$1.distrib}
++ case $div in
++ '' | *by\ dash)
++ ;;
++ *by\ ash)
++ dst=${div% by ash}
++ dst=${dst##* to }
++
++ # Work around dpkg-divert bug.
++ if [ -e "$dst" ]; then
++ mv "$dst" "$dst.ash-tmp"
++ fi
++ dpkg-divert --remove $1
++ if [ -e "$dst.ash-tmp" ]; then
++ mv "$dst.ash-tmp" "$dst"
++ fi
++
++ dpkg-divert --package dash --divert $distrib --add $1
++ if [ "$dst" != $distrib ] && [ -e "$dst" ]; then
++ mv "$dst" $distrib
++ fi
++ ln -sf $2 $1
++ ;;
++ *)
++ d=${1%/*}
++ if
++ [ -h $1 ] && [ -f $1 ] && [ -f $d/$4 ] &&
++ cmp $1 $d/$4
++ then
++ ln -sf $2 $1
++ fi
++ ;;
++ esac
++}
++
++dcv='dpkg --compare-versions'
++
++if [ "$1" = configure ] && [ -n "$2" ] && $dcv "$2" lt 0.4.3; then
++ check_divert /bin/sh dash '' ash
++ check_divert /usr/share/man/man1/sh.1.gz dash.1.gz \
++ /usr/share/man/man1/sh.distrib.1.gz ash.1.gz
++fi
++
++#DEBHELPER#
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/bsdyacc bin_NetBSD-1.6release/src/bin/sh/debian/bsdyacc
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/bsdyacc 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/bsdyacc 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,20 @@
++#!/bin/sh -e
++
++if echo "$@" | grep -q -- -o; then
++ OUTPUT=$(echo "$@" |
++ sed 's/.*-o[[:blank:]]\+\([^[:blank:]]\+\)\.c.*/\1/')
++ OPTIONS=$(echo "$@" |
++ sed 's/\(.*\)-o[[:blank:]]\+[^[:blank:]]\+\(.*\)/\1\2/')
++ NEW=1
++else
++ OUTPUT=$(echo "$@" |
++ sed -e 's/.*[[:blank:]]\+\([^[:blank:]]\+\)\.y.*/\1/')
++ OPTIONS="$@"
++ NEW=0
++fi
++
++byacc $OPTIONS
++if [ $NEW = 1 ]; then
++ mv y.tab.c $OUTPUT.c
++fi
++mv y.tab.h $OUTPUT.h
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/changelog bin_NetBSD-1.6release/src/bin/sh/debian/changelog
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/changelog 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/changelog 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,868 @@
++dash (0.4.10) unstable; urgency=low
++
++ * Fixed redirection fd leak when execing.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 19 Jan 2003 13:25:41 +1100
++
++dash (0.4.9) unstable; urgency=low
++
++ * Reset exitstatus in evalsubshell if backgnd is true.
++ * Fixed glibc glob syntax error in expand.c.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 11 Jan 2003 16:04:02 +1100
++
++dash (0.4.8) unstable; urgency=low
++
++ * Removed backgnd flag from ncmd due to previous redirection change.
++ * Set lim after the stack stablises in updatepwd (closes: #173884).
++ * Do not clobber the exitstatus after redirection.
++
++ -- Herbert Xu <herbert@debian.org> Mon, 23 Dec 2002 19:50:06 +1100
++
++dash (0.4.7) unstable; urgency=low
++
++ * Merged clearredir with reset code in redir.c.
++ * Redirect before command search in evalcommand (closes: #168862).
++ * Build binary-all packages in binary-indep (closes: #173191).
++
++ -- Herbert Xu <herbert@debian.org> Sat, 21 Dec 2002 13:52:37 +1100
++
++dash (0.4.6) unstable; urgency=low
++
++ * Restored code for leaving job control.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 8 Dec 2002 15:21:58 +1100
++
++dash (0.4.5) unstable; urgency=low
++
++ * Optimised doformat so that vsnprintf is usually called only once.
++ * Reset redirlist in clearredir so that popredir can work (closes: #170247).
++
++ -- Herbert Xu <herbert@debian.org> Sat, 23 Nov 2002 22:09:59 +1100
++
++dash (0.4.4) unstable; urgency=low
++
++ * Fixed duplicate define warnings in init.c.
++ * Set debhelper compat to 4.
++ * Vanishing mail boxes no longer elicit "you have mail" messages.
++ * Function redirection errors no longer abort the shell.
++ * Fixed potential memory leak in redirect.
++ * Only allocate memory if necessary in redirect.
++ * Reap dead here documents.
++ * Do not strdup default values of static shell variables.
++ * Removed unnecessary setprompt(0) calls.
++ * Read in BUFSIZ chunks rather than BUFSIZ - 1.
++ * Documented undefined escape behaviour for echo(1) (closes: #167893).
++ * Do va_copy when we use a va_list twice (closes: #169503).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 20 Nov 2002 19:48:31 +1100
++
++dash (0.4.3) unstable; urgency=low
++
++ * Added manual entry for PPID.
++ * Exporting an unset variable no longer causes it to be set.
++ * Fixed fd0 redirection in asynchronous lists.
++ * Only stat if necessary in cdcmd (see #42880).
++ * Removed extra newline in error message in arith lexer.
++ * Set heredoclist to 0 ASAP in parseheredoc.
++ * Removed BSD advertising clause from copyright file.
++ * Check non-ash diversions as well in dash.postinst.
++ * Duplicated diversion checking in ash.postinst (closes: #166441).
++
++ -- Herbert Xu <herbert@debian.org> Sat, 26 Oct 2002 21:28:33 +1000
++
++dash (0.4.2) unstable; urgency=low
++
++ * Give benefits of dash in templates (closes: #161527).
++ * Fixed signed/unsigned on result of xwrite (closes: #161606).
++ * Removed support for SIG prefixes in kill and trap.
++ * Added -- processing in trap.
++ * Dropped use of unset in postinst (closes: 161868).
++ * Fixed printf(1) %* processing on bad integers and zero.
++ * Use stat64 in test(1).
++ * Allocate group_array with stalloc in test(1).
++ * Disabled alias checking after a pattern in a case statement.
++ * Wait now returns 128 + last trapped signal.
++ * Printf now keeps going after errors.
++ * Empty non-trivial parameter expansions are now removed correctly.
++ * Call reset() before exitshell() is called. This fixes the bug where
++ returning an error from a function running under set -e caused the exit
++ trap to be taken with evalskip set.
++ * Fixed quoting of empty strings in single_quote().
++ * Show line numbers on all errors.
++ * Function names must be valid identifiers.
++ * Removed unused dependency on groff.
++ * Fixed race condition before entering a function.
++ * Fixed getopts initialisation for functions.
++ * Added memory barriers in INT macros.
++ * Banned empty compound lists in most places.
++ * Keep usage counters on functions (closes: #164234).
++ * Updated copyright file.
++ * Check evalskip in evalstring (closes: #165056).
++ * Merged changes from NetBSD 1.6:
++ . Added intmax support in printf(1).
++ . Implemented set -u.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 19 Oct 2002 14:23:11 +1000
++
++dash (0.4.1) unstable; urgency=low
++
++ * Removed extra new line in command -v output for aliases.
++ * Removed alais prefix in the output of alias.
++ * Recognise octal and hex numbers in arith expansion (closes: #151449).
++ * Added sh(1) entries for echo, printf and test (closes: #156446).
++ * Renamed to dash --- the Debian Almquist Shell.
++ * Cleaned up rules file (Matej Vela).
++ * Check mtime instead of size in chkmail per POSIX.
++ * Added support for LFS (closes: #157884).
++ * Added SuS options to cd and pwd (closes: #145828).
++
++ -- Herbert Xu <herbert@debian.org> Fri, 13 Sep 2002 20:35:06 +1000
++
++ash (0.3.8-38) unstable; urgency=low
++
++ * Turned pre-dependency to dependency in udeb since the former is not allowed
++ (closes: #143749).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 28 Apr 2002 11:59:05 +1000
++
++ash (0.3.8-37) unstable; urgency=low
++
++ * Added Japanese debconf translation (Tomohiro KUBOTA, closes: #137431).
++ * Added missing escapes in manual page (Aaron Schrab, closes: #137966).
++ * Added Russian debconf translation (Ilgiz Kalmetev, closes: #137618).
++ * Fixed trap(1) documentation (closes: #140973).
++ * Do not abort if getcwd fails.
++
++ -- Herbert Xu <herbert@debian.org> Wed, 3 Apr 2002 20:58:09 +1000
++
++ash (0.3.8-36) unstable; urgency=low
++
++ * Added library dependency for ash-udeb.
++ * Handle null case statements correctly.
++ * Fixed alias expansions in case statements (NetBSD).
++ * Disabled unused jobid command.
++ * Corrected documentation about shifting too much.
++ * Added French debconf translation (Denis Barbier, closes: #134625).
++ * Updated Spanish debconf translation (Carlos Valdivia, closes: #136366).
++
++ -- Herbert Xu <herbert@debian.org> Sat, 2 Mar 2002 18:31:22 +1100
++
++ash (0.3.8-35) unstable; urgency=low
++
++ * Moved PWD initialisation into var.c (closes: #124032).
++
++ -- Herbert Xu <herbert@debian.org> Mon, 24 Dec 2001 09:34:55 +1100
++
++ash (0.3.8-34) unstable; urgency=low
++
++ * NSEMI must be NOR + 1.
++ * Set exitstatus to zero before evaluating cases (closes: #124066).
++ * Explicitly set default answer of the ash/sh question to false so that
++ people whose debconf priority is set to low and who keeps banging on their
++ keyboards don't accidently end up with ash as /bin/sh.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 21 Dec 2001 20:30:49 +1100
++
++ash (0.3.8-33) unstable; urgency=low
++
++ * Added missing inclusion of bltin.h in bltin/times.c.
++
++ -- Herbert Xu <herbert@debian.org> Thu, 13 Dec 2001 18:46:07 +1100
++
++ash (0.3.8-32) unstable; urgency=low
++
++ * Back slashes in expansions are now escaped (closes: #121516).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 28 Nov 2001 20:15:01 +1100
++
++ash (0.3.8-31) unstable; urgency=low
++
++ * Made sure all back slashes are escaped.
++
++ -- Herbert Xu <herbert@debian.org> Mon, 26 Nov 2001 19:10:27 +1100
++
++ash (0.3.8-30) unstable; urgency=low
++
++ * Restored fnmatch(3) code.
++ * Treat escaped slashes correctly while globbing.
++ * Restored missing EV_EXIT check in evalcommand (closes: #120364).
++ * Fixed stack corruption in _rmescapes.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 25 Nov 2001 17:51:19 +1100
++
++ash (0.3.8-29) unstable; urgency=low
++
++ * Added missing va_end in fmtstr (NetBSD).
++ * Removed shellproc crap.
++ * Updated Swedish debconf translation (Mikael Hedin, closes: #116097).
++ * Updated German debconf translation (Andreas Metzler, closes: #117160).
++ * Break now treats illegal numbers according to SuS.
++ * Errors in special builtins now rise to the top.
++ * Normal redirection errors no longer abort the shell.
++ * Functions now have the same variable assignment properties as special
++ builtins.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 3 Nov 2001 11:36:36 +1100
++
++ash (0.3.8-28) unstable; urgency=low
++
++ * Local variables are now unset properly in shprocvar() (closes: #114917).
++
++ -- Herbert Xu <herbert@debian.org> Sat, 13 Oct 2001 14:07:21 +1000
++
++ash (0.3.8-27) unstable; urgency=low
++
++ * Kill no longer aborts if it fails to kill someone.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 30 Sep 2001 22:20:36 +1000
++
++ash (0.3.8-26) unstable; urgency=low
++
++ * The sh.1.gz diversion now agrees with reality (closes: #113831).
++
++ -- Herbert Xu <herbert@debian.org> Sat, 29 Sep 2001 08:43:27 +1000
++
++ash (0.3.8-25) unstable; urgency=low
++
++ * Only read ENV if the shell is interactive (closes: #110421).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 29 Aug 2001 19:18:53 +1000
++
++ash (0.3.8-24) unstable; urgency=low
++
++ * Handle SIGINT when waiting even if there is no trap (closes: #107699).
++ * Protect all makejob/forkshell/waitforjobs sequences from SIGINT.
++ * Work around gcc bug that generates bad ..ng references (closes: #107994).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 8 Aug 2001 20:28:28 +1000
++
++ash (0.3.8-23) unstable; urgency=low
++
++ * Fixed fence post error in scanleft (closes: #107229).
++ * Removed stunalloc in expname as it interferes with addfname.
++ * Fixed CTLESC skipping in scanright.
++
++ -- Herbert Xu <herbert@debian.org> Thu, 2 Aug 2001 20:06:00 +1000
++
++ash (0.3.8-22) unstable; urgency=low
++
++ * Fixed trailing back slash bug in echo/printf (closes: #106693).
++ * Some quoted's are meant to be quotes.
++ * Added Brazilian translation (Andre Luis Lopes, closes: #107041).
++
++ -- Herbert Xu <herbert@debian.org> Mon, 30 Jul 2001 20:21:52 +1000
++
++ash (0.3.8-21) unstable; urgency=low
++
++ * Fixed EV_EXIT/redirection bugs that caused core dumps.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 28 Jul 2001 17:03:28 +1000
++
++ash (0.3.8-20) unstable; urgency=low
++
++ * Don't save fd2 if job control is turned off.
++ * Don't push redirections when EV_EXIT is set.
++ * Fixed assignment recognition in the presence of back ticks.
++ * Combined checkkwd and checkalias.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 27 Jul 2001 22:29:41 +1000
++
++ash (0.3.8-19) unstable; urgency=low
++
++ * Recompute strings after growing in subevalvar (closes: #106050).
++
++ -- Herbert Xu <herbert@debian.org> Mon, 23 Jul 2001 21:16:50 +1000
++
++ash (0.3.8-18) unstable; urgency=low
++
++ * Added more space optimisations for udeb on i386.
++ * Set stack mark in patmatch (closes: #106050).
++ * Fixed theoretical bug in expari.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 21 Jul 2001 20:08:15 +1000
++
++ash (0.3.8-17) unstable; urgency=low
++
++ * Don't complain about unknown escape codes in echo and printf
++ (closes: #105659).
++ * Updated build-time dependency on groff-base (closes: #105612).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 18 Jul 2001 19:33:20 +1000
++
++ash (0.3.8-16) unstable; urgency=low
++
++ * Fixed backslash bug in new pattern matching code.
++
++ -- Herbert Xu <herbert@debian.org> Mon, 16 Jul 2001 21:47:39 +1000
++
++ash (0.3.8-15) unstable; urgency=low
++
++ * Added Swedish translation of templates (Martin Sjögren, closes: #103158).
++ * Restored escape code support in echo.
++ * Removed assignment builtins since it is at best undefined by the SuS and
++ also can't be implemented consistently.
++ * Removed extraneous volatile modifier (closes: #104518).
++ * General overhaul of word expansion (closes: #96588).
++ * Redirection prefixes no longer stop assignments from being recognised.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 15 Jul 2001 17:27:03 +1000
++
++ash (0.3.8-14) unstable; urgency=low
++
++ * Divert sh.1.gz to sh.distrib.1.gz (closes: #102251).
++ * Added HETIO support for ^D and ^U (Aaron Lehmann, closes: #102215).
++ * Added Spaniash translation of debconf templates (Carlos Valdivia Yagüe,
++ closes: #103040).
++ * Added versioned build-time dependency on groff.
++
++ -- Herbert Xu <herbert@debian.org> Mon, 2 Jul 2001 19:32:03 +1000
++
++ash (0.3.8-13) unstable; urgency=low
++
++ * Fixed a bug where errors in pipelines which are part of andor lists were
++ not ignored when -e is in effect.
++
++ -- Herbert Xu <herbert@debian.org> Mon, 25 Jun 2001 19:40:27 +1000
++
++ash (0.3.8-12) unstable; urgency=low
++
++ * Rewrote arith_lex.l in C (Aaron Lehmann, closes: #101741).
++ * && and || in arithmetic expansions now return either 0 or 1.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 24 Jun 2001 20:14:29 +1000
++
++ash (0.3.8-11) unstable; urgency=low
++
++ * Check for NULL argument in evaltree() (closes: #98865, #98867).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 27 May 2001 17:53:14 +1000
++
++ash (0.3.8-10) unstable; urgency=low
++
++ * Use /bin/ash in postinst to sidestep bugs in other shells (closes: #98739).
++ * Exit status is now tested on non-negated pipelines (closes: #98736).
++
++ -- Herbert Xu <herbert@debian.org> Sat, 26 May 2001 23:56:07 +1000
++
++ash (0.3.8-9) unstable; urgency=medium
++
++ * IFS is now fetched using bltinlookup() again in read (closes: #98343).
++ * Divert sh(1) man page as well as /bin/sh (closes: #98525).
++
++ -- Herbert Xu <herbert@debian.org> Fri, 25 May 2001 20:30:06 +1000
++
++ash (0.3.8-8) unstable; urgency=low
++
++ * Fixed diversion removal in prerm (duh, closes: #98031).
++
++ -- Herbert Xu <herbert@debian.org> Mon, 21 May 2001 20:52:48 +1000
++
++ash (0.3.8-7) unstable; urgency=low
++
++ * Fixed diversion test in prerm (closes: #98031).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 20 May 2001 12:30:53 +1000
++
++ash (0.3.8-6) unstable; urgency=low
++
++ * Make sure that fd2 is closed when clearing redirects (closes: #96619).
++ * Fixed memory corruption in stunalloc().
++ * The output of export/readonly/set is now correctly quoted.
++ * Fixed newline eating bug in expbackq().
++ * Set OLDPWD.
++ * Removed ash-medium as neither bf or di uses it.
++ * Wait now waits for all its argument rather than the first one.
++ * Wait will exit with 129 when interrupted by a signal for a which a trap has
++ been set.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 18 May 2001 21:51:41 +1000
++
++ash (0.3.8-5) unstable; urgency=low
++
++ * Added German translation to template file (Sebastian Feltel,
++ closes: #96203).
++ * Added missing initialisation in setalias() (closes: #95433).
++
++ -- Herbert Xu <herbert@debian.org> Fri, 4 May 2001 20:54:31 +1000
++
++ash (0.3.8-4) unstable; urgency=low
++
++ * Disabled fnmatch code as fnmatch(3) in glibc is broken.
++ * Fixed echo example in man page (Kalle Olavi Niemitalo, closes: #96014).
++ * Fixed trailing semicolon bug with eval (NetBSD).
++ * Fixed globbing inconsistency with broken symlinks (NetBSD).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 2 May 2001 22:57:16 +1000
++
++ash (0.3.8-3) unstable; urgency=low
++
++ * Work around broken autoconf scripts (closes: #95430).
++
++ -- Herbert Xu <herbert@debian.org> Tue, 1 May 2001 18:27:50 +1000
++
++ash (0.3.8-2) unstable; urgency=low
++
++ * Save checkalias before calling xxreadtoken() (closes: #95628).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 29 Apr 2001 17:36:01 +1000
++
++ash (0.3.8-1) unstable; urgency=low
++
++ * NetBSD-current version as of 20010316.
++ * Removed code that sets IFS.
++ * Fixed memory leak with PWD.
++ * Set PPID.
++ * Fixed inconsistencies in alias expansion.
++ * Restored original output code.
++ * Enabled fnmatch code again.
++ * Added builtin printf.
++ * Offer to divert /bin/sh (closes: #70462).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 25 Apr 2001 22:32:39 +1000
++
++ash (0.3.7-16) unstable; urgency=low
++
++ * Fixed incorrect default IFS in readcmd (closes: #88950).
++ * Added missing return in hashcmd.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 9 Mar 2001 20:44:40 +1100
++
++ash (0.3.7-15) unstable; urgency=low
++
++ * Unknown escape codes are now prnted literally by echo (closes: #82869).
++ * Made hetio_read_input() fail if fd is not stdin.
++ * Some uses of VSQUOTE were really meant to be quotes (closes: #88777).
++ * Build different ashes in different subdirectories.
++
++ -- Herbert Xu <herbert@debian.org> Thu, 8 Mar 2001 21:32:28 +1100
++
++ash (0.3.7-14) unstable; urgency=low
++
++ * Removed predependency from udeb (closes: #81995).
++ * Added /bin/sh symlink to udeb (closes: #81967).
++
++ -- Herbert Xu <herbert@debian.org> Sat, 13 Jan 2001 15:23:21 +1100
++
++ash (0.3.7-13) unstable; urgency=low
++
++ * Renamed the udeb to ash-udeb.
++
++ -- Herbert Xu <herbert@debian.org> Wed, 20 Dec 2000 19:32:34 +1100
++
++ash (0.3.7-12) unstable; urgency=low
++
++ * Added support for udebs (Randolph Chung, closes: #79237).
++
++ -- Herbert Xu <herbert@debian.org> Sat, 16 Dec 2000 13:53:28 +1100
++
++ash (0.3.7-11) unstable; urgency=low
++
++ * Preserve the previous exit status upon entering a function
++ (closes: #78374).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 3 Dec 2000 13:34:27 +1100
++
++ash (0.3.7-10) unstable; urgency=low
++
++ * Merged changes for GNU from Igor Khavkine.
++ * Minimise the number of sigactions.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 3 Nov 2000 20:31:52 +1100
++
++ash (0.3.7-9) unstable; urgency=low
++
++ * Predepend on the libraries.
++ * Always save fd 2 when it is redirected (closes: #75302).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 22 Oct 2000 08:40:40 +1100
++
++ash (0.3.7-8) unstable; urgency=high
++
++ * More redirection fixes (closes: #73613).
++
++ -- Herbert Xu <herbert@debian.org> Thu, 5 Oct 2000 18:22:17 +1100
++
++ash (0.3.7-7) unstable; urgency=high
++
++ * Added missing break in redirection code (closes: #72956).
++
++ -- Herbert Xu <herbert@debian.org> Tue, 3 Oct 2000 07:58:04 +1100
++
++ash (0.3.7-6) unstable; urgency=low
++
++ * command -[vV] no longer displays an error message on stdout.
++ * Redirecting to /proc/self/fd/* now works (closes: #72852).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 1 Oct 2000 12:56:39 +1100
++
++ash (0.3.7-5) unstable; urgency=low
++
++ * Implemented set -a.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 30 Sep 2000 16:00:33 +1100
++
++ash (0.3.7-4) unstable; urgency=low
++
++ * Added build-time dependency on debhelper (closes: #69920).
++ * Extended maximum length of arithmetic expansions to match 32-bit integers.
++
++ -- Herbert Xu <herbert@debian.org> Wed, 20 Sep 2000 14:28:16 +1100
++
++ash (0.3.7-3) unstable; urgency=low
++
++ * Switch to the old globbing code since glob(3) is hopelessly broken
++ (closes: #69455).
++
++ -- Herbert Xu <herbert@debian.org> Mon, 21 Aug 2000 20:37:15 +1000
++
++ash (0.3.7-2) unstable; urgency=low
++
++ * Call glob(3) with GLOB_NOMAGIC (ouch).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 6 Aug 2000 17:47:08 +1000
++
++ash (0.3.7-1) unstable; urgency=low
++
++ * NetBSD-current version as of 20000729.
++ * Use fnmatch(3) and glob(3).
++ * Fixed the use of backslashes in the pattern in parameter substitutions,
++ hopefully for the last time.
++ * Applied HETIO patch and built ash.medium (closes: #50788). Will do ash.big
++ when readline is fixed so that it doesn't leak anymore.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 4 Aug 2000 21:36:44 +1000
++
++ash (0.3.6-5) unstable; urgency=low
++
++ * Fixed manpage entry for read with patch from Kevin Ryde (closes: #62500).
++ * Fixed a file descriptor leak for pipelines.
++
++ -- Herbert Xu <herbert@debian.org> Wed, 19 Apr 2000 18:56:20 +1000
++
++ash (0.3.6-4) unstable; urgency=low
++
++ * Fixed the case of an empty command with redirections.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 7 Apr 2000 12:07:18 +1000
++
++ash (0.3.6-3) unstable; urgency=low
++
++ * ! is now recognised correctly.
++ * Ash is now more strict on the syntax, e.g., a lone ! is no longer accepted
++ as an alternative to ! true.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 7 Apr 2000 10:46:06 +1000
++
++ash (0.3.6-2) unstable; urgency=low
++
++ * Fixed a problem with fmtstr() which broke getopts.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 2 Apr 2000 10:49:26 +1000
++
++ash (0.3.6-1) unstable; urgency=low
++
++ * NetBSD-current version as of 20000326.
++ * Added a Build-Depends on groff (closes: #61041).
++ * Implemented noclobber (closes: #59028).
++ * Rewrote output.c to use stream IO.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 1 Apr 2000 19:24:31 +1000
++
++ash (0.3.5-10) frozen unstable; urgency=low
++
++ * Don't stat mail boxes in non-interactive mode (closes: #59213).
++ * Added an fflush(stdout) to the times builtin (closes: #59027).
++ * Documented the times builtin.
++ * Added source depends.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 18 Mar 2000 18:58:44 +1100
++
++ash (0.3.5-9) unstable; urgency=low
++
++ * Double quotes inside paramater substitutions inside double quotes are now
++ ignored as in bash (the originial behaviour was POSIX compliant too but
++ IMHO this one makes a little bit more sense).
++ This one broke mwm (but it was actually mwm's fault).
++ * Corrected backslash/CTLESC treatment for patterns in parameter
++ substitutions.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 6 Nov 1999 18:13:19 +1100
++
++ash (0.3.5-8) unstable; urgency=low
++
++ * Replaced use of echo -n in manual page with escape codes.
++ * Made FHS compliant (closes: #47978).
++ * Restored echo's option processing ability.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 22 Oct 1999 10:20:58 +1000
++
++ash (0.3.5-7) unstable; urgency=low
++
++ * echo no longer supports options.
++ * Don't quote patterns inside parameter substitutions enclosed by double
++ quotes (closes: #47842).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 20 Oct 1999 20:28:14 +1000
++
++ash (0.3.5-6) unstable; urgency=low
++
++ * Use getcwd() instead of /bin/pwd -- Zack Weinberg (closes: #46981).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 10 Oct 1999 16:31:49 +1000
++
++ash (0.3.5-5) unstable; urgency=low
++
++ * Only test for -e on simple commands (fixes #44559).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 8 Sep 1999 22:18:27 +1000
++
++ash (0.3.5-4) unstable; urgency=low
++
++ * Don't wait for stopped children if job control is disabled (fixes #42814).
++ * Allow an option '(' in a case statement (fixes #42364).
++
++ -- Herbert Xu <herbert@debian.org> Thu, 12 Aug 1999 23:30:30 +1000
++
++ash (0.3.5-3) unstable; urgency=low
++
++ * OK, the fix to the esoteric problem in 0.3.5-1 actually breaks VSASSIGN
++ and VSQUESTION, they should work properly now (fixes #41327).
++
++ -- Herbert Xu <herbert@debian.org> Thu, 15 Jul 1999 22:47:13 +1000
++
++ash (0.3.5-2) unstable; urgency=low
++
++ * PATH search and execution is now correct.
++ * hash no longer shows builtins.
++ * Added kill builtin.
++ * New description from James R. van Zandt reformatted by Josip Rodin.
++
++ -- Herbert Xu <herbert@debian.org> Mon, 12 Jul 1999 18:51:42 +1000
++
++ash (0.3.5-1) unstable; urgency=low
++
++ * New upstream release.
++ * Adapted to new pmake (fixes #38737).
++ * Fixed behvaiour of backslashes preceding a closing brace for a parameter
++ substituion inside double quotes (even bash messes this one up :).
++ * Fixed command (fixes #34639).
++ * Fixed a pipe bug where stdin may be wrongly closed (fixes #35452).
++ * Revamped getopts (fixes #39694).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 4 Jul 1999 12:19:01 +1000
++
++ash (0.3.4-7) unstable; urgency=low
++
++ * Fixed a glibc 2.1 compatitibility problem.
++ * Fixed a PWD inconsistency that stuffed up the kernel compilation.
++
++ -- Herbert Xu <herbert@debian.org> Mon, 17 May 1999 23:14:57 +1000
++
++ash (0.3.4-6) unstable; urgency=low
++
++ * Fixed incorrect -e test due to the last bug fix (fixes #26509).
++
++ -- Herbert Xu <herbert@debian.org> Tue, 8 Sep 1998 10:02:46 +1000
++
++ash (0.3.4-5) unstable; urgency=low
++
++ * Use test_eaccess from bash instead of access(2) (fixes #26110).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 26 Aug 1998 21:22:49 +1000
++
++ash (0.3.4-4) unstable; urgency=low
++
++ * Only upload to unstable.
++
++ -- Herbert Xu <herbert@debian.org> Tue, 5 May 1998 18:01:02 +1000
++
++ash (0.3.4-3) frozen unstable; urgency=low
++
++ * Applied sparc patch (fixes #21562).
++
++ -- Herbert Xu <herbert@debian.org> Fri, 1 May 1998 19:48:13 +1000
++
++ash (0.3.4-2) frozen unstable; urgency=low
++
++ * Fixed the incorrect trap fixes (fixes #20363).
++
++ -- Herbert Xu <herbert@debian.org> Thu, 16 Apr 1998 21:07:10 +1000
++
++ash (0.3.4-1) unstable; urgency=low
++
++ * New upstream release.
++ * Reverted word splitting change in 0.3.2-1 since the fix was broken and
++ major work (the quote removal is done too quickly at the moment) is needed
++ to fix it properly.
++ * Fixed more trap noncompliance.
++
++ -- Herbert Xu <herbert@debian.org> Thu, 19 Mar 1998 22:59:12 +1100
++
++ash (0.3.2-5) unstable; urgency=low
++
++ * Fixed a bug when doing pattern matching in parameter expansions.
++
++ -- Herbert Xu <herbert@debian.org> Tue, 10 Mar 1998 21:25:40 +1100
++
++ash (0.3.2-4) unstable; urgency=low
++
++ * Allow ] to be quoted in bracket expressions (fixes #17533).
++ * Move dh_fixperms to second last spot (fixes #18267).
++ * Don't do field splitting in evalfor.
++
++ -- Herbert Xu <herbert@debian.org> Tue, 17 Feb 1998 13:32:09 +1100
++
++ash (0.3.2-3) unstable; urgency=low
++
++ * Fixed stupid core dump.
++
++ -- Herbert Xu <herbert@debian.org> Wed, 11 Feb 1998 21:33:55 +1100
++
++ash (0.3.2-2) unstable; urgency=low
++
++ * Hack for special builtins (fixes #18055).
++ * Hack for command.
++
++ -- Herbert Xu <herbert@debian.org> Wed, 11 Feb 1998 21:19:46 +1100
++
++ash (0.3.2-1) unstable; urgency=low
++
++ * NetBSD-current version as of 19980209.
++ * Fixed a word splitting problem after parameter expansion thanks to Alexey
++ Marinichev.
++ * Converted to debhelper (fixes #14612, #15005).
++
++ -- Herbert Xu <herbert@debian.org> Mon, 9 Feb 1998 16:53:48 +1100
++
++ash (0.3.1-20) unstable; urgency=low
++
++ * Fixed -e problem with eval.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 7 Dec 1997 20:19:00 +1100
++
++ash (0.3.1-19) unstable; urgency=low
++
++ * Fixed -e problem with command substitution.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 7 Dec 1997 19:44:49 +1100
++
++ash (0.3.1-18) unstable; urgency=low
++
++ * Do not link with ncurses (#15485).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 30 Nov 1997 12:00:11 +1100
++
++ash (0.3.1-17) unstable; urgency=low
++
++ * Set PATH like bash (#15238).
++
++ -- Herbert Xu <herbert@debian.org> Wed, 26 Nov 1997 16:17:27 +1100
++
++ash (0.3.1-16) unstable; urgency=low
++
++ * Fixed incorrect assignment builtin code.
++
++ -- Herbert Xu <herbert@debian.org> Mon, 24 Nov 1997 16:19:10 +1100
++
++ash (0.3.1-15) unstable; urgency=low
++
++ * hash now returns error codes (needed by the Linux kernel).
++
++ -- Herbert Xu <herbert@debian.org> Sun, 23 Nov 1997 21:37:08 +1100
++
++ash (0.3.1-14) unstable; urgency=low
++
++ * Disabled word-splitting for assignment builtins.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 23 Nov 1997 12:45:15 +1100
++
++ash (0.3.1-13) unstable; urgency=low
++
++ * ! is now recognised even after &&/||.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 21 Nov 1997 22:09:05 +1100
++
++ash (0.3.1-12) unstable; urgency=low
++
++ * More fixes to the handling of SIGINT when forking.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 14 Nov 1997 15:14:32 +1100
++
++ash (0.3.1-11) unstable; urgency=low
++
++ * Ignore SIGINT when forking non-interactively.
++
++ -- Herbert Xu <herbert@debian.org> Mon, 3 Nov 1997 12:00:02 +1100
++
++ash (0.3.1-10) unstable; urgency=low
++
++ * echo now handles options correctly.
++ * echo nolonger returns 0 if erorrs occured while writing to stdout.
++ * New code from GNU echo merged.
++ * Error messages from test now work.
++
++ -- Herbert Xu <herbert@debian.org> Wed, 8 Oct 1997 21:47:13 +1000
++
++ash (0.3.1-9) unstable; urgency=low
++
++ * ! is recognised at pipeline level like bash.
++
++ -- Herbert Xu <herbert@debian.org> Mon, 15 Sep 1997 23:13:45 +1000
++
++ash (0.3.1-8) unstable; urgency=medium
++
++ * Old patch regarding SIGCHLD in again.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 31 Aug 1997 11:20:27 +1000
++
++ash (0.3.1-7) unstable; urgency=low
++
++ * /bin/sh -e is behaving even better now (for loops within conditionals).
++
++ -- Herbert Xu <herbert@debian.org> Sat, 23 Aug 1997 22:08:19 +1000
++
++ash (0.3.1-6) unstable; urgency=low
++
++ * /bin/sh -e is behaving better now.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 23 Aug 1997 13:16:26 +1000
++
++ash (0.3.1-5) unstable; urgency=low
++
++ * hash -v /dir/command doesn't coredump anymore.
++ * type /dir/command now works correctly.
++
++ -- Herbert Xu <herbert@debian.org> Fri, 1 Aug 1997 20:48:19 +1000
++
++ash (0.3.1-4) unstable; urgency=low
++
++ * trap now understands symbolic signal names.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 26 Jul 1997 14:04:46 +1000
++
++ash (0.3.1-3) unstable; urgency=low
++
++ * Added the builtin test command.
++
++ -- Herbert Xu <herbert@debian.org> Sun, 20 Jul 1997 15:00:14 +1000
++
++ash (0.3.1-2) unstable; urgency=medium
++
++ * Fixed a coredump involving $*.
++
++ -- Herbert Xu <herbert@debian.org> Sat, 19 Jul 1997 12:03:02 +1000
++
++ash (0.3.1-1) unstable; urgency=medium
++
++ * NetBSD-current version as of 19970715.
++ * Fixed a "use after free" bug (#11294).
++
++ -- Herbert Xu <herbert@debian.org> Fri, 18 Jul 1997 13:48:09 +1000
++
++ash (0.3-1) unstable; urgency=low
++
++ * Initial Release.
++
++ -- Herbert Xu <herbert@debian.org> Thu, 19 Jun 1997 19:29:16 +1000
++
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/compat bin_NetBSD-1.6release/src/bin/sh/debian/compat
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/compat 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/compat 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1 @@
++4
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/control bin_NetBSD-1.6release/src/bin/sh/debian/control
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/control 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/control 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,53 @@
++Source: dash
++Section: shells
++Priority: optional
++Maintainer: Herbert Xu <herbert@debian.org>
++Build-Depends: byacc, debhelper (>= 4), pmake
++Standards-Version: 3.5.8
++
++Package: dash
++Architecture: any
++Pre-Depends: ${shlibs:Depends}
++Description: The Debian Almquist Shell
++ "dash" is a POSIX compliant shell that is much smaller than "bash".
++ We take advantage of that by making it the shell on the installation
++ root floppy, where space is at a premium.
++ .
++ It can be usefully installed as /bin/sh (because it executes scripts
++ somewhat faster than "bash"), or as the default shell either of root
++ or of a second user with a userid of 0 (because it depends on fewer
++ libraries, and is therefore less likely to be affected by an upgrade
++ problem or a disk failure). It is also useful for checking that a
++ script uses only POSIX syntax.
++ .
++ "bash" is a better shell for most users, since it has some nice
++ features absent from "dash", and is a required part of the system.
++
++Package: dash-udeb
++Architecture: any
++Depends: ${shlibs:Depends}
++Section: debian-installer
++Priority: standard
++Description: The Debian Almquist Shell for boot floppies
++ "dash" is a POSIX compliant shell that is much smaller than "bash".
++ We take advantage of that by making it the shell on the installation
++ root floppy, where space is at a premium.
++ .
++ It can be usefully installed as /bin/sh (because it executes scripts
++ somewhat faster than "bash"), or as the default shell either of root
++ or of a second user with a userid of 0 (because it depends on fewer
++ libraries, and is therefore less likely to be affected by an upgrade
++ problem or a disk failure). It is also useful for checking that a
++ script uses only POSIX syntax.
++ .
++ "bash" is a better shell for most users, since it has some nice
++ features absent from "dash", and is a required part of the system.
++
++Package: ash
++Architecture: all
++Pre-Depends: dash
++Description: Compatibility package for the Debian Almquist Shell
++ This package exists so that users of the "ash" package can upgrade to the
++ "dash" package which replaces the former. It includes the /bin/ash symlink.
++ You can remove this package if you do not use /bin/ash explicitly.
++
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/copyright bin_NetBSD-1.6release/src/bin/sh/debian/copyright
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/copyright 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/copyright 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,44 @@
++This package was debianized by Mark W. Eichin eichin@kitten.gen.ma.us on
++Mon, 24 Feb 1997 16:00:16 -0500.
++
++This package was re-ported from NetBSD and debianized by
++Herbert Xu herbert@debian.org on Thu, 19 Jun 1997 19:29:16 +1000.
++
++It was downloaded from ftp.netbsd.org.
++
++Copyright:
++
++Copyright (c) 1989-1994
++ The Regents of the University of California. All rights reserved.
++Copyright (c) 1997-2002
++ Herbert Xu <herbert@debian.org>
++
++This code is derived from software contributed to Berkeley by Kenneth Almquist.
++
++Please refer to /usr/share/common-licenses/BSD for details.
++
++mksignames.c:
++
++This file is not directly linked with dash. However, its output is.
++
++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 with
++your Debian GNU/Linux system, in /usr/share/common-licenses/GPL, or with the
++Debian GNU/Linux hello source package as the file COPYING. If not,
++write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++Boston, MA 02111 USA.
++
++$Id: dash-ash-hetio-yacc.diff,v 1.1 2003/02/09 09:36:31 satai Exp $
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.config bin_NetBSD-1.6release/src/bin/sh/debian/dash.config
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.config 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.config 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,39 @@
++#!/bin/sh
++#
++# debconf script for the Debian GNU/Linux ash package
++#
++# $Id: dash-ash-hetio-yacc.diff,v 1.1 2003/02/09 09:36:31 satai Exp $
++
++set -e
++
++. /usr/share/debconf/confmodule
++
++db_version 2.0
++
++if [ "$1" = configure ] && [ -z "$2" ]; then
++ set +e
++ db_fget ash/sh seen
++ err=$?
++ set -e
++
++ case $err in
++ 0)
++ if [ "$RET" = true ]; then
++ db_fset dash/sh seen true
++ db_get ash/sh
++ db_set dash/sh "$RET"
++ exit
++ fi
++ ;;
++ 10)
++ # ash/sh does not exist
++ ;;
++ *)
++ echo "db_fget exited with $err" >&2
++ exit $err
++ ;;
++ esac
++fi
++
++db_input low dash/sh || true
++db_go
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.dirs bin_NetBSD-1.6release/src/bin/sh/debian/dash.dirs
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.dirs 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.dirs 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,2 @@
++bin
++usr/share/man/man1
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.postinst bin_NetBSD-1.6release/src/bin/sh/debian/dash.postinst
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.postinst 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.postinst 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,81 @@
++#!/bin/sh
++#
++# post-install script for the Debian GNU/Linux dash package
++#
++# $Id: dash-ash-hetio-yacc.diff,v 1.1 2003/02/09 09:36:31 satai Exp $
++
++set -e
++
++check_divert() {
++ div=$(dpkg-divert --list $2)
++ distrib=${4:-$2.distrib}
++ case "$1" in
++ true)
++ if [ -z "$div" ]; then
++ dpkg-divert --package dash --divert $distrib --add $2
++ cp -dp $2 $distrib
++ ln -sf $3 $2
++ fi
++ ;;
++ false)
++ if [ -n "$div" ] && [ -z "${div%%*by dash}" ]; then
++ mv $distrib $2
++ dpkg-divert --remove $2
++ fi
++ ;;
++ ash)
++ case $div in
++ '')
++ ;;
++ *by\ ash)
++ dst=${div% by ash}
++ dst=${dst##* to }
++
++ # Work around dpkg-divert bug.
++ if [ -e "$dst" ]; then
++ mv "$dst" "$dst.dash-tmp"
++ fi
++ dpkg-divert --remove $2
++ if [ -e "$dst.dash-tmp" ]; then
++ mv "$dst.dash-tmp" "$dst"
++ fi
++
++ dpkg-divert --package dash --divert $distrib --add $2
++ if [ "$dst" != $distrib ] && [ -e "$dst" ]; then
++ mv "$dst" $distrib
++ fi
++ ln -sf $3 $2
++ ;;
++ *)
++ d=${2%/*}
++ if
++ [ -h $2 ] && [ -f $2 ] && [ -f $d/$5 ] &&
++ cmp $2 $d/$5
++ then
++ ln -sf $3 $2
++ fi
++ ;;
++ esac
++ esac
++}
++
++debconf=
++if [ -f /usr/share/debconf/confmodule ]; then
++ . /usr/share/debconf/confmodule
++ debconf=yes
++fi
++
++if [ "$1" = configure ] && [ -z "$2" ]; then
++ check_divert ash /bin/sh dash '' ash
++ check_divert ash /usr/share/man/man1/sh.1.gz dash.1.gz \
++ /usr/share/man/man1/sh.distrib.1.gz ash.1.gz
++fi
++
++if [ $debconf ]; then
++ db_get dash/sh
++ check_divert "$RET" /bin/sh dash
++ check_divert "$RET" /usr/share/man/man1/sh.1.gz dash.1.gz \
++ /usr/share/man/man1/sh.distrib.1.gz
++fi
++
++#DEBHELPER#
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.prerm bin_NetBSD-1.6release/src/bin/sh/debian/dash.prerm
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.prerm 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.prerm 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,24 @@
++#!/bin/sh
++#
++# pre-removal script for the Debian GNU/Linux ash package
++#
++# $Id: dash-ash-hetio-yacc.diff,v 1.1 2003/02/09 09:36:31 satai Exp $
++
++set -e
++
++remove_divert() {
++ div=$(dpkg-divert --list $1)
++ if [ -n "$div" ] && [ -z "${div%%*by dash}" ]; then
++ distrib=${div% by dash}
++ distrib=${distrib##* to }
++ mv $distrib $1
++ dpkg-divert --remove $1
++ fi
++}
++
++if [ "$1" = remove ] || [ "$1" = deconfigure ]; then
++ remove_divert /bin/sh
++ remove_divert /usr/share/man/man1/sh.1.gz
++fi
++
++#DEBHELPER#
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,10 @@
++Template: dash/sh
++Type: boolean
++Default: false
++Description: Install dash as /bin/sh?
++ Bash is the default /bin/sh on a Debian system. However, since our policy
++ requires all shell scripts using /bin/sh to be POSIX compliant, any shell
++ that conforms to POSIX can serve as /bin/sh. Since dash is POSIX compliant,
++ it can be used as /bin/sh. You may wish to do this because dash is faster
++ and smaller than bash.
++
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.de bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.de
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.de 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.de 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,13 @@
++Template: dash/sh
++Description: Install dash as /bin/sh?
++ Bash is the default /bin/sh on a Debian system. However, since our policy
++ requires all shell scripts using /bin/sh to be POSIX compliant, any shell
++ that conforms to POSIX can serve as /bin/sh. Since dash is POSIX compliant,
++ it can be used as /bin/sh.
++Description-de: dash als /bin/sh installieren?
++ Bash ist die Standard-Shell (/bin/sh) auf einem Debian-System. Da die
++ Debian-Policy von allen Shellscripts, die /bin/sh benutzen,
++ POSIX-Kompatibilität verlangt, kann für /bin/sh jede POSIX-kompatible Shell
++ benutzt werden. Dash ist POSIX-kompatibel und kann daher als /bin/sh verwendet
++ werden.
++
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.es bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.es
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.es 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.es 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,13 @@
++Template: dash/sh
++Description: Install dash as /bin/sh?
++ Bash is the default /bin/sh on a Debian system. However, since our policy
++ requires all shell scripts using /bin/sh to be POSIX compliant, any shell
++ that conforms to POSIX can serve as /bin/sh. Since dash is POSIX compliant,
++ it can be used as /bin/sh.
++Description-es: ¿Instalar dash como /bin/sh?
++ Bash es el intérprte de comandos /bin/sh por defecto de los sistemas Debian.
++ Sin embargo, dado que nuestras normas obligan a que todos los scripts para el
++ intérprete de comandos se atengan a las normas POSIX, cualquier intérprete
++ compatible con POSIX puede servir como /bin/sh. Puesto que dash lo es, puede
++ usarse como /bin/sh.
++
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.fr bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.fr
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.fr 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.fr 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,12 @@
++Template: dash/sh
++Description: Install dash as /bin/sh?
++ Bash is the default /bin/sh on a Debian system. However, since our policy
++ requires all shell scripts using /bin/sh to be POSIX compliant, any shell
++ that conforms to POSIX can serve as /bin/sh. Since dash is POSIX compliant,
++ it can be used as /bin/sh.
++Description-fr: Mettre un lien de /bin/sh vers dash ?
++ Sur un système Debian, /bin/sh est bash par défaut. Cependant, comme notre
++ charte impose que tous les scripts shells utilisant /bin/sh soient conformes
++ à la norme POSIX, /bin/sh peut être n'importe quel shell conforme à cette
++ norme. Et comme dash l'est, il peut servir de /bin/sh.
++
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.ja bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.ja
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.ja 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.ja 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,13 @@
++Template: dash/sh
++Description: Install dash as /bin/sh?
++ Bash is the default /bin/sh on a Debian system. However, since our policy
++ requires all shell scripts using /bin/sh to be POSIX compliant, any shell
++ that conforms to POSIX can serve as /bin/sh. Since dash is POSIX compliant,
++ it can be used as /bin/sh.
++Description-ja: dash ¤ò /bin/sh ¤È¤·¤Æ¥¤¥ó¥¹¥È¡¼¥ë¤·¤Þ¤¹¤«?
++ Debian ¥·¥¹¥Æ¥à¤Ç¤Ï bash ¤¬¥Ç¥Õ¥©¥ë¥È¤Î /bin/sh ¤Ç¤¹¡£¤·¤«¤·¡¢Debian
++ ¤Î¥Ý¥ê¥·¡¼¤Ë¤è¤Ã¤Æ¡¢/bin/sh ¤òÍѤ¤¤ëÁ´¤Æ¤Î¥·¥§¥ë¥¹¥¯¥ê¥×¥È¤Ï POSIX
++ ½àµò¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¤¿¤á¡¢POSIX ¤òËþ¤¿¤¹¥·¥§¥ë¤Ï¤É¤ì¤Ç¤â /bin/sh
++ ¤È¤Ê¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£dash ¤Ï POSIX ½àµò¤Ç¤¹¤Î¤Ç¡¢/bin/sh ¤È¤·¤Æ»È¤¦
++ ¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
++
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.pt_BR bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.pt_BR
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.pt_BR 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.pt_BR 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,12 @@
++Template: dash/sh
++Description: Install dash as /bin/sh?
++ Bash is the default /bin/sh on a Debian system. However, since our policy
++ requires all shell scripts using /bin/sh to be POSIX compliant, any shell
++ that conforms to POSIX can serve as /bin/sh. Since dash is POSIX compliant,
++ it can be used as /bin/sh.
++Description-pt_BR: Instalar dash como /bin/sh ?
++ Bash é o /bin/sh padrão em um sistema Debian. Porém, uma vez que nossa
++ política requer que todos os shell scripts usando /bin/sh sejam compatíveis
++ POSIX, qualquer shell que esteja em conformidade POSIX pode servir como
++ /bin/sh. Uma vez que dash é compatível POSIX, pode ser usado como /bin/sh.
++
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.ru bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.ru
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.ru 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.ru 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,13 @@
++Template: dash/sh
++Description: Install dash as /bin/sh?
++ Bash is the default /bin/sh on a Debian system. However, since our policy
++ requires all shell scripts using /bin/sh to be POSIX compliant, any shell
++ that conforms to POSIX can serve as /bin/sh. Since dash is POSIX compliant,
++ it can be used as /bin/sh.
++Description-ru: õÓÔÁÎÏ×ÉÔØ dash ËÁË /bin/sh?
++ Bash - ÜÔÏ /bin/sh ÐÏ ÕÍÏÌÞÁÎÉÀ × ÓÉÓÔÅÍÅ Debian. ïÄÎÁËÏ, ÔÁË ËÁË ÎÁÛÁ
++ ÐÏÌÉÔÉËÁ ÔÒÅÂÕÅÔ, ÞÔÏÂÙ ×ÓÅ ÓÃÅÎÁÒÉÉ ÏÂÏÌÏÞËÉ, ÉÓÐÏÌØÚÕÀÝÉÅ /bin/sh,
++ ÂÙÌÉ ÓÏ×ÍÅÓÔÉÍÙÍÉ Ó POSIX, ÔÏ ÒÁÂÏÔÁÔØ ËÁË /bin/sh ÍÏÖÅÔ ÌÀÂÁÑ
++ ÏÂÏÌÏÞËÁ, ÓÏ×ÍÅÓÔÉÍÁÑ Ó POSIX. ôÁË ËÁË dash ÓÏ×ÍÅÓÔÉÍ Ó POSIX, ÔÏ ÏÎ
++ ÍÏÖÅÔ ÉÓÐÏÌØÚÏ×ÁÔØÓÑ ËÁË /bin/sh.
++
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.sv bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.sv
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash.templates.sv 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash.templates.sv 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,13 @@
++Template: dash/sh
++Type: boolean
++Description: Install dash as /bin/sh?
++ Bash is the default /bin/sh on a Debian system. However, since our policy
++ requires all shell scripts using /bin/sh to be POSIX compliant, any shell
++ that conforms to POSIX can serve as /bin/sh. Since dash is POSIX compliant,
++ it can be used as /bin/sh.
++Description-sv: Installera dash som /bin/sh?
++ Bash är standardinställningen för /bin/sh på Debiansystem. Eftersom vår
++ policy kräver att alla script som använder /bin/sh måste vara
++ POSIX-kompatibla, kan vilket POSIX-kompatibelt skal som helst vara /bi/sh. Då
++ dash är POSIX-kompatibelt kan det användas som /bin/sh.
++
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash-udeb.dirs bin_NetBSD-1.6release/src/bin/sh/debian/dash-udeb.dirs
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/dash-udeb.dirs 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/dash-udeb.dirs 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1 @@
++bin
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/debian/rules bin_NetBSD-1.6release/src/bin/sh/debian/rules
+--- bin_NetBSD-1.6release.orig/src/bin/sh/debian/rules 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/debian/rules 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,101 @@
++#!/usr/bin/make -f
++# $Id: dash-ash-hetio-yacc.diff,v 1.1 2003/02/09 09:36:31 satai Exp $
++
++# Uncomment this to turn on verbose mode.
++#export DH_VERBOSE=1
++
++# This has to be exported to make some magic below work.
++export DH_OPTIONS
++
++DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH)
++
++CDEF := \
++ -Wall -DBSD=1 -DSMALL -D_GNU_SOURCE -DGLOB_BROKEN -DHAVE_VASPRINTF=1 \
++ -DIFS_BROKEN -DGCC_BROKEN_NG \
++ -D__COPYRIGHT\(x\)= -D__RCSID\(x\)= -D_DIAGASSERT\(x\)=
++
++OPT := -g -O2 -fstrict-aliasing
++OPTSM := -g -Os -fstrict-aliasing -fomit-frame-pointer -DREALLY_SMALL
++
++ifeq ($(DEB_HOST_ARCH),i386)
++OPTSM += \
++ -malign-loops=0 -malign-jumps=0 -malign-functions=0 \
++ -mpreferred-stack-boundary=2 -DUSE_NORETURN
++endif
++
++setup: setup-stamp
++setup-stamp:
++ rm -rf obj obj-udeb
++ mkdir obj obj-udeb
++ chmod u+x debian/bsdyacc
++ touch setup-stamp
++
++build: setup-stamp
++ dh_testdir
++
++ pmake CFLAGS:='$(CDEF) $(OPT)' \
++ YACC:='$${.CURDIR}/debian/bsdyacc'
++ MAKEOBJDIR=obj-udeb pmake CFLAGS:='$(CDEF) $(OPTSM)' \
++ YACC:='$${.CURDIR}/debian/bsdyacc'
++
++clean:
++ dh_testdir
++ dh_testroot
++ rm -f setup-stamp
++
++ rm -rf obj obj-udeb
++
++ dh_clean
++
++install: build
++ dh_testdir
++ dh_testroot
++ dh_clean -k
++ dh_installdirs
++
++ install obj/sh debian/dash/bin/dash
++ install -m 644 sh.1 debian/dash/usr/share/man/man1/dash.1
++ install obj-udeb/sh debian/dash-udeb/bin/dash
++ ln -s dash debian/dash-udeb/bin/sh
++ ln -s dash debian/ash/bin/ash
++ ln -s dash.1.gz debian/ash/usr/share/man/man1/ash.1.gz
++
++# This single target is used to build all the packages, all at once, or
++# one at a time. So keep in mind: any options passed to commands here will
++# affect _all_ packages. Anything you want to only affect one package
++# should be put in another target, such as the install target.
++binary-common:
++ dh_testdir
++ dh_testroot
++ dh_installdebconf
++ dh_installdocs -Ndash-udeb
++ dh_installexamples
++ dh_installmenu
++ dh_installcron
++ dh_installchangelogs -Ndash-udeb
++ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
++ dh_strip
++endif
++ dh_compress
++ dh_fixperms
++ dh_installdeb -Ndash-udeb
++ dh_shlibdeps
++ dh_gencontrol
++ dh_md5sums
++ dh_builddeb
++
++# Build architecture-independent files here.
++binary-indep: install
++ $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common
++
++# Build architecture-dependent files here.
++binary-arch: install
++ $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common
++
++ for i in ../dash-udeb_*.deb; do mv $$i $${i%deb}udeb; done
++ sed '/^[^ ]*\.udeb/d; s/^\(dash-udeb_[^ ]*\.\)deb/\1udeb/' \
++ debian/files > debian/files.new
++ mv debian/files.new debian/files
++
++binary: binary-indep binary-arch
++.PHONY: build clean binary-indep binary-arch binary binary-common install setup
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/error.c bin_NetBSD-1.6release/src/bin/sh/error.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/error.c 2002-05-16 11:41:19.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/error.c 2003-02-08 14:35:42.000000000 +0000
+@@ -1,4 +1,4 @@
+-/* $NetBSD: error.c,v 1.24 2002/05/15 16:33:35 christos Exp $ */
++/* $NetBSD: error.c,v 1.25 2002/05/25 23:09:06 wiz Exp $ */
+
+ /*-
+ * Copyright (c) 1991, 1993
+@@ -41,7 +41,7 @@
+ #if 0
+ static char sccsid[] = "@(#)error.c 8.2 (Berkeley) 5/4/95";
+ #else
+-__RCSID("$NetBSD: error.c,v 1.24 2002/05/15 16:33:35 christos Exp $");
++__RCSID("$NetBSD: error.c,v 1.25 2002/05/25 23:09:06 wiz Exp $");
+ #endif
+ #endif /* not lint */
+
+@@ -51,6 +51,7 @@
+
+ #include <signal.h>
+ #include <stdlib.h>
++#include <string.h>
+ #include <unistd.h>
+ #include <errno.h>
+
+@@ -60,6 +61,8 @@
+ #include "output.h"
+ #include "error.h"
+ #include "show.h"
++#include "eval.h"
++#include "parser.h"
+
+
+ /*
+@@ -68,13 +71,12 @@
+
+ struct jmploc *handler;
+ int exception;
+-volatile int suppressint;
+-volatile int intpending;
+-char *commandname;
++int suppressint;
++volatile sig_atomic_t intpending;
+
+
+-static void exverror __P((int, const char *, va_list))
+- __attribute__((__noreturn__));
++static void exverror(int, const char *, va_list) __attribute__((__noreturn__));
++static void vwarn(const char *, va_list);
+
+ /*
+ * Called to raise an exception. Since C doesn't include exceptions, we
+@@ -86,8 +88,12 @@
+ exraise(e)
+ int e;
+ {
++#ifdef DEBUG
+ if (handler == NULL)
+ abort();
++#endif
++ INTOFF;
++
+ exception = e;
+ longjmp(handler->loc, 1);
+ }
+@@ -97,29 +103,18 @@
+ * 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.)
++ * are held using the INTOFF macro. (The test for iflag is just
++ * defensive programming.)
+ */
+
+ void
+ onint() {
+- sigset_t sigset;
+-
+- if (suppressint) {
+- intpending++;
+- return;
+- }
+ intpending = 0;
+- sigemptyset(&sigset);
+- sigprocmask(SIG_SETMASK, &sigset, NULL);
+- if (rootshell && iflag)
+- exraise(EXINT);
+- else {
++ if (!(rootshell && iflag)) {
+ signal(SIGINT, SIG_DFL);
+ raise(SIGINT);
+ }
++ exraise(EXINT);
+ /* NOTREACHED */
+ }
+
+@@ -135,73 +130,37 @@
+ const char *msg;
+ va_list ap;
+ {
+- CLEAR_PENDING_INT;
+- INTOFF;
+-
+ #ifdef DEBUG
+ if (msg)
+ TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid()));
+ else
+ TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
+ #endif
+- if (msg) {
+- if (commandname)
+- outfmt(&errout, "%s: ", commandname);
+- doformat(&errout, msg, ap);
+- out2c('\n');
+- }
++ vwarn(msg, ap);
+ flushall();
+ exraise(cond);
+ /* NOTREACHED */
+ }
+
+
+-#ifdef __STDC__
+ void
+ error(const char *msg, ...)
+-#else
+-void
+-error(va_alist)
+- va_dcl
+-#endif
+ {
+-#ifndef __STDC__
+- const char *msg;
+-#endif
+ va_list ap;
+-#ifdef __STDC__
++
+ va_start(ap, msg);
+-#else
+- va_start(ap);
+- msg = va_arg(ap, const char *);
+-#endif
+ exverror(EXERROR, msg, ap);
+ /* NOTREACHED */
+ va_end(ap);
+ }
+
+
+-#ifdef __STDC__
+ void
+ exerror(int cond, const char *msg, ...)
+-#else
+-void
+-exerror(va_alist)
+- va_dcl
+-#endif
+ {
+-#ifndef __STDC__
+- int cond;
+- const char *msg;
+-#endif
+ va_list ap;
+-#ifdef __STDC__
++
+ va_start(ap, msg);
+-#else
+- va_start(ap);
+- cond = va_arg(ap, int);
+- msg = va_arg(ap, const char *);
+-#endif
+ exverror(cond, msg, ap);
+ /* NOTREACHED */
+ va_end(ap);
+@@ -223,56 +182,14 @@
+ #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_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_OPEN, "No such file" },
++ { ENOTDIR, E_CREAT,"Directory nonexistent" },
+ { ENOTDIR, E_EXEC, "not found" },
+- { EISDIR, ALL, "is a directory" },
+-#ifdef notdef
+- { 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 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" },
++ { EAGAIN, E_EXEC, "Not enough memory" },
+ #endif
+ { 0, 0, NULL },
+ };
+@@ -290,12 +207,55 @@
+ 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;
++ return strerror(e);
++}
++
++
++#ifdef REALLY_SMALL
++void
++__inton() {
++ if (--suppressint == 0 && intpending) {
++ onint();
++ }
++}
++#endif
++
++
++
++/*
++ * Print an error message for the current command.
++ */
++static void vwarn(const char *const 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
++}
++
++
++void warnx(const char *msg, ...) {
++ va_list ap;
++
++ va_start(ap, msg);
++ vwarn(msg, ap);
++ va_end(ap);
+ }
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/error.h bin_NetBSD-1.6release/src/bin/sh/error.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/error.h 2001-02-05 11:15:29.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/error.h 2003-02-08 14:35:42.000000000 +0000
+@@ -38,6 +38,9 @@
+ * @(#)error.h 8.2 (Berkeley) 5/4/95
+ */
+
++#include <setjmp.h>
++#include <signal.h>
++
+ /*
+ * Types of operations (passed to the errmsg routine).
+ */
+@@ -57,8 +60,6 @@
+ * inner scope, and restore handler on exit from the scope.
+ */
+
+-#include <setjmp.h>
+-
+ struct jmploc {
+ jmp_buf loc;
+ };
+@@ -71,6 +72,7 @@
+ #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 */
+
+
+ /*
+@@ -80,20 +82,53 @@
+ * 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;
+
+-#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 __P((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(); \
++ })
+ #define CLEAR_PENDING_INT intpending = 0
+ #define int_pending() intpending
+
+ void exraise __P((int)) __attribute__((__noreturn__));
++#ifdef USE_NORETURN
++void onint __P((void)) __attribute__((__noreturn__));
++#else
+ void onint __P((void));
++#endif
+ void error __P((const char *, ...)) __attribute__((__noreturn__));
+ void exerror __P((int, const char *, ...)) __attribute__((__noreturn__));
+ const char *errmsg __P((int, int));
++void warnx __P((const char *, ...));
+
+
+ /*
+@@ -101,7 +136,7 @@
+ * so we use _setjmp instead.
+ */
+
+-#if defined(BSD) && !defined(__SVR4)
++#if defined(BSD) && !defined(__SVR4) && !defined(__GLIBC__)
+ #define setjmp(jmploc) _setjmp(jmploc)
+ #define longjmp(jmploc, val) _longjmp(jmploc, val)
+ #endif
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/eval.c bin_NetBSD-1.6release/src/bin/sh/eval.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/eval.c 2002-05-16 11:41:20.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/eval.c 2003-02-08 14:35:42.000000000 +0000
+@@ -45,8 +45,11 @@
+ #endif
+ #endif /* not lint */
+
++#include <sys/types.h>
+ #include <signal.h>
++#include <malloc.h>
+ #include <unistd.h>
++#include <errno.h>
+
+ /*
+ * Evaluate a command.
+@@ -98,9 +101,18 @@
+ STATIC void evalcase __P((union node *, int));
+ STATIC void evalsubshell __P((union node *, int));
+ STATIC void expredir __P((union node *));
+-STATIC void evalpipe __P((union node *));
++STATIC void evalpipe __P((union node *, int));
++#ifdef notyet
+ STATIC void evalcommand __P((union node *, int, struct backcmd *));
++#else
++STATIC void evalcommand __P((union node *, int));
++#endif
+ STATIC void prehash __P((union node *));
++STATIC void eprintlist __P((struct strlist *));
++#if !defined(__alpha__) || !defined(GCC_BROKEN_NG)
++STATIC
++#endif
++void evaltreenr __P((union node *, int)) __attribute__ ((noreturn));
+
+
+ /*
+@@ -115,10 +127,6 @@
+ loopnest = 0;
+ funcnest = 0;
+ }
+-
+-SHELLPROC {
+- exitstatus = 0;
+-}
+ #endif
+
+
+@@ -142,8 +150,7 @@
+ STARTSTACKSTR(concat);
+ ap = argv + 2;
+ for (;;) {
+- while (*p)
+- STPUTC(*p++, concat);
++ concat = stputs(p, concat);
+ if ((p = *ap++) == NULL)
+ break;
+ STPUTC(' ', concat);
+@@ -170,10 +177,12 @@
+ struct stackmark smark;
+
+ setstackmark(&smark);
+- setinputstring(s, 1);
++ setinputstring(s);
+ while ((n = parsecmd(0)) != NEOF) {
+ evaltree(n, flag);
+ popstackmark(&smark);
++ if (evalskip)
++ break;
+ }
+ popfile();
+ popstackmark(&smark);
+@@ -191,9 +200,12 @@
+ 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"));
+- exitstatus = 0;
+ goto out;
+ }
+ #ifndef SMALL
+@@ -201,89 +213,122 @@
+ #endif
+ TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
+ 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) {
+- /* don't bomb out on "set -e; false && true" */
+- flags |= EV_TESTED;
+- 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);
++ 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);
+- redirect(n->nredir.redirect, REDIR_PUSH);
+- evaltree(n->nredir.n, flags);
+- popredir();
++ 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:
+- evalsubshell(n, flags);
+- break;
+ case NBACKGND:
+- evalsubshell(n, flags);
++ 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;
+- 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;
++ if (!evalskip) {
++ n = n->nbinary.ch2;
++evaln:
++ evalfn = evaltree;
++calleval:
++ evalfn(n, flags);
+ break;
+ }
+- case NWHILE:
+- case NUNTIL:
+- evalloop(n, flags);
+ break;
+- case NFOR:
+- evalfor(n, flags);
+- break;
+- case NCASE:
+- evalcase(n, flags);
++ 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);
+- 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);
++success:
++ status = 0;
++setstatus:
++ exitstatus = status;
+ break;
+ }
+ out:
+ if (pendingsigs)
+ dotrap();
+- if ((flags & EV_EXIT) != 0)
+- exitshell(exitstatus);
++ if (flags & EV_EXIT || checkexit & exitstatus)
++ exraise(EXEXIT);
+ }
+
+
++#if !defined(__alpha__) || !defined(GCC_BROKEN_NG)
++STATIC
++#endif
++void evaltreenr(union node *, int) __attribute__ ((alias("evaltree")));
++
++
+ STATIC void
+ evalloop(n, flags)
+ union node *n;
+@@ -293,6 +338,7 @@
+
+ loopnest++;
+ status = 0;
++ flags &= EV_TESTED;
+ for (;;) {
+ evaltree(n->nbinary.ch1, EV_TESTED);
+ if (evalskip) {
+@@ -311,7 +357,7 @@
+ if (exitstatus == 0)
+ break;
+ }
+- evaltree(n->nbinary.ch2, flags & EV_TESTED);
++ evaltree(n->nbinary.ch2, flags);
+ status = exitstatus;
+ if (evalskip)
+ goto skipping;
+@@ -344,9 +390,10 @@
+
+ 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 & EV_TESTED);
++ evaltree(n->nfor.body, flags);
+ if (evalskip) {
+ if (evalskip == SKIPCONT && --skipcount <= 0) {
+ evalskip = 0;
+@@ -377,6 +424,7 @@
+ setstackmark(&smark);
+ arglist.lastp = &arglist.list;
+ oexitstatus = exitstatus;
++ exitstatus = 0;
+ 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) {
+@@ -405,20 +453,28 @@
+ {
+ 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);
+- evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
++ evaltreenr(n->nredir.n, flags);
++ /* never returns */
+ }
+- if (! backgnd) {
+- INTOFF;
+- exitstatus = waitforjob(jp);
++ status = 0;
++ if (!backgnd)
++ status = waitforjob(jp);
++ exitstatus = status;
+ INTON;
+- }
+ }
+
+
+@@ -467,8 +523,9 @@
+ */
+
+ STATIC void
+-evalpipe(n)
++evalpipe(n, flags)
+ union node *n;
++ int flags;
+ {
+ struct job *jp;
+ struct nodelist *lp;
+@@ -480,6 +537,7 @@
+ pipelen = 0;
+ for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
+ pipelen++;
++ flags |= EV_EXIT;
+ INTOFF;
+ jp = makejob(n, pipelen);
+ prevfd = -1;
+@@ -494,33 +552,29 @@
+ }
+ if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
+ INTON;
++ if (pip[1] >= 0) {
++ close(pip[0]);
++ }
+ if (prevfd > 0) {
+- close(0);
+- copyfd(prevfd, 0);
++ dup2(prevfd, 0);
+ close(prevfd);
+ }
+- if (pip[1] >= 0) {
+- close(pip[0]);
+- if (pip[1] != 1) {
+- close(1);
+- copyfd(pip[1], 1);
++ if (pip[1] > 1) {
++ dup2(pip[1], 1);
+ close(pip[1]);
+ }
+- }
+- evaltree(lp->n, EV_EXIT);
++ evaltree(lp->n, flags);
+ }
+ if (prevfd >= 0)
+ close(prevfd);
+ prevfd = pip[0];
+ close(pip[1]);
+ }
+- INTON;
+ if (n->npipe.backgnd == 0) {
+- INTOFF;
+ exitstatus = waitforjob(jp);
+ TRACE(("evalpipe: job done exit status %d\n", exitstatus));
+- INTON;
+ }
++ INTON;
+ }
+
+
+@@ -595,10 +649,16 @@
+ */
+
+ STATIC void
++#ifdef notyet
+ evalcommand(cmd, flags, backcmd)
+ union node *cmd;
+ int flags;
+ struct backcmd *backcmd;
++#else
++evalcommand(cmd, flags)
++ union node *cmd;
++ int flags;
++#endif
+ {
+ struct stackmark smark;
+ union node *argp;
+@@ -607,25 +667,30 @@
+ char **argv;
+ int argc;
+ char **envp;
+- int varflag;
+ struct strlist *sp;
+ int mode;
++#ifdef notyet
+ int pip[2];
++#endif
+ 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;
++ int spclbltin;
++ int redir;
++ struct jmploc *volatile savehandler;
++ struct jmploc jmploc;
+ #if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &argv;
+ (void) &argc;
+ (void) &lastarg;
+ (void) &flags;
++ (void) &spclbltin;
++ (void) &redir;
+ #endif
+
+ /* First expand the arguments. */
+@@ -633,22 +698,15 @@
+ setstackmark(&smark);
+ arglist.lastp = &arglist.list;
+ varlist.lastp = &varlist.list;
+- varflag = 1;
++ arglist.list = 0;
+ oexitstatus = exitstatus;
+ exitstatus = 0;
+- 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 == '=') {
++ path = pathval();
++ for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
+ expandarg(argp, &varlist, EXP_VARTILDE);
+- continue;
+- }
+ }
++ for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
+ expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+- varflag = 0;
+ }
+ *arglist.lastp = NULL;
+ *varlist.lastp = NULL;
+@@ -670,77 +728,140 @@
+
+ /* Print the command if xflag is set. */
+ if (xflag) {
++#ifdef FLUSHERR
+ outc('+', &errout);
+- for (sp = varlist.list ; sp ; sp = sp->next) {
+- outc(' ', &errout);
+- out2str(sp->text);
+- }
+- for (sp = arglist.list ; sp ; sp = sp->next) {
+- outc(' ', &errout);
+- out2str(sp->text);
+- }
++#else
++ outcslow('+', &errout);
++#endif
++ eprintlist(varlist.list);
++ eprintlist(arglist.list);
++#ifdef FLUSHERR
+ outc('\n', &errout);
+ flushout(&errout);
++#else
++ outcslow('\n', &errout);
++#endif
+ }
+
++ redir = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH);
++
+ /* Now locate the command. */
+ if (argc == 0) {
+ cmdentry.cmdtype = CMDBUILTIN;
+- cmdentry.u.index = BLTINCMD;
++ cmdentry.u.cmd = BLTINCMD;
++ spclbltin = 1;
+ } else {
+- static const char PATH[] = "PATH=";
+- const char *path = pathval();
++ const char *oldpath;
++ int findflag = DO_ERR;
++ int oldfindflag;
+
+ /*
+ * 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;
+-
+- find_command(argv[0], &cmdentry, DO_ERR, path);
++ if (varequal(sp->text, defpathvar)) {
++ path = sp->text + 5;
++ findflag |= DO_BRUTE;
++ }
++ oldpath = path;
++ oldfindflag = findflag;
++ spclbltin = -1;
++ for(;;) {
++ find_command(argv[0], &cmdentry, findflag, path);
+ if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */
+- exitstatus = 127;
+- flushout(&errout);
+- return;
++ goto notfound;
+ }
+- /* implement the bltin builtin here */
+- if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
+- for (;;) {
++ /* implement bltin and command here */
++ if (cmdentry.cmdtype != CMDBUILTIN) {
++ break;
++ }
++ if (spclbltin < 0) {
++ spclbltin =
++ !!(
++ cmdentry.u.cmd->flags &
++ BUILTIN_SPECIAL
++ ) * 2
++ ;
++ }
++ if (cmdentry.u.cmd == BLTINCMD) {
++ for(;;) {
++ struct builtincmd *bcmd;
++
+ argv++;
+ if (--argc == 0)
+- break;
+- if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
++ goto found;
++ if (!(bcmd = find_builtin(*argv))) {
+ outfmt(&errout, "%s: not found\n", *argv);
++notfound:
+ exitstatus = 127;
++#ifdef FLUSHERR
+ flushout(&errout);
+- return;
++#endif
++ goto out;
+ }
+- if (cmdentry.u.index != BLTINCMD)
++ cmdentry.u.cmd = bcmd;
++ if (bcmd != BLTINCMD)
+ break;
+ }
+ }
++ if (cmdentry.u.cmd == COMMANDCMD) {
++ argv++;
++ if (--argc == 0) {
++ goto found;
++ }
++ if (*argv[0] == '-') {
++ if (!equal(argv[0], "-p")) {
++ argv--;
++ argc++;
++ break;
++ }
++ argv++;
++ if (--argc == 0) {
++ goto found;
++ }
++ path = defpath;
++ findflag |= DO_BRUTE;
++ } else {
++ path = oldpath;
++ findflag = oldfindflag;
++ }
++ findflag |= DO_NOFUN;
++ continue;
++ }
++found:
++ break;
++ }
+ }
+
+ /* Fork off a child process if necessary. */
+- if (cmd->ncmd.backgnd
+- || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
++ if (
++ (
++ cmdentry.cmdtype == CMDNORMAL &&
++ (!(flags & EV_EXIT) || trap[0])
++ )
++#ifdef notyet
+ || ((flags & EV_BACKCMD) != 0
+ && (cmdentry.cmdtype != CMDBUILTIN
+- || cmdentry.u.index == DOTCMD
+- || cmdentry.u.index == EVALCMD))) {
++ || cmdentry.u.bcmd == DOTCMD
++ || cmdentry.u.bcmd == EVALCMD))
++#endif
++ ) {
++ INTOFF;
+ jp = makejob(cmd, 1);
+- mode = cmd->ncmd.backgnd;
++ mode = FORK_FG;
++#ifdef notyet
+ if (flags & EV_BACKCMD) {
+ mode = FORK_NOJOB;
+ if (pipe(pip) < 0)
+ error("Pipe call failed");
+ }
++#endif
+ if (forkshell(jp, cmd, mode) != 0)
+ goto parent; /* at end of routine */
+- if (flags & EV_BACKCMD) {
+ FORCEINTON;
++#ifdef notyet
++ if (flags & EV_BACKCMD) {
+ close(pip[0]);
+ if (pip[1] != 1) {
+ close(1);
+@@ -748,148 +869,168 @@
+ close(pip[1]);
+ }
+ }
++#endif
+ flags |= EV_EXIT;
++ } else {
++ flags &= ~EV_EXIT;
+ }
+
+ /* This is the child process if a fork occurred. */
+ /* Execute the command. */
+- if (cmdentry.cmdtype == CMDFUNCTION) {
++ if (redir) {
++ /* We have a redirection error. */
++ exitstatus = redir;
++ if (spclbltin == 2)
++ exraise(EXERROR);
++ } else if (cmdentry.cmdtype == CMDFUNCTION) {
+ #ifdef DEBUG
+ trputs("Shell function: "); trargs(argv);
+ #endif
+- redirect(cmd->ncmd.redirect, REDIR_PUSH);
++ exitstatus = oexitstatus;
+ 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;
++ exception = -1;
+ 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);
++ goto funcdone;
+ }
++ INTOFF;
+ savehandler = handler;
+ handler = &jmploc;
+- for (sp = varlist.list ; sp ; sp = sp->next)
+- mklocal(sp->text);
++ localvars = NULL;
++ shellparam.malloc = 0;
++ cmdentry.u.func->count++;
++ INTON;
++ shellparam.nparam = argc - 1;
++ shellparam.p = argv + 1;
++ shellparam.optind = 1;
++ shellparam.optoff = -1;
++ listsetvar(varlist.list, 0);
+ funcnest++;
+- evaltree(cmdentry.u.func, flags & EV_TESTED);
++ evaltree(&cmdentry.u.func->n, flags & EV_TESTED);
+ funcnest--;
++funcdone:
+ INTOFF;
++ freefunc(cmdentry.u.func);
+ poplocalvars();
+ localvars = savelocalvars;
+ freeparam(&shellparam);
+ shellparam = saveparam;
+ handler = savehandler;
+- popredir();
+ INTON;
++ if (exception >= 0) {
++ longjmp(handler->loc, 1);
++ }
+ if (evalskip == SKIPFUNC) {
+ evalskip = 0;
+ skipcount = 0;
+ }
+- if (flags & EV_EXIT)
+- exitshell(exitstatus);
+ } else if (cmdentry.cmdtype == CMDBUILTIN) {
+ #ifdef DEBUG
+ trputs("builtin command: "); trargs(argv);
+ #endif
+- mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
+- if (flags == EV_BACKCMD) {
+- memout.nleft = 0;
+- memout.nextc = memout.buf;
+- memout.bufsize = 64;
+- mode |= REDIR_BACKQ;
++ if (spclbltin) {
++ int f = 0;
++ if (cmdentry.u.cmd == EXECCMD) {
++ redir++;
++ if (argc > 1)
++ f = VEXPORT;
++ }
++ listsetvar(varlist.list, f);
+ }
+- redirect(cmd->ncmd.redirect, mode);
+ savecmdname = commandname;
+ cmdenviron = varlist.list;
+- e = -1;
++ exception = -1;
+ if (setjmp(jmploc.loc)) {
+- e = exception;
+- exitstatus = (e == EXINT)? SIGINT+128 : 2;
+ goto cmddone;
+ }
+ savehandler = handler;
+ handler = &jmploc;
++#ifdef notyet
++ if (flags == EV_BACKCMD) {
++#ifdef USE_GLIBC_STDIO
++ openmemout();
++#else
++ memout.nleft = 0;
++ memout.nextc = memout.buf;
++ memout.bufsize = 64;
++#endif
++ }
++#endif
+ commandname = argv[0];
+ argptr = argv + 1;
+ optptr = NULL; /* initialize nextopt */
+- exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
++ exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv);
+ flushall();
+ cmddone:
++ exitstatus |= outerr(out1);
++#ifdef notyet
+ out1 = &output;
+ out2 = &errout;
++#endif
+ freestdout();
+ cmdenviron = NULL;
+- if (e != EXSHELLPROC) {
+ commandname = savecmdname;
+- if (flags & EV_EXIT)
+- exitshell(exitstatus);
+- }
+ handler = savehandler;
+- if (e != -1) {
+- if ((e != EXERROR && e != EXEXEC)
+- || cmdentry.u.index == BLTINCMD
+- || cmdentry.u.index == DOTCMD
+- || cmdentry.u.index == EVALCMD
+-#ifndef SMALL
+- || cmdentry.u.index == HISTCMD
+-#endif
+- || cmdentry.u.index == EXECCMD)
+- exraise(e);
++ if (exception >= 0) {
++ int f = exception;
++ if (f != EXEXIT) {
++ int status = (f == EXINT) ? SIGINT + 128 : 2;
++ exitstatus = status;
++ }
++ if (f == EXINT || spclbltin & 2) {
++ exraise(f);
++ }
+ FORCEINTON;
+ }
+- if (cmdentry.u.index != EXECCMD)
+- popredir();
++#ifdef notyet
+ if (flags == EV_BACKCMD) {
++#ifdef USE_GLIBC_STDIO
++ if (__closememout()) {
++ error(
++ "__closememout() failed: %s",
++ strerror(errno)
++ );
++ }
++#endif
+ backcmd->buf = memout.buf;
++#ifdef USE_GLIBC_STDIO
++ backcmd->nleft = memout.bufsize;
++#else
+ backcmd->nleft = memout.nextc - memout.buf;
++#endif
+ memout.buf = NULL;
+ }
++#endif
+ } else {
+ #ifdef DEBUG
+ trputs("normal command: "); trargs(argv);
+ #endif
+- clearredir();
+- redirect(cmd->ncmd.redirect, 0);
+ for (sp = varlist.list ; sp ; sp = sp->next)
+ setvareq(sp->text, VEXPORT|VSTACK);
+ envp = environment();
+- shellexec(argv, envp, pathval(), cmdentry.u.index);
++ shellexec(argv, envp, path, cmdentry.u.index);
+ }
++ if (flags & EV_EXIT)
++ exraise(EXEXIT);
+ goto out;
+
+ parent: /* parent process gets here (if we forked) */
+- if (mode == 0) { /* argument to fork */
+- INTOFF;
++ if (mode == FORK_FG) { /* argument to fork */
+ exitstatus = waitforjob(jp);
+- INTON;
+- } else if (mode == 2) {
++#ifdef notyet
++ } else if (mode == FORK_NOJOB) {
+ backcmd->fd = pip[0];
+ close(pip[1]);
+ backcmd->jp = jp;
++#endif
+ }
++ INTON;
+
+ out:
++ popredir(redir);
+ if (lastarg)
+ setvar("_", lastarg, 0);
+ popstackmark(&smark);
+-
+- if (eflag && exitstatus && !(flags & EV_TESTED))
+- exitshell(exitstatus);
+ }
+
+
+@@ -897,8 +1038,7 @@
+ /*
+ * 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.
++ * the child.
+ */
+
+ STATIC void
+@@ -908,9 +1048,7 @@
+ 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());
++ find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
+ }
+
+
+@@ -930,7 +1068,6 @@
+ int argc;
+ char **argv;
+ {
+- listsetvar(cmdenviron);
+ /*
+ * Preserve exitstatus of a previous possible redirection
+ * as POSIX mandates
+@@ -957,6 +1094,8 @@
+ {
+ int n = argc > 1 ? number(argv[1]) : 1;
+
++ if (n <= 0)
++ error(illnum, argv[1]);
+ if (n > loopnest)
+ n = loopnest;
+ if (n > 0) {
+@@ -1016,14 +1155,18 @@
+ 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);
+ }
+ return 0;
+ }
++
++STATIC void
++eprintlist(struct strlist *sp)
++{
++ for (; sp; sp = sp->next) {
++ outfmt(&errout, " %s",sp->text);
++ }
++}
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/exec.c bin_NetBSD-1.6release/src/bin/sh/exec.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/exec.c 2001-02-05 11:15:30.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/exec.c 2003-02-08 14:35:42.000000000 +0000
+@@ -51,6 +51,9 @@
+ #include <fcntl.h>
+ #include <errno.h>
+ #include <stdlib.h>
++#include <sysexits.h>
++#include <stdbool.h>
++#include <paths.h>
+
+ /*
+ * When commands are first encountered, they are entered in a hash table.
+@@ -99,19 +102,21 @@
+
+ STATIC struct tblentry *cmdtable[CMDTABLESIZE];
+ STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
+-int exerrno = 0; /* Last exec error */
++int exerrno; /* Last exec error */
+
+
+ STATIC void tryexec __P((char *, char **, char **));
++#if !defined(BSD) && !defined(linux)
+ STATIC void execinterp __P((char **, char **));
++#endif
+ STATIC void printentry __P((struct tblentry *, int));
+ STATIC void clearcmdentry __P((int));
+ STATIC struct tblentry *cmdlookup __P((char *, int));
+ STATIC void delete_cmd_entry __P((void));
++STATIC int describe_command __P((struct output *, char *, int));
++STATIC int path_change __P((const char *, int *));
+
+
+-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.
+@@ -126,6 +131,7 @@
+ char *cmdname;
+ int e;
+
++ clearredir(1);
+ if (strchr(argv[0], '/') != NULL) {
+ tryexec(argv[0], argv, envp);
+ e = errno;
+@@ -164,11 +170,12 @@
+ char **argv;
+ char **envp;
+ {
+- int e;
+-#ifndef BSD
++ int repeated = 0;
++#if !defined(BSD) && !defined(linux)
+ char *p;
+ #endif
+
++repeat:
+ #ifdef SYSV
+ do {
+ execve(cmd, argv, envp);
+@@ -176,103 +183,22 @@
+ #else
+ execve(cmd, argv, envp);
+ #endif
+- e = errno;
+- if (e == ENOEXEC) {
+- initshellproc();
+- setinputfile(cmd, 0);
+- commandname = arg0 = savestr(argv[0]);
+-#ifndef BSD
+- 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;
+-}
+-
+-
+-#ifndef BSD
+-/*
+- * 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(argv, envp)
+- char **argv, **envp;
+- {
+- int n;
+- char *inp;
+- char *outp;
+- char c;
+- char *p;
++ if (repeated++) {
++ ckfree(argv);
++ } else if (errno == ENOEXEC) {
+ 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:;
++ for (ap = argv; *ap; ap++)
++ ;
++ ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
++ *ap++ = cmd = _PATH_BSHELL;
++ while ((*ap++ = *argv++))
++ ;
++ argv = new;
++ goto repeat;
+ }
+- 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
+
+
+
+@@ -296,7 +222,7 @@
+ const char *p;
+ char *q;
+ const char *start;
+- int len;
++ size_t len;
+
+ if (*path == NULL)
+ return NULL;
+@@ -345,6 +271,7 @@
+ while ((c = nextopt("rv")) != '\0') {
+ if (c == 'r') {
+ clearcmdentry(0);
++ return 0;
+ } else if (c == 'v') {
+ verbose++;
+ }
+@@ -352,27 +279,29 @@
+ if (*argptr == NULL) {
+ for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+ for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
++ if (cmdp->cmdtype != CMDBUILTIN) {
+ printentry(cmdp, verbose);
+ }
+ }
++ }
+ 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 (verbose) {
+- if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
++ if (entry.cmdtype == CMDUNKNOWN) c = 1;
++ else if (verbose) {
+ cmdp = cmdlookup(name, 0);
+- printentry(cmdp, verbose);
+- }
++ if (cmdp) printentry(cmdp, verbose);
+ flushall();
+ }
+ argptr++;
+ }
+- return 0;
++ return c;
+ }
+
+
+@@ -399,9 +328,8 @@
+ out1fmt("function %s", cmdp->cmdname);
+ if (verbose) {
+ INTOFF;
+- name = commandtext(cmdp->param.func);
+- out1c(' ');
+- out1str(name);
++ name = commandtext(&cmdp->param.func->n);
++ out1fmt(" %s", name);
+ ckfree(name);
+ INTON;
+ }
+@@ -410,9 +338,7 @@
+ error("internal error: cmdtype %d", cmdp->cmdtype);
+ #endif
+ }
+- if (cmdp->rehash)
+- out1c('*');
+- out1c('\n');
++ out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr);
+ }
+
+
+@@ -433,14 +359,18 @@
+ int idx;
+ int prev;
+ char *fullname;
+- struct stat statb;
++ struct stat64 statb;
+ int e;
+- int i;
++ int bltin;
++ int firstchange;
++ int updatetbl;
++ bool regular;
++ struct builtincmd *bcmd;
+
+ /* If name contains a slash, don't use the hash table */
+ if (strchr(name, '/') != NULL) {
+ if (act & DO_ABS) {
+- while (stat(name, &statb) < 0) {
++ while (stat64(name, &statb) < 0) {
+ #ifdef SYSV
+ if (errno == EINTR)
+ continue;
+@@ -460,23 +390,69 @@
+ return;
+ }
+
++ updatetbl = 1;
++ if (act & DO_BRUTE) {
++ firstchange = path_change(path, &bltin);
++ } else {
++ bltin = builtinloc;
++ firstchange = 9999;
++ }
++
+ /* If name is in the table, and not invalidated by cd, we're done */
+- if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
++ if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
++ if (cmdp->cmdtype == CMDFUNCTION) {
++ if (act & DO_NOFUN) {
++ updatetbl = 0;
++ } else {
++ goto success;
++ }
++ } else if (act & DO_BRUTE) {
++ if ((cmdp->cmdtype == CMDNORMAL &&
++ cmdp->param.index >= firstchange) ||
++ (cmdp->cmdtype == CMDBUILTIN &&
++ ((builtinloc < 0 && bltin >= 0) ?
++ bltin : builtinloc) >= firstchange)) {
++ /* need to recompute the entry */
++ } else {
++ goto success;
++ }
++ } else {
++ goto success;
++ }
++ }
++
++ bcmd = find_builtin(name);
++ regular = bcmd && bcmd->flags & BUILTIN_REGULAR;
++
++ if (regular) {
++ if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) {
+ goto success;
++ }
++ } else if (act & DO_BRUTE) {
++ if (firstchange == 0) {
++ updatetbl = 0;
++ }
++ }
+
+ /* If %builtin not in path, check for builtin next */
+- if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
++ if (regular || (bltin < 0 && bcmd)) {
++builtin:
++ if (!updatetbl) {
++ entry->cmdtype = CMDBUILTIN;
++ entry->u.cmd = bcmd;
++ return;
++ }
+ INTOFF;
+ cmdp = cmdlookup(name, 1);
+ cmdp->cmdtype = CMDBUILTIN;
+- cmdp->param.index = i;
++ cmdp->param.cmd = bcmd;
+ INTON;
+ goto success;
+ }
+
+ /* We have to search path. */
+ prev = -1; /* where to start */
+- if (cmdp) { /* doing a rehash */
++ if (cmdp && cmdp->rehash) { /* doing a rehash */
+ if (cmdp->cmdtype == CMDBUILTIN)
+ prev = builtinloc;
+ else
+@@ -489,30 +465,31 @@
+ while ((fullname = padvance(&path, name)) != NULL) {
+ stunalloc(fullname);
+ idx++;
++ if (idx >= firstchange) {
++ updatetbl = 0;
++ }
+ if (pathopt) {
+ if (prefix("builtin", pathopt)) {
+- if ((i = find_builtin(name)) < 0)
+- goto loop;
+- INTOFF;
+- cmdp = cmdlookup(name, 1);
+- cmdp->cmdtype = CMDBUILTIN;
+- cmdp->param.index = i;
+- INTON;
+- goto success;
+- } else if (prefix("func", pathopt)) {
++ if ((bcmd = find_builtin(name))) {
++ goto builtin;
++ }
++ continue;
++ } else if (!(act & DO_NOFUN) &&
++ prefix("func", pathopt)) {
+ /* handled below */
+ } else {
+- goto loop; /* ignore unimplemented options */
++ continue; /* ignore unimplemented options */
+ }
+ }
+ /* if rehash, don't redo absolute path names */
+- if (fullname[0] == '/' && idx <= prev) {
++ if (fullname[0] == '/' && idx <= prev &&
++ idx < firstchange) {
+ if (idx < prev)
+- goto loop;
++ continue;
+ TRACE(("searchexec \"%s\": no change\n", name));
+ goto success;
+ }
+- while (stat(fullname, &statb) < 0) {
++ while (stat64(fullname, &statb) < 0) {
+ #ifdef SYSV
+ if (errno == EINTR)
+ continue;
+@@ -523,7 +500,7 @@
+ }
+ e = EACCES; /* if we fail, this will be the error */
+ if (!S_ISREG(statb.st_mode))
+- goto loop;
++ continue;
+ if (pathopt) { /* this is a %func directory */
+ stalloc(strlen(fullname) + 1);
+ readcmdfile(fullname);
+@@ -545,6 +522,13 @@
+ }
+ #endif
+ TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
++ /* If we aren't called with DO_BRUTE and cmdp is set, it must
++ be a function and we're being called with DO_NOFUN */
++ if (!updatetbl) {
++ entry->cmdtype = CMDNORMAL;
++ entry->u.index = idx;
++ return;
++ }
+ INTOFF;
+ cmdp = cmdlookup(name, 1);
+ cmdp->cmdtype = CMDNORMAL;
+@@ -554,10 +538,10 @@
+ }
+
+ /* We failed. If there was an entry for this command, delete it */
+- if (cmdp)
++ if (cmdp && updatetbl)
+ delete_cmd_entry();
+ if (act & DO_ERR)
+- outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
++ warnx("%s: %s", name, errmsg(e, E_EXEC));
+ entry->cmdtype = CMDUNKNOWN;
+ return;
+
+@@ -573,17 +557,16 @@
+ * Search the table of builtin commands.
+ */
+
+-int
+-find_builtin(name)
+- char *name;
++struct builtincmd *
++find_builtin(const char *name)
+ {
+- const struct builtincmd *bp;
++ struct builtincmd *bp;
+
+- for (bp = builtincmd ; bp->name ; bp++) {
+- if (*bp->name == *name && equal(bp->name, name))
+- return bp->code;
+- }
+- return -1;
++ bp = bsearch(
++ &name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd),
++ pstrcmp
++ );
++ return bp;
+ }
+
+
+@@ -619,37 +602,12 @@
+ changepath(newval)
+ 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++;
+- }
++ firstchange = path_change(newval, &bltin);
+ if (builtinloc < 0 && bltin >= 0)
+ builtinloc = bltin; /* zap builtins */
+- if (builtinloc >= 0 && bltin < 0)
+- firstchange = 0;
+ clearcmdentry(firstchange);
+ builtinloc = bltin;
+ }
+@@ -687,41 +645,6 @@
+ }
+
+
+-/*
+- * Delete all functions.
+- */
+-
+-#ifdef mkinit
+-MKINIT void deletefuncs __P((void));
+-
+-SHELLPROC {
+- deletefuncs();
+-}
+-#endif
+-
+-void
+-deletefuncs() {
+- 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,
+@@ -780,6 +703,8 @@
+ INTOFF;
+ cmdp = *lastcmdentry;
+ *lastcmdentry = cmdp->next;
++ if (cmdp->cmdtype == CMDFUNCTION)
++ freefunc(cmdp->param.func);
+ ckfree(cmdp);
+ INTON;
+ }
+@@ -851,18 +776,14 @@
+ * Delete a function if it exists.
+ */
+
+-int
++void
+ unsetfunc(name)
+ char *name;
+ {
+ struct tblentry *cmdp;
+
+- if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
+- freefunc(cmdp->param.func);
++ if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION)
+ delete_cmd_entry();
+- return (0);
+- }
+- return (1);
+ }
+
+ /*
+@@ -874,74 +795,187 @@
+ int argc;
+ char **argv;
+ {
+- struct cmdentry entry;
+- struct tblentry *cmdp;
+- char * const *pp;
+- struct alias *ap;
+ int i;
+ int err = 0;
+
+ for (i = 1; i < argc; i++) {
+- out1str(argv[i]);
+- /* First look at the keywords */
+- for (pp = parsekwd; *pp; pp++)
+- if (**pp == *argv[i] && equal(*pp, argv[i]))
+- break;
++ err |= describe_command(out1, argv[i], 1);
++ }
++ return err;
++}
+
+- if (*pp) {
+- out1str(" is a shell keyword\n");
+- continue;
++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(argv[i], 1)) != NULL) {
+- out1fmt(" is an alias for %s\n", ap->val);
+- continue;
++ 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(argv[i], 0)) != NULL) {
++ if ((cmdp = cmdlookup(command, 0)) != NULL) {
+ entry.cmdtype = cmdp->cmdtype;
+ entry.u = cmdp->param;
+- }
+- else {
++ } else {
+ /* Finally use brute force */
+- find_command(argv[i], &entry, DO_ABS, pathval());
++ find_command(command, &entry, DO_ABS, path);
+ }
+
+ switch (entry.cmdtype) {
+ case CMDNORMAL: {
+- if (strchr(argv[i], '/') == NULL) {
+- const char *path = pathval();
+- char *name;
+ int j = entry.u.index;
++ char *p;
++ if (j == -1) {
++ p = command;
++ } else {
+ do {
+- name = padvance(&path, argv[i]);
+- stunalloc(name);
++ p = padvance(&path, command);
++ stunalloc(p);
+ } while (--j >= 0);
+- out1fmt(" is%s %s\n",
+- cmdp ? " a tracked alias for" : "", name);
++ }
++ if (verbose) {
++ outfmt(
++ out, " is%s %s",
++ cmdp ? " a tracked alias for" : nullstr, p
++ );
+ } else {
+- if (access(argv[i], X_OK) == 0)
+- out1fmt(" is %s\n", argv[i]);
+- else
+- out1fmt(": %s\n", strerror(errno));
++ outstr(p, out);
+ }
+ break;
+ }
++
+ case CMDFUNCTION:
+- out1str(" is a shell function\n");
++ if (verbose) {
++ outstr(" is a shell function", out);
++ } else {
++ outstr(command, out);
++ }
+ break;
+
+ case CMDBUILTIN:
+- out1str(" is a shell builtin\n");
++ if (verbose) {
++ outfmt(
++ out, " is a %sshell builtin",
++ entry.u.cmd->flags & BUILTIN_SPECIAL ?
++ "special " : nullstr
++ );
++ } else {
++ outstr(command, out);
++ }
+ break;
+
+ default:
+- out1str(": not found\n");
+- err |= 127;
++ 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;
++ int default_path = 0;
++ int verify_only = 0;
++ int verbose_verify_only = 0;
++
++ while ((c = nextopt("pvV")) != '\0')
++ switch (c) {
++ case 'p':
++ default_path = 1;
+ break;
++ case 'v':
++ verify_only = 1;
++ break;
++ case 'V':
++ verbose_verify_only = 1;
++ break;
++ default:
++ outfmt(out2,
++"command: nextopt returned character code 0%o\n", c);
++ return EX_SOFTWARE;
+ }
++
++ if (default_path + verify_only + verbose_verify_only > 1 ||
++ !*argptr) {
++ outfmt(out2,
++"command [-p] command [arg ...]\n");
++ outfmt(out2,
++"command {-v|-V} command\n");
++ return EX_USAGE;
+ }
+- return err;
++
++ if (verify_only || verbose_verify_only) {
++ return describe_command(out1, *argptr, verbose_verify_only);
++ }
++
++ return 0;
++}
++
++STATIC int
++path_change(newval, bltin)
++ const char *newval;
++ int *bltin;
++{
++ const char *old, *new;
++ int idx;
++ int firstchange;
++
++ 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)
++ firstchange = 0;
++ return firstchange;
+ }
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/exec.h bin_NetBSD-1.6release/src/bin/sh/exec.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/exec.h 2000-05-23 11:03:19.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/exec.h 2003-02-08 14:35:42.000000000 +0000
+@@ -49,13 +49,16 @@
+ int cmdtype;
+ union param {
+ int index;
+- union node *func;
++ struct funcnode *func;
++ const struct builtincmd *cmd;
+ } u;
+ };
+
+
+ #define DO_ERR 1 /* find_command prints errors */
+ #define DO_ABS 2 /* find_command checks absolute paths */
++#define DO_NOFUN 4 /* find_command ignores functions */
++#define DO_BRUTE 8 /* find_command ignores hash table */
+
+ extern const char *pathopt; /* set by padvance */
+ extern int exerrno; /* last exec error */
+@@ -65,12 +68,12 @@
+ char *padvance __P((const char **, const char *));
+ int hashcmd __P((int, char **));
+ void find_command __P((char *, struct cmdentry *, int, const char *));
+-int find_builtin __P((char *));
++struct builtincmd *find_builtin __P((const char *));
+ void hashcd __P((void));
+ void changepath __P((const char *));
+-void deletefuncs __P((void));
+ void getcmdentry __P((char *, struct cmdentry *));
+ void addcmdentry __P((char *, struct cmdentry *));
+ void defun __P((char *, union node *));
+-int unsetfunc __P((char *));
++void unsetfunc __P((char *));
+ int typecmd __P((int, char **));
++int commandcmd __P((int, char **));
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/expand.c bin_NetBSD-1.6release/src/bin/sh/expand.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/expand.c 2002-05-16 11:41:20.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/expand.c 2003-02-08 14:35:42.000000000 +0000
+@@ -54,6 +54,15 @@
+ #include <pwd.h>
+ #include <stdlib.h>
+ #include <stdio.h>
++#include <limits.h>
++#if defined(__GLIBC__)
++#if !defined(FNMATCH_BROKEN)
++#include <fnmatch.h>
++#if !defined(GLOB_BROKEN)
++#include <glob.h>
++#endif
++#endif
++#endif
+
+ /*
+ * Routines to expand arguments to commands. We have to deal with
+@@ -78,6 +87,15 @@
+ #include "show.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 */
++
++/*
+ * Structure specifying which parts of the string should be searched
+ * for IFS characters.
+ */
+@@ -89,34 +107,84 @@
+ 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 */
++/* 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 __P((char *, int));
+ STATIC char *exptilde __P((char *, int));
+ STATIC void expbackq __P((union node *, int, int));
+-STATIC int subevalvar __P((char *, char *, int, int, int, int));
++STATIC const char *subevalvar __P((char *, char *, int, int, int, int, int));
+ STATIC char *evalvar __P((char *, int));
+ STATIC int varisset __P((char *, int));
++STATIC void strtodest __P((const char *, const char *, int));
++STATIC void memtodest __P((const char *, size_t, const char *, int));
+ STATIC void varvalue __P((char *, int, int));
+ STATIC void recordregion __P((int, int, int));
+ STATIC void removerecordregions __P((int));
+ STATIC void ifsbreakup __P((char *, struct arglist *));
+ STATIC void ifsfree __P((void));
+ STATIC void expandmeta __P((struct strlist *, int));
++#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
++STATIC void addglob __P((const glob_t *));
++#else
+ STATIC void expmeta __P((char *, char *));
++#endif
+ STATIC void addfname __P((char *));
++#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+ STATIC struct strlist *expsort __P((struct strlist *));
+ STATIC struct strlist *msort __P((struct strlist *, int));
+-STATIC int pmatch __P((char *, char *, int));
+-STATIC char *cvtnum __P((int, char *));
++#endif
++STATIC int patmatch __P((char *, const char *));
++#if !defined(__GLIBC__) || defined(FNMATCH_BROKEN)
++STATIC int pmatch __P((const char *, const char *));
++#else
++#define pmatch(a, b) !fnmatch((a), (b), 0)
++#endif
++STATIC int cvtnum __P((long));
++STATIC size_t esclen __P((const char *, const char *));
++STATIC char *scanleft __P((char *, char *, char *, char *, int, int));
++STATIC char *scanright __P((char *, char *, char *, char *, int, int));
++static void varunset(const char *, const char *, const char *, int)
++ __attribute__((__noreturn__));
+
+ extern int oexitstatus;
+
++
++/*
++ * 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;
++}
++
++
+ /*
+ * Expand shell variables and backquotes inside a here document.
+ */
+@@ -196,64 +264,126 @@
+ char *p;
+ int flag;
+ {
+- char c;
++ static const char spclchars[] = {
++ '=',
++ ':',
++ CTLQUOTEMARK,
++ CTLENDVAR,
++ CTLESC,
++ CTLVAR,
++ CTLBACKQ,
++ CTLBACKQ | CTLQUOTE,
++ CTLENDARI,
++ 0
++ };
++ const char *reject = spclchars;
++ int c;
+ int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
+- int firsteq = 1;
++ int breakall = flag & EXP_WORD;
++ int inquotes;
++ size_t length;
++ int startloc;
+
+- if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
++ if (!(flag & EXP_VARTILDE)) {
++ reject += 2;
++ } else if (flag & EXP_VARTILDE2) {
++ reject++;
++ }
++ inquotes = 0;
++ length = 0;
++ if (flag & EXP_TILDE) {
++ flag &= ~EXP_TILDE;
++tilde:
+ p = exptilde(p, flag);
++ }
++start:
++ startloc = expdest - stackblock();
+ for (;;) {
+- switch (c = *p++) {
++ 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 - 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 (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
+- break;
+- if ((flag & EXP_FULL) != 0)
+- STPUTC(c, expdest);
++ 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:
+- if (quotes)
+- STPUTC(c, expdest);
+- c = *p++;
+- STPUTC(c, expdest);
+- break;
++ startloc++;
++ length++;
++ goto addquote;
+ case CTLVAR:
+ p = evalvar(p, flag);
+- break;
++ goto start;
+ case CTLBACKQ:
++ c = 0;
+ case CTLBACKQ|CTLQUOTE:
+- expbackq(argbackq->n, c & CTLQUOTE, flag);
++ expbackq(argbackq->n, c, quotes);
+ argbackq = argbackq->next;
+- break;
++ goto start;
+ 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);
++ p--;
++ expari(quotes);
++ goto start;
+ }
+ }
+-breakloop:;
+- return;
++breakloop:
++ ;
+ }
+
+ STATIC char *
+@@ -262,11 +392,21 @@
+ int flag;
+ {
+ char c, *startp = p;
++ char *name;
+ struct passwd *pw;
+ const char *home;
+ int quotes = flag & (EXP_FULL | EXP_CASE);
++ int startloc;
+
+- while ((c = *p) != '\0') {
++ if (*p == CTLESC && (flag & EXP_QWORD)) {
++ p++;
++ }
++ if (*p != '~') {
++ return startp;
++ }
++ name = p + 1;
++
++ while ((c = *++p) != '\0') {
+ switch(c) {
+ case CTLESC:
+ return (startp);
+@@ -277,28 +417,26 @@
+ goto done;
+ break;
+ case '/':
++ case CTLENDVAR:
+ goto done;
+ }
+- p++;
+ }
+ done:
+ *p = '\0';
+- if (*(startp+1) == '\0') {
+- if ((home = lookupvar("HOME")) == NULL)
++ if (*name == '\0') {
++ if ((home = lookupvar(homestr)) == NULL)
+ goto lose;
+ } else {
+- if ((pw = getpwnam(startp+1)) == NULL)
++ if ((pw = getpwnam(name)) == NULL)
+ goto lose;
+ home = pw->pw_dir;
+ }
+ if (*home == '\0')
+ goto lose;
+ *p = c;
+- while ((c = *home++) != '\0') {
+- if (quotes && SQSYNTAX[(int)c] == CCTL)
+- STPUTC(CTLESC, expdest);
+- STPUTC(c, expdest);
+- }
++ startloc = expdest - stackblock();
++ strtodest(home, SQSYNTAX, quotes);
++ recordregion(startloc, expdest - stackblock(), 0);
+ return (p);
+ lose:
+ *p = c;
+@@ -352,61 +490,60 @@
+ * evaluate, place result in (backed up) result, adjust string position.
+ */
+ void
+-expari(flag)
+- int flag;
++expari(quotes)
++ int quotes;
+ {
+ char *p, *start;
+- int result;
+ int begoff;
+- int quotes = flag & (EXP_FULL | EXP_CASE);
+- int quoted;
++ int flag;
++ int len;
+
+ /* 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.
++ * efficiency. Next we scan backwards looking for the
++ * start of arithmetic.
+ */
+-#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)
++ *p = '\0';
++ p--;
++ do {
++ int esc;
++
++ while (*p != CTLARI) {
++ p--;
++#ifdef DEBUG
++ if (p < start) {
+ error("missing CTLARI (shouldn't happen)");
+- if (p > start && *(p-1) == CTLESC)
+- for (p = start; *p != CTLARI; p++)
+- if (*p == CTLESC)
+- p++;
++ }
++#endif
++ }
++
++ esc = esclen(start, p);
++ if (!(esc % 2)) {
++ break;
++ }
++
++ p -= esc + 1;
++ } while (1);
+
+- if (p[1] == '"')
+- quoted=1;
+- else
+- quoted=0;
+ begoff = p - start;
++
+ removerecordregions(begoff);
++
++ flag = p[1];
++
++ expdest = p;
++
+ if (quotes)
+- rmescapes(p+2);
+- result = arith(p+2);
+- fmtstr(p, 12, "%d", result);
++ rmescapes(p + 2);
+
+- while (*p++)
+- ;
++ len = cvtnum(arith(p + 2));
+
+- if (quoted == 0)
+- recordregion(begoff, p - 1 - start, 0);
+- result = expdest - p + 1;
+- STADJUST(-result, expdest);
++ if (flag != '"')
++ recordregion(begoff, begoff + len, 0);
+ }
+
+
+@@ -415,42 +552,47 @@
+ */
+
+ STATIC void
+-expbackq(cmd, quoted, flag)
++expbackq(cmd, quoted, quotes)
+ union node *cmd;
+ int quoted;
+- int flag;
++ int quotes;
+ {
+ struct backcmd in;
+ int i;
+ char buf[128];
+ char *p;
+ char *dest = expdest;
+- struct ifsregion saveifs, *savelastp;
++ struct ifsregion saveifs;
++ struct ifsregion *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;
++ in.fd = -1;
++ in.buf = 0;
++ in.jp = 0;
++
+ saveifs = ifsfirst;
+ savelastp = ifslastp;
+ saveargbackq = argbackq;
+ saveherefd = herefd;
+ herefd = -1;
+- p = grabstackstr(dest);
+- evalbackcmd(cmd, &in);
+- ungrabstackstr(p, dest);
++
++ INTOFF;
++ evalbackcmd(cmd, (struct backcmd *) &in);
+ ifsfirst = saveifs;
+ ifslastp = savelastp;
+ argbackq = saveargbackq;
+ herefd = saveherefd;
+
+ p = in.buf;
+- lastc = '\0';
++ i = in.nleft;
++ if (i == 0)
++ goto read;
+ for (;;) {
+- if (--in.nleft < 0) {
++ memtodest(p, i, syntax, quotes);
++read:
+ if (in.fd < 0)
+ break;
+ while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
+@@ -458,18 +600,12 @@
+ 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);
+- }
+ }
+
++ dest = expdest;
++
+ /* Eat all trailing newlines */
+- for (p--; lastc == '\n'; lastc = *--p)
++ for (; dest > stackblock() && dest[-1] == '\n';)
+ STUNPUTC(dest);
+
+ if (in.fd >= 0)
+@@ -478,6 +614,7 @@
+ ckfree(in.buf);
+ if (in.jp)
+ exitstatus = waitforjob(in.jp);
++ INTON;
+ if (quoted == 0)
+ recordregion(startloc, dest - stackblock(), 0);
+ TRACE(("evalbackq: size=%d: \"%.*s\"\n",
+@@ -485,131 +622,149 @@
+ (dest - stackblock()) - startloc,
+ stackblock() + startloc));
+ expdest = dest;
+- INTON;
+ }
+
+
++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;
+
+-STATIC int
+-subevalvar(p, str, strloc, subtype, startloc, varflags)
++ 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(p, str, strloc, subtype, startloc, varflags, quotes)
+ char *p;
+ char *str;
+ int strloc;
+ int subtype;
+ int startloc;
+ int varflags;
++ int quotes;
+ {
+ char *startp;
+- char *loc = NULL;
+- char *q;
+- int c = 0;
++ 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, 0);
+- STACKSTRNUL(expdest);
++ argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
++ STPUTC('\0', 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;
++ return startp;
+
+ 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);
++ varunset(p, str, startp, varflags);
+ /* 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;
++ subtype -= VSTRIMRIGHT;
++#ifdef DEBUG
++ if (subtype < 0 || subtype > 3)
++ abort();
++#endif
+
+- 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--;
++ rmesc = startp;
++ rmescend = stackblock() + strloc;
++ if (quotes) {
++ rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
++ if (rmesc != startp) {
++ rmescend = expdest;
++ startp = stackblock() + startloc;
+ }
+ }
+- return 0;
++ rmescend--;
++ str = stackblock() + strloc;
++ preglob(str, varflags & VSQUOTE, 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;
++ /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
++ zero = subtype >> 1;
++ /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
++ scan = (subtype & 1) ^ zero ? scanleft : scanright;
+
+- default:
+- abort();
++ loc = scan(startp, rmesc, rmescend, str, quotes, zero);
++ if (loc) {
++ if (zero) {
++ memmove(startp, loc, str - loc);
++ loc = startp + (str - loc) - 1;
+ }
+-
+-recordleft:
+- *loc = c;
+- amount = ((str - 1) - (loc - startp)) - expdest;
+- STADJUST(amount, expdest);
+- while (loc != str - 1)
+- *startp++ = *loc++;
+- return 1;
+-
+-recordright:
++ *loc = '\0';
+ amount = loc - expdest;
+ STADJUST(amount, expdest);
+- STPUTC('\0', expdest);
+- STADJUST(-1, expdest);
+- return 1;
++ }
++ return loc;
+ }
+
+
+@@ -617,120 +772,129 @@
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
+ */
+-
+ STATIC char *
+ evalvar(p, flag)
+ char *p;
+- int flag;
++ const int flag;
+ {
+ int subtype;
+ int varflags;
+ char *var;
+- char *val;
+ int patloc;
+ int c;
+ int set;
+- int special;
+ int startloc;
+- int varlen;
++ size_t varlen;
+ int easy;
+- int quotes = flag & (EXP_FULL | EXP_CASE);
++ int quotes;
++ int quoted;
+
++ quotes = flag & (EXP_FULL | EXP_CASE);
+ varflags = *p++;
+ subtype = varflags & VSTYPE;
++ quoted = varflags & VSQUOTE;
+ var = p;
+- special = 0;
+- if (! is_name(*p))
+- special = 1;
+- 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;
+- }
++ easy = (!quoted || (*var == '@' && shellparam.nparam));
+ 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, flag & EXP_FULL);
++ p = strchr(p, '=') + 1;
++
++ if (!is_name(*var)) {
++ set = varisset(var, varflags & VSNUL);
++ set--;
++ if (subtype == VSPLUS)
++ goto vsplus;
++ if (++set) {
++ varvalue(var, quoted, flag);
+ if (subtype == VSLENGTH) {
+ varlen = expdest - stackblock() - startloc;
+ STADJUST(-varlen, expdest);
++ goto vslen;
++ }
+ }
+ } else {
+- char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
+- : BASESYNTAX;
++ const char *val;
++again:
++ /* jump here after setting a variable with ${var=text} */
++ val = lookupvar(var);
++ set = !val || ((varflags & VSNUL) && !*val);
++ if (subtype == VSPLUS)
++ goto vsplus;
++ if (--set) {
++ varlen = strlen(val);
++ if (subtype == VSLENGTH)
++ goto vslen;
++ memtodest(
++ val, varlen, quoted ? DQSYNTAX : BASESYNTAX,
++ quotes
++ );
++ }
++ }
+
+- if (subtype == VSLENGTH) {
+- for (;*val; val++)
+- varlen++;
++
++ if (subtype == VSMINUS) {
++vsplus:
++ if (!set) {
++ argstr(
++ p, flag | EXP_TILDE |
++ (quoted ? EXP_QWORD : EXP_WORD)
++ );
++ goto end;
+ }
+- else {
+- while (*val) {
+- if (quotes && syntax[(int)*val] == CCTL)
+- STPUTC(CTLESC, expdest);
+- STPUTC(*val++, expdest);
++ if (easy)
++ goto record;
++ goto end;
+ }
+
++ if (subtype == VSASSIGN || subtype == VSQUESTION) {
++ if (!set) {
++ 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 (subtype == VSPLUS)
+- set = ! set;
+-
+- easy = ((varflags & VSQUOTE) == 0 ||
+- (*var == '@' && shellparam.nparam != 1));
+-
++ if (!set && uflag)
++ varunset(p, var, 0, 0);
+
+- switch (subtype) {
+- case VSLENGTH:
+- expdest = cvtnum(varlen, expdest);
++ if (subtype == VSLENGTH) {
++vslen:
++ cvtnum(varlen);
+ goto record;
++ }
+
+- case VSNORMAL:
++ if (subtype == VSNORMAL) {
+ if (!easy)
+- break;
++ goto end;
+ record:
+- recordregion(startloc, expdest - stackblock(),
+- varflags & VSQUOTE);
+- break;
+-
+- case VSPLUS:
+- case VSMINUS:
+- if (!set) {
+- argstr(p, flag);
+- break;
++ recordregion(startloc, expdest - stackblock(), quoted);
++ goto end;
+ }
+- if (easy)
+- goto record;
+- break;
+
++#ifdef DEBUG
++ switch (subtype) {
+ case VSTRIMLEFT:
+ case VSTRIMLEFTMAX:
+ case VSTRIMRIGHT:
+ case VSTRIMRIGHTMAX:
+- if (!set)
+ break;
++ default:
++ abort();
++ }
++#endif
++
++ if (set) {
+ /*
+ * Terminate the string and start recording the pattern
+ * right after it
+@@ -738,37 +902,16 @@
+ STPUTC('\0', expdest);
+ patloc = expdest - stackblock();
+ if (subevalvar(p, NULL, patloc, subtype,
+- startloc, varflags) == 0) {
+- int amount = (expdest - stackblock() - patloc) + 1;
++ startloc, varflags, quotes) == 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();
+ }
+
++end:
+ if (subtype != VSNORMAL) { /* skip to end of alternative */
+ int nesting = 1;
+ for (;;) {
+@@ -801,7 +944,7 @@
+ int nulok;
+ {
+ if (*name == '!')
+- return backgndpid != -1;
++ return backgndpid != 0;
+ else if (*name == '@' || *name == '*') {
+ if (*shellparam.p == NULL)
+ return 0;
+@@ -835,37 +978,60 @@
+
+
+ /*
++ * Put a string on the stack.
++ */
++
++STATIC void
++memtodest(const char *p, size_t len, const char *syntax, int quotes) {
++ char *q = expdest;
++
++ q = makestrspace(len * 2, q);
++
++ while (len--) {
++ int c = *p++;
++ if (!c)
++ continue;
++ if (quotes && (syntax[c] == CCTL || syntax[c] == CBACK))
++ USTPUTC(CTLESC, q);
++ USTPUTC(c, q);
++ }
++
++ expdest = q;
++}
++
++
++STATIC void
++strtodest(p, syntax, quotes)
++ const char *p;
++ const char *syntax;
++ int quotes;
++{
++ memtodest(p, strlen(p), syntax, quotes);
++}
++
++
++
++/*
+ * Add the value of a specialized variable to the stack string.
+ */
+
+ STATIC void
+-varvalue(name, quoted, allow_split)
++varvalue(name, quoted, flags)
+ char *name;
+ int quoted;
+- int allow_split;
++ int flags;
+ {
+ int num;
+ char *p;
+ int i;
+- char sep;
++ int sep;
++ int sepq = 0;
+ char **ap;
+ char const *syntax;
++ int allow_split = flags & EXP_FULL;
++ int quotes = flags & (EXP_FULL | EXP_CASE);
+
+-#define STRTODEST(p) \
+- do {\
+- if (allow_split) { \
+- 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)
+-
+-
++ syntax = quoted ? DQSYNTAX : BASESYNTAX;
+ switch (*name) {
+ case '$':
+ num = rootpid;
+@@ -879,46 +1045,44 @@
+ case '!':
+ num = backgndpid;
+ numvar:
+- expdest = cvtnum(num, expdest);
++ cvtnum(num);
+ break;
+ case '-':
+ for (i = 0 ; i < NOPTS ; i++) {
+- if (optlist[i].val)
+- STPUTC(optlist[i].letter, expdest);
++ if (optlist[i])
++ STPUTC(optletters[i], expdest);
+ }
+ break;
+ case '@':
+ if (allow_split && quoted) {
+- for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
+- STRTODEST(p);
+- if (*ap)
+- STPUTC('\0', expdest);
+- }
+- break;
++ sep = 1 << CHAR_BIT;
++ goto param;
+ }
+ /* fall through */
+ case '*':
+- if (ifsset() != 0)
+- sep = ifsval()[0];
+- else
+- sep = ' ';
++ sep = ifsset() ? ifsval()[0] : ' ';
++ if (quotes) {
++ sepq = (syntax[sep] == CCTL) || (syntax[sep] == CBACK);
++ }
++param:
+ for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
+- STRTODEST(p);
+- if (*ap && sep)
+- STPUTC(sep, expdest);
++ strtodest(p, syntax, quotes);
++ if (*ap && sep) {
++ p = expdest;
++ if (sepq)
++ STPUTC(CTLESC, p);
++ STPUTC(sep, p);
++ expdest = p;
++ }
+ }
+ break;
+ case '0':
+- p = arg0;
+- STRTODEST(p);
++ strtodest(arg0, syntax, quotes);
+ break;
+ default:
+- if (is_digit(*name)) {
+ num = atoi(name);
+ if (num > 0 && num <= shellparam.nparam) {
+- p = shellparam.p[num - 1];
+- STRTODEST(p);
+- }
++ strtodest(shellparam.p[num - 1], syntax, quotes);
+ }
+ break;
+ }
+@@ -942,11 +1106,13 @@
+ if (ifslastp == NULL) {
+ ifsp = &ifsfirst;
+ } else {
++ INTOFF;
+ ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
++ ifsp->next = NULL;
+ ifslastp->next = ifsp;
++ INTON;
+ }
+ ifslastp = ifsp;
+- ifslastp->next = NULL;
+ ifslastp->begoff = start;
+ ifslastp->endoff = end;
+ ifslastp->nulonly = nulonly;
+@@ -969,7 +1135,7 @@
+ char *start;
+ char *p;
+ char *q;
+- const char *ifs;
++ const char *ifs, *realifs;
+ int ifsspc;
+ int nulonly;
+
+@@ -977,13 +1143,13 @@
+ start = string;
+ ifsspc = 0;
+ nulonly = 0;
++ realifs = ifsset() ? ifsval() : defifs;
+ if (ifslastp != NULL) {
+ ifsp = &ifsfirst;
+ do {
+ p = string + ifsp->begoff;
+ nulonly = ifsp->nulonly;
+- ifs = nulonly ? nullstr :
+- ( ifsset() ? ifsval() : " \t\n" );
++ ifs = nulonly ? nullstr : realifs;
+ ifsspc = 0;
+ while (p < string + ifsp->endoff) {
+ q = p;
+@@ -991,7 +1157,7 @@
+ p++;
+ if (strchr(ifs, *p)) {
+ if (!nulonly)
+- ifsspc = (strchr(" \t\n", *p) != NULL);
++ ifsspc = (strchr(defifs, *p) != NULL);
+ /* Ignore IFS whitespace at start */
+ if (q == start && ifsspc) {
+ p++;
+@@ -1015,7 +1181,7 @@
+ if (strchr(ifs, *p) == NULL ) {
+ p = q;
+ break;
+- } else if (strchr(" \t\n",*p) == NULL) {
++ } else if (strchr(defifs, *p) == NULL) {
+ if (ifsspc) {
+ p++;
+ ifsspc = 0;
+@@ -1032,19 +1198,18 @@
+ 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;
++ if (nulonly)
++ goto add;
+ }
+- } else {
++
++ if (!*start)
++ return;
++
++add:
+ sp = (struct strlist *)stalloc(sizeof *sp);
+ sp->text = start;
+ *arglist->lastp = sp;
+ arglist->lastp = &sp->next;
+- }
+ }
+
+ STATIC void
+@@ -1069,7 +1234,69 @@
+ * should be escapes. The results are stored in the list exparg.
+ */
+
+-char *expdir;
++#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 */
++ 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
+@@ -1077,32 +1304,33 @@
+ struct strlist *str;
+ int flag;
+ {
+- char *p;
+- struct strlist **savelastp;
+- struct strlist *sp;
+- char c;
++ static const char metachars[] = {
++ '*', '?', '[', 0
++ };
+ /* TODO - EXP_REDIR */
+
+ while (str) {
++ struct strlist **savelastp;
++ struct strlist *sp;
++ char *p;
++
+ if (fflag)
+ goto nometa;
+- p = str->text;
+- for (;;) { /* fast check for meta chars */
+- if ((c = *p++) == '\0')
++ if (!strpbrk(str->text, metachars))
+ goto nometa;
+- if (c == '*' || c == '?' || c == '[' || c == '!')
+- break;
+- }
+ savelastp = exparg.lastp;
++
+ INTOFF;
+- if (expdir == NULL) {
++ p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
++ {
+ int i = strlen(str->text);
+ expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
+ }
+
+- expmeta(expdir, str->text);
++ expmeta(expdir, p);
+ ckfree(expdir);
+- expdir = NULL;
++ if (p != str->text)
++ ckfree(p);
+ INTON;
+ if (exparg.lastp == savelastp) {
+ /*
+@@ -1135,11 +1363,10 @@
+ {
+ char *p;
+ const char *cp;
+- char *q;
+ char *start;
+ char *endname;
+ int metaflag;
+- struct stat statb;
++ struct stat64 statb;
+ DIR *dirp;
+ struct dirent *dp;
+ int atend;
+@@ -1147,17 +1374,15 @@
+
+ metaflag = 0;
+ start = name;
+- for (p = name ; ; p++) {
++ for (p = name; *p; p++) {
+ if (*p == '*' || *p == '?')
+ metaflag = 1;
+ else if (*p == '[') {
+- q = p + 1;
++ char *q = p + 1;
+ if (*q == '!')
+ q++;
+ for (;;) {
+- while (*q == CTLQUOTEMARK)
+- q++;
+- if (*q == CTLESC)
++ if (*q == '\\')
+ q++;
+ if (*q == '/' || *q == '\0')
+ break;
+@@ -1166,46 +1391,36 @@
+ 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)
++ } else if (*p == '\\')
+ p++;
+- if (*p == '/') {
++ else if (*p == '/') {
+ if (metaflag)
+- break;
++ goto out;
+ start = p + 1;
+ }
+ }
++out:
+ 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 = name;
++ do {
++ if (*p == '\\')
+ p++;
+ *enddir++ = *p;
+- if (*p == '\0')
+- break;
+- }
+- if (metaflag == 0 || lstat(expdir, &statb) >= 0)
++ } while (*p++);
++ if (metaflag == 0 || lstat64(expdir, &statb) >= 0)
+ addfname(expdir);
+ return;
+ }
+ endname = p;
+- if (start != name) {
++ if (name < start) {
+ p = name;
+- while (p < start) {
+- while (*p == CTLQUOTEMARK)
+- p++;
+- if (*p == CTLESC)
++ do {
++ if (*p == '\\')
+ p++;
+ *enddir++ = *p++;
+- }
++ } while (p < start);
+ }
+ if (enddir == expdir) {
+ cp = ".";
+@@ -1227,16 +1442,14 @@
+ }
+ matchdot = 0;
+ p = start;
+- while (*p == CTLQUOTEMARK)
+- p++;
+- if (*p == CTLESC)
++ if (*p == '\\')
+ 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 (pmatch(start, dp->d_name)) {
+ if (atend) {
+ scopy(dp->d_name, enddir);
+ addfname(expdir);
+@@ -1253,6 +1466,7 @@
+ if (! atend)
+ endname[-1] = '/';
+ }
++#endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
+
+
+ /*
+@@ -1263,18 +1477,16 @@
+ addfname(name)
+ char *name;
+ {
+- char *p;
+ struct strlist *sp;
+
+- p = stalloc(strlen(name) + 1);
+- scopy(name, p);
+ sp = (struct strlist *)stalloc(sizeof *sp);
+- sp->text = p;
++ 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
+@@ -1336,35 +1548,29 @@
+ }
+ return list;
+ }
+-
++#endif
+
+
+ /*
+ * Returns true if the pattern matches the string.
+ */
+
+-int
+-patmatch(pattern, string, squoted)
++STATIC inline int
++patmatch(pattern, string)
+ char *pattern;
+- char *string;
+- int squoted; /* string might have quote chars */
++ const char *string;
+ {
+-#ifdef notdef
+- if (pattern[0] == '!' && pattern[1] == '!')
+- return 1 - pmatch(pattern + 2, string);
+- else
+-#endif
+- return pmatch(pattern, string, squoted);
++ return pmatch(preglob(pattern, 0, 0), string);
+ }
+
+
++#if !defined(__GLIBC__) || defined(FNMATCH_BROKEN)
+ STATIC int
+-pmatch(pattern, string, squoted)
+- char *pattern;
+- char *string;
+- int squoted;
++pmatch(pattern, string)
++ const char *pattern;
++ const char *string;
+ {
+- char *p, *q;
++ const char *p, *q;
+ char c;
+
+ p = pattern;
+@@ -1373,46 +1579,33 @@
+ 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 (*p) {
++ c = *p++;
++ }
++ goto dft;
+ case '?':
+- if (squoted && *q == CTLESC)
+- q++;
+ if (*q++ == '\0')
+ return 0;
+ break;
+ case '*':
+ c = *p;
+- while (c == CTLQUOTEMARK || c == '*')
++ while (c == '*')
+ c = *++p;
+- if (c != CTLESC && c != CTLQUOTEMARK &&
+- c != '?' && c != '*' && c != '[') {
++ if (c != '\\' && 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))
++ if (pmatch(p, q))
+ return 1;
+- if (squoted && *q == CTLESC)
+- q++;
+ } while (*q++ != '\0');
+ return 0;
+ case '[': {
+- char *endp;
++ const char *endp;
+ int invert, found;
+ char chr;
+
+@@ -1420,11 +1613,9 @@
+ if (*endp == '!')
+ endp++;
+ for (;;) {
+- while (*endp == CTLQUOTEMARK)
+- endp++;
+ if (*endp == '\0')
+ goto dft; /* no matching ] */
+- if (*endp == CTLESC)
++ if (*endp == '\\')
+ endp++;
+ if (*++endp == ']')
+ break;
+@@ -1436,21 +1627,15 @@
+ }
+ 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)
++ if (c == '\\')
+ c = *p++;
+ if (*p == '-' && p[1] != ']') {
+ p++;
+- while (*p == CTLQUOTEMARK)
+- p++;
+- if (*p == CTLESC)
++ if (*p == '\\')
+ p++;
+ if (chr >= c && chr <= *p)
+ found = 1;
+@@ -1465,8 +1650,6 @@
+ break;
+ }
+ dft: default:
+- if (squoted && *q == CTLESC)
+- q++;
+ if (*q++ != c)
+ return 0;
+ break;
+@@ -1477,6 +1660,7 @@
+ return 0;
+ return 1;
+ }
++#endif
+
+
+
+@@ -1484,28 +1668,75 @@
+ * Remove any CTLESC characters from a string.
+ */
+
+-void
+-rmescapes(str)
++char *
++_rmescapes(str, flag)
+ char *str;
++ int flag;
+ {
+- char *p, *q;
++ char *p, *q, *r;
++ static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
++ unsigned inquotes;
++ int notescaped;
++ int globbing;
+
+- p = str;
+- while (*p != CTLESC && *p != CTLQUOTEMARK) {
+- if (*p++ == '\0')
+- return;
++ 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) {
++#ifdef _GNU_SOURCE
++ q = mempcpy(q, str, len);
++#else
++ memcpy(q, str, len);
++ q += len;
++#endif
++ }
++ }
++ 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 == CTLESC)
++ 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;
+ }
+
+
+@@ -1521,16 +1752,14 @@
+ {
+ 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);
++ STACKSTRNUL(expdest);
++ result = patmatch(stackblock(), val);
+ popstackmark(&smark);
+ return result;
+ }
+@@ -1539,25 +1768,30 @@
+ * Our own itoa().
+ */
+
+-STATIC char *
+-cvtnum(num, buf)
+- int num;
+- char *buf;
+- {
+- char temp[32];
+- int neg = num < 0;
+- char *p = temp + 31;
+-
+- temp[31] = '\0';
++STATIC int
++cvtnum(long num) {
++ int len;
+
+- do {
+- *--p = num % 10 + '0';
+- } while ((num /= 10) != 0);
++ expdest = makestrspace(32, expdest);
++ len = fmtstr(expdest, 32, "%ld", num);
++ STADJUST(len, expdest);
++ return len;
++}
+
+- if (neg)
+- *--p = '-';
++static void
++varunset(const char *end, const char *var, const char *umsg, int varflags)
++{
++ const char *msg;
++ const char *tail;
+
+- while (*p)
+- STPUTC(*p++, buf);
+- return buf;
++ tail = nullstr;
++ msg = "parameter not set";
++ if (umsg) {
++ if (*end == CTLENDVAR) {
++ if (varflags & VSNUL)
++ tail = " or null";
++ } else
++ msg = umsg;
++ }
++ error("%.*s: %s%s", end - var - 1, var, msg, tail);
+ }
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/expand.h bin_NetBSD-1.6release/src/bin/sh/expand.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/expand.h 1999-07-09 12:02:06.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/expand.h 2003-02-08 14:35:42.000000000 +0000
+@@ -58,18 +58,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 __P((union node *, int));
+ void expandarg __P((union node *, struct arglist *, int));
+ void expari __P((int));
+-int patmatch __P((char *, char *, int));
+-void rmescapes __P((char *));
++#define rmescapes(p) _rmescapes((p), 0)
++char *_rmescapes __P((char *, int));
+ int casematch __P((union node *, char *));
+
+ /* From arith.y */
+ int arith __P((const char *));
+ int expcmd __P((int , char **));
++#ifdef USE_LEX
+ void arith_lex_reset __P((void));
++#else
++#define arith_lex_reset()
++#endif
+ int yylex __P((void));
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/hetio.c bin_NetBSD-1.6release/src/bin/sh/hetio.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/hetio.c 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/hetio.c 2003-02-08 14:35:42.000000000 +0000
+@@ -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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/hetio.h bin_NetBSD-1.6release/src/bin/sh/hetio.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/hetio.h 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/hetio.h 2003-02-08 14:35:42.000000000 +0000
+@@ -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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/histedit.c bin_NetBSD-1.6release/src/bin/sh/histedit.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/histedit.c 2002-03-12 11:22:09.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/histedit.c 2003-02-08 14:35:42.000000000 +0000
+@@ -60,9 +60,9 @@
+ #include "main.h"
+ #include "output.h"
+ #include "mystring.h"
+-#include "myhistedit.h"
+ #include "error.h"
+ #ifndef SMALL
++#include "myhistedit.h"
+ #include "eval.h"
+ #include "memalloc.h"
+
+@@ -219,7 +219,11 @@
+ if (argc == 1)
+ error("missing history argument");
+
++#ifdef __GLIBC__
++ optind = 0;
++#else
+ optreset = 1; optind = 1; /* initialize getopt */
++#endif
+ while (not_fcnumber(argv[optind]) &&
+ (ch = getopt(argc, argv, ":e:lnrs")) != -1)
+ switch ((char)ch) {
+@@ -277,8 +281,8 @@
+ */
+ 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 */
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/input.c bin_NetBSD-1.6release/src/bin/sh/input.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/input.c 2001-02-05 11:15:30.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/input.c 2003-02-08 14:35:42.000000000 +0000
+@@ -66,17 +66,24 @@
+ #include "error.h"
+ #include "alias.h"
+ #include "parser.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 */
+ };
+
+ /*
+@@ -103,12 +110,13 @@
+ 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 */
+
++#ifndef SMALL
+ EditLine *el; /* cookie for editline package */
++#endif
+
+ STATIC void pushfile __P((void));
+ static int preadfd __P((void));
+@@ -123,14 +131,9 @@
+ }
+
+ RESET {
+- if (exception != EXSHELLPROC)
+ parselleft = parsenleft = 0; /* clear input buffer */
+ popallfiles();
+ }
+-
+-SHELLPROC {
+- popallfiles();
+-}
+ #endif
+
+
+@@ -148,7 +151,7 @@
+ int c;
+
+ while (--nleft > 0) {
+- c = pgetc_macro();
++ c = pgetc2();
+ if (c == PEOF) {
+ if (p == line)
+ return NULL;
+@@ -163,7 +166,6 @@
+ }
+
+
+-
+ /*
+ * Read a character from the script, returning PEOF on end of file.
+ * Nul characters in the input are silently discarded.
+@@ -176,6 +178,21 @@
+ }
+
+
++/*
++ * Same as pgetc(), but ignores PEOA.
++ */
++
++int
++pgetc2()
++{
++ int c;
++ do {
++ c = pgetc_macro();
++ } while (c == PEOA);
++ return c;
++}
++
++
+ static int
+ preadfd()
+ {
+@@ -197,10 +214,14 @@
+ }
+ } else
+ #endif
+- nr = read(parsefile->fd, buf, BUFSIZ - 1);
++
++#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;
+@@ -215,8 +236,6 @@
+ }
+ }
+ }
+- nr = -1;
+- }
+ return nr;
+ }
+
+@@ -235,10 +254,18 @@
+ {
+ char *p, *q;
+ int more;
++#ifndef SMALL
+ int something;
++#endif
+ char savec;
+
+- if (parsefile->strpush) {
++ while (parsefile->strpush) {
++ if (
++ parsenleft == -1 && parsefile->strpush->ap &&
++ parsenextc[-1] != ' ' && parsenextc[-1] != '\t'
++ ) {
++ return PEOA;
++ }
+ popstring();
+ if (--parsenleft >= 0)
+ return (*parsenextc++);
+@@ -246,11 +273,13 @@
+ if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+ return PEOF;
+ flushout(&output);
++#ifdef FLUSHERR
+ flushout(&errout);
++#endif
+
+ again:
+ if (parselleft <= 0) {
+- if ((parselleft = preadfd()) == -1) {
++ if ((parselleft = preadfd()) <= 0) {
+ parselleft = parsenleft = EOF_NLEFT;
+ return PEOF;
+ }
+@@ -259,34 +288,39 @@
+ q = p = parsenextc;
+
+ /* delete nul characters */
++#ifndef SMALL
+ something = 0;
++#endif
+ for (more = 1; more;) {
+ switch (*p) {
+ case '\0':
+ p++; /* Skip nul */
+ goto check;
+
++#ifndef SMALL
+ case '\t':
+ case ' ':
+ break;
++#endif
+
+ case '\n':
+ parsenleft = q - parsenextc;
+ more = 0; /* Stop processing here */
+ break;
+
++#ifndef SMALL
+ default:
+ something = 1;
+ break;
++#endif
+ }
+
+ *q++ = *p++;
+ check:
+- if (--parselleft <= 0) {
++ if (--parselleft <= 0 && more) {
+ parsenleft = q - parsenextc - 1;
+ if (parsenleft < 0)
+ goto again;
+- *q = '\0';
+ more = 0;
+ }
+ }
+@@ -306,7 +340,9 @@
+
+ if (vflag) {
+ out2str(parsenextc);
++#ifdef FLUSHERR
+ flushout(out2);
++#endif
+ }
+
+ *q = savec;
+@@ -330,13 +366,14 @@
+ * We handle aliases this way.
+ */
+ void
+-pushstring(s, len, ap)
++pushstring(s, ap)
+ char *s;
+- int len;
+ void *ap;
+ {
+ struct strpush *sp;
++ size_t len;
+
++ len = strlen(s);
+ INTOFF;
+ /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
+ if (parsefile->strpush) {
+@@ -347,10 +384,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;
+@@ -362,12 +400,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);
+@@ -414,13 +461,15 @@
+ (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+ if (push) {
+ pushfile();
+- parsefile->buf = ckmalloc(BUFSIZ);
++ parsefile->buf = 0;
++ } else {
++ closescript();
++ while (parsefile->strpush)
++ popstring();
+ }
+- 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;
+ }
+@@ -431,15 +480,13 @@
+ */
+
+ void
+-setinputstring(string, push)
++setinputstring(string)
+ char *string;
+- int push;
+ {
+ INTOFF;
+- if (push)
+ pushfile();
+ parsenextc = string;
+- parselleft = parsenleft = strlen(string);
++ parsenleft = strlen(string);
+ parsefile->buf = NULL;
+ plinno = 1;
+ INTON;
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/input.h bin_NetBSD-1.6release/src/bin/sh/input.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/input.h 2000-05-23 11:03:19.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/input.h 2003-02-08 14:35:42.000000000 +0000
+@@ -48,17 +48,17 @@
+ 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 __P((char *, int));
+ int pgetc __P((void));
++int pgetc2 __P((void));
+ int preadbuffer __P((void));
+ void pungetc __P((void));
+-void pushstring __P((char *, int, void *));
++void pushstring __P((char *, void *));
+ void popstring __P((void));
+ void setinputfile __P((const char *, int));
+ void setinputfd __P((int, int));
+-void setinputstring __P((char *, int));
++void setinputstring __P((char *));
+ void popfile __P((void));
+ void popallfiles __P((void));
+ void closescript __P((void));
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/jobs.c bin_NetBSD-1.6release/src/bin/sh/jobs.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/jobs.c 2002-05-16 11:41:20.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/jobs.c 2003-02-08 14:35:42.000000000 +0000
+@@ -85,21 +85,30 @@
+ #include "mystring.h"
+
+
+-struct job *jobtab; /* array of jobs */
+-int njobs; /* size of array */
+-MKINIT short backgndpid = -1; /* pid of last background process */
++/* array of jobs */
++STATIC struct job *jobtab;
++/* size of array */
++STATIC int njobs;
++/* pid of last background process */
++MKINIT pid_t backgndpid;
+ #if JOBS
+-int initialpgrp; /* pgrp of shell on invocation */
+-short curjob; /* current job */
++/* pgrp of shell on invocation */
++STATIC int initialpgrp;
++/* current job */
++STATIC struct job *curjob;
+ #endif
+-static int ttyfd = -1;
++STATIC int ttyfd = -1;
++/* number of presumed living untracked jobs */
++STATIC int jobless;
+
+ STATIC void restartjob __P((struct job *));
+ STATIC void freejob __P((struct job *));
+ STATIC struct job *getjob __P((char *));
+ STATIC int dowait __P((int, struct job *));
++#ifdef SYSV
+ STATIC int onsigchild __P((void));
+-STATIC int waitproc __P((int, struct job *, int *));
++#endif
++STATIC int waitproc __P((int, int *));
+ STATIC void cmdtxt __P((union node *));
+ STATIC void cmdputs __P((const char *));
+
+@@ -127,12 +136,15 @@
+ }
+ #endif
+
++#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.
+ */
+
+ MKINIT int jobctl;
+@@ -144,93 +156,185 @@
+ #ifdef OLD_TTY_DRIVER
+ int ldisc;
+ #endif
++ int fd;
++ int pgrp;
+
+ if (on == jobctl || rootshell == 0)
+ return;
+ if (on) {
+ #if defined(FIOCLEX) || defined(FD_CLOEXEC)
+- int err;
+- if (ttyfd != -1)
+- close(ttyfd);
+- if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) {
+- int i;
+- for (i = 0; i < 3; i++) {
+- if (isatty(i) && (ttyfd = dup(i)) != -1)
+- break;
++ int ofd;
++ ofd = fd = open(_PATH_TTY, O_RDWR);
++ if (fd < 0) {
++ fd += 3;
++ while (!isatty(fd) && --fd >= 0)
++ ;
+ }
+- if (i == 3)
++ fd = fcntl(fd, F_DUPFD, 10);
++ close(ofd);
++ if (fd < 0)
+ goto out;
+- }
+-#ifdef FIOCLEX
+- err = ioctl(ttyfd, FIOCLEX, 0);
+-#elif FD_CLOEXEC
+- err = fcntl(ttyfd, FD_CLOEXEC, 1);
++#ifdef linux
++ fcntl(fd, F_SETFD, FD_CLOEXEC);
++#elif defined(FIOCLEX)
++ ioctl(fd, FIOCLEX, 0);
++#elif defined(FD_CLOEXEC)
++ fcntl(fd, FD_CLOEXEC, 1);
+ #endif
+- if (err == -1) {
+- close(ttyfd);
+- ttyfd = -1;
+- goto out;
+- }
+ #else
+- out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control");
+- goto out;
++ warnx("Need FIOCLEX or FD_CLOEXEC to support job control");
++ mflag = 0;
++ return;
+ #endif
+ do { /* while we are in the background */
+- if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) {
++ if ((pgrp = tcgetpgrp(fd)) < 0) {
+ out:
+- out2str("sh: can't access tty; job control turned off\n");
+- mflag = 0;
+- return;
++ warnx("can't access tty; job control turned off");
++ mflag = on = 0;
++ goto close;
+ }
+- if (initialpgrp == -1)
+- initialpgrp = getpgrp();
+- else if (initialpgrp != getpgrp()) {
++ if (pgrp == getpgrp())
++ break;
+ killpg(0, SIGTTIN);
+- continue;
+- }
+- } while (0);
++ } while (1);
++ initialpgrp = pgrp;
+
+ #ifdef OLD_TTY_DRIVER
+- if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0
++ if (ioctl(fd, TIOCGETD, (char *)&ldisc) < 0
+ || ldisc != NTTYDISC) {
+- out2str("sh: need new tty driver to run job control; job control turned off\n");
+- mflag = 0;
+- return;
++ warnx("need new tty driver to run job control; job control turned off");
++ mflag = on = 0;
++ goto close;
+ }
+ #endif
+ setsignal(SIGTSTP);
+ setsignal(SIGTTOU);
+ setsignal(SIGTTIN);
+- setpgid(0, rootpid);
+- tcsetpgrp(ttyfd, rootpid);
+- } else { /* turning job control off */
+- setpgid(0, initialpgrp);
+- tcsetpgrp(ttyfd, initialpgrp);
+- close(ttyfd);
+- ttyfd = -1;
++ pgrp = rootpid;
++ setpgid(0, pgrp);
++ tcsetpgrp(fd, pgrp);
++ } else {
++ /* turning job control off */
++ fd = ttyfd;
++ pgrp = initialpgrp;
++ tcsetpgrp(fd, pgrp);
++ setpgid(0, pgrp);
+ setsignal(SIGTSTP);
+ setsignal(SIGTTOU);
+ setsignal(SIGTTIN);
++close:
++ close(fd);
++ fd = -1;
+ }
++ ttyfd = fd;
+ jobctl = on;
+ }
++#endif
+
+
+-#ifdef mkinit
+-INCLUDE <stdlib.h>
+
+-SHELLPROC {
+- backgndpid = -1;
+ #if JOBS
+- jobctl = 0;
+-#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:
++ error(
++"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
++"kill -l [exitstatus]"
++ );
++ }
++
++ if (*argv[1] == '-') {
++ signo = decode_signal(argv[1] + 1, 1);
++ if (signo < 0) {
++ int c;
+
++ while ((c = nextopt("ls:")) != '\0')
++ switch (c) {
++ case 'l':
++ list = 1;
++ break;
++ case 's':
++ signo = decode_signal(optionarg, 1);
++ if (signo < 0) {
++ error(
++ "invalid signal number or name: %s",
++ optionarg
++ );
++ }
++ break;
++#ifdef DEBUG
++ default:
++ error(
++ "nextopt returned character code 0%o", c);
+ #endif
++ }
++ } else
++ argptr++;
++ }
+
++ if (!list && signo < 0)
++ signo = SIGTERM;
+
++ if ((signo < 0 || !*argptr) ^ list) {
++ goto usage;
++ }
++
++ if (list) {
++ if (!*argptr) {
++ out1str("0\n");
++ for (i = 1; i < NSIG; i++) {
++ out1fmt(snlfmt, signal_names[i]);
++ }
++ return 0;
++ }
++ signo = atoi(*argptr);
++ if (signo > 128)
++ signo -= 128;
++ if (0 < signo && signo < NSIG)
++ out1fmt(snlfmt, signal_names[signo]);
++ else
++ error("invalid signal number or exit status: %s",
++ *argptr);
++ return 0;
++ }
++
++ i = 0;
++ do {
++ if (**argptr == '%') {
++ jp = getjob(*argptr);
++ if (jp->jobctl == 0) {
++ outfmt(
++ out2,
++ "job %s not created "
++ "under job control\n",
++ *argptr
++ );
++ i = 1;
++ continue;
++ }
++ pid = -jp->ps[0].pid;
++ } else
++ pid = atoi(*argptr);
++ if (kill(pid, signo) != 0) {
++ outfmt(out2, "%s: %s\n", *argptr, strerror(errno));
++ i = 1;
++ }
++ } while (*++argptr);
++
++ return i;
++}
+
+-#if JOBS
+ int
+ fgcmd(argc, argv)
+ int argc;
+@@ -246,9 +350,7 @@
+ pgrp = jp->ps[0].pid;
+ tcsetpgrp(ttyfd, pgrp);
+ restartjob(jp);
+- INTOFF;
+ status = waitforjob(jp);
+- INTON;
+ return status;
+ }
+
+@@ -366,12 +468,10 @@
+ }
+ out1str(s);
+ col += strlen(s);
+- do {
+- out1c(' ');
+- col++;
+- } while (col < 30);
+- out1str(ps->cmd);
+- out1c('\n');
++ out1fmt(
++ "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ',
++ ps->cmd
++ );
+ if (--procno <= 0)
+ break;
+ }
+@@ -399,14 +499,11 @@
+ if (ps->cmd != nullstr)
+ ckfree(ps->cmd);
+ }
+- if (jp->ps != &jp->ps0) {
++ if (jp->ps != &jp->ps0)
+ ckfree(jp->ps);
+- jp->ps = &jp->ps0;
+- }
+- jp->nprocs = 0;
+ jp->used = 0;
+ #if JOBS
+- if (curjob == jp - jobtab + 1)
++ if (curjob == jp)
+ curjob = 0;
+ #endif
+ INTON;
+@@ -423,8 +520,9 @@
+ int status, retval;
+ struct job *jp;
+
+- if (argc > 1) {
+- job = getjob(argv[1]);
++ if (--argc > 0) {
++start:
++ job = getjob(*++argv);
+ } else {
+ job = NULL;
+ }
+@@ -432,6 +530,11 @@
+ if (job != NULL) {
+ if (job->state) {
+ status = job->ps[job->nprocs - 1].status;
++ if (! iflag)
++ freejob(job);
++ if (--argc) {
++ goto start;
++ }
+ if (WIFEXITED(status))
+ retval = WEXITSTATUS(status);
+ #if JOBS
+@@ -442,26 +545,28 @@
+ /* XXX: limits number of signals */
+ retval = WTERMSIG(status) + 128;
+ }
+- if (! iflag)
+- freejob(job);
+ return retval;
+ }
+ } else {
++ struct job *end = jobtab + njobs;
+ for (jp = jobtab ; ; jp++) {
+- if (jp >= jobtab + njobs) { /* no running procs */
++ if (jp >= end) {
++ /* no running procs */
+ return 0;
+ }
+ if (jp->used && jp->state == 0)
+ break;
+ }
+ }
+- if (dowait(1, (struct job *)NULL) == -1)
+- return 128 + SIGINT;
++ dowait(2, 0);
++ if (pendingsigs)
++ return 128 + pendingsigs;
+ }
+ }
+
+
+
++#if 0
+ int
+ jobidcmd(argc, argv)
+ int argc;
+@@ -472,11 +577,14 @@
+
+ jp = getjob(argv[1]);
+ for (i = 0 ; i < jp->nprocs ; ) {
+- out1fmt("%ld", (long)jp->ps[i].pid);
+- out1c(++i < jp->nprocs? ' ' : '\n');
++ out1fmt(
++ "%ld%c", (long)jp->ps[i].pid,
++ ++i < jp->nprocs? ' ' : '\n'
++ );
+ }
+ return 0;
+ }
++#endif
+
+
+
+@@ -496,9 +604,9 @@
+ if (name == NULL) {
+ #if JOBS
+ currentjob:
+- if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
++ if (!curjob || !curjob->used)
+ error("No current job");
+- return &jobtab[jobno - 1];
++ return curjob;
+ #else
+ error("No current job");
+ #endif
+@@ -575,13 +683,13 @@
+ break;
+ }
+ INTOFF;
+- jp->state = 0;
+- jp->used = 1;
+- jp->changed = 0;
+ jp->nprocs = 0;
++ jp->state = 0;
+ #if JOBS
+ jp->jobctl = jobctl;
+ #endif
++ jp->changed = 0;
++ jp->used = 1;
+ if (nprocs > 1) {
+ jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
+ } else {
+@@ -636,23 +744,17 @@
+ TRACE(("Child shell %d\n", getpid()));
+ wasroot = rootshell;
+ rootshell = 0;
+- for (i = njobs, p = jobtab ; --i >= 0 ; p++) {
+- if (p == jp)
+- continue; /* don't free current job */
+- if (p->used)
+- freejob(p);
+- }
+ closescript();
+ INTON;
+ clear_traps();
+ #if JOBS
+- jobctl = 0; /* do job control only in root shell */
++ jobctl = pid; /* 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;
+- setpgid(0, pgrp);
++ setpgid(pid, pgrp);
+ if (mode == FORK_FG) {
+ /*** this causes superfluous TIOCSPGRPS ***/
+ if (tcsetpgrp(ttyfd, pgrp) < 0)
+@@ -661,32 +763,26 @@
+ setsignal(SIGTSTP);
+ setsignal(SIGTTOU);
+ } else if (mode == FORK_BG) {
+- ignoresig(SIGINT);
+- ignoresig(SIGQUIT);
+- 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) {
++#endif
+ ignoresig(SIGINT);
+ ignoresig(SIGQUIT);
+- if ((jp == NULL || jp->nprocs == 0) &&
+- ! fd0_redirected_p ()) {
+- close(0);
++ if ((jp == NULL || jp->nprocs == 0)) {
++ close(pid);
+ if (open(devnull, O_RDONLY) != 0)
+ error(nullerr, devnull);
+ }
+ }
+-#endif
++ for (i = njobs, p = jobtab ; --i >= 0 ; p++)
++ if (p->used)
++ freejob(p);
+ if (wasroot && iflag) {
+ setsignal(SIGINT);
+ setsignal(SIGQUIT);
+ setsignal(SIGTERM);
+ }
++ jobless = pid;
+ return pid;
+ }
+ if (rootshell && mode != FORK_NOJOB && mflag) {
+@@ -705,6 +801,9 @@
+ ps->cmd = nullstr;
+ if (iflag && rootshell && n)
+ ps->cmd = commandtext(n);
++ } else {
++ while (jobless && dowait(0, 0) > 0);
++ jobless++;
+ }
+ INTON;
+ TRACE(("In parent shell: child = %d\n", pid));
+@@ -753,7 +852,7 @@
+ error("tcsetpgrp failed, errno=%d\n", errno);
+ }
+ if (jp->state == JOBSTOPPED)
+- curjob = jp - jobtab + 1;
++ curjob = jp;
+ #endif
+ status = jp->ps[jp->nprocs - 1].status;
+ /* convert to 8 bits */
+@@ -798,29 +897,31 @@
+ {
+ int pid;
+ int status;
+- struct procstat *sp;
+ struct job *jp;
++ struct job *end;
+ struct job *thisjob;
+- int done;
+- int stopped;
+ int core;
+ int sig;
+- extern volatile char gotsig[];
++ int state;
+
+ TRACE(("dowait(%d) called\n", block));
+ do {
+- pid = waitproc(block, job, &status);
++ pid = waitproc(block, &status);
+ TRACE(("wait returns %d, status=%d\n", pid, status));
+- } while (pid == -1 && errno == EINTR && gotsig[SIGINT - 1] == 0);
++ } while (!(block & 2) && pid == -1 && errno == EINTR);
+ 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++) {
++ end = jobtab + njobs;
++ for (jp = jobtab; jp < end; jp++) {
++ struct procstat *sp;
++ struct procstat *spend;
++ if (!jp->used)
++ continue;
++ state = JOBDONE;
++ spend = jp->ps + jp->nprocs;
++ for (sp = jp->ps; sp < spend; sp++) {
+ if (sp->pid == -1)
+ continue;
+ if (sp->pid == pid) {
+@@ -829,23 +930,30 @@
+ thisjob = jp;
+ }
+ if (sp->status == -1)
+- stopped = 0;
++ state = 0;
+ else if (WIFSTOPPED(sp->status))
+- done = 0;
++ state = JOBSTOPPED;
+ }
+- 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 (!thisjob)
++ continue;
++ if (state) {
++ /* stopped or done */
++ if (thisjob->state != state) {
++ TRACE(("Job %d: changing state from %d to %d\n", thisjob - jobtab + 1, thisjob->state, state));
++ thisjob->state = state;
+ #if JOBS
+- if (done && curjob == jp - jobtab + 1)
+- curjob = 0; /* no current job */
+-#endif
++ if (state == JOBDONE && curjob == thisjob) {
++ /* no current job */
++ curjob = 0;
+ }
++#endif
+ }
+ }
++ goto gotjob;
+ }
++ if (!WIFSTOPPED(status))
++ jobless--;
++gotjob:
+ INTON;
+ if (! rootshell || ! iflag || (job && thisjob == job)) {
+ core = WCOREDUMP(status);
+@@ -871,7 +979,9 @@
+ if (core)
+ out2str(" - core dumped");
+ out2c('\n');
++#ifdef FLUSHERR
+ flushout(&errout);
++#endif
+ } else {
+ TRACE(("Not printing status: status=%d, sig=%d\n",
+ status, sig));
+@@ -925,16 +1035,15 @@
+
+
+ STATIC int
+-waitproc(block, jp, status)
++waitproc(block, status)
+ int block;
+- struct job *jp;
+ int *status;
+ {
+ #ifdef BSD
+ int flags = 0;
+
+ #if JOBS
+- if (jp != NULL && jp->jobctl)
++ if (jobctl)
+ flags |= WUNTRACED;
+ #endif
+ if (block == 0)
+@@ -963,7 +1072,7 @@
+ /*
+ * return 1 if there are stopped jobs, otherwise 0
+ */
+-int job_warning = 0;
++int job_warning;
+ int
+ stoppedjobs()
+ {
+@@ -1088,10 +1197,10 @@
+ for (np = n->ncmd.args ; np ; np = np->narg.next) {
+ cmdtxt(np);
+ if (np->narg.next)
+- cmdputs(" ");
++ cmdputs(spcstr);
+ }
+ for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
+- cmdputs(" ");
++ cmdputs(spcstr);
+ cmdtxt(np);
+ }
+ break;
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/jobs.h bin_NetBSD-1.6release/src/bin/sh/jobs.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/jobs.h 2000-05-23 11:03:19.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/jobs.h 2003-02-08 14:35:42.000000000 +0000
+@@ -38,6 +38,9 @@
+ * @(#)jobs.h 8.2 (Berkeley) 5/4/95
+ */
+
++#include <stdint.h>
++#include <sys/types.h>
++
+ /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
+ #define FORK_FG 0
+ #define FORK_BG 1
+@@ -66,20 +69,22 @@
+ struct job {
+ struct procstat ps0; /* status of process */
+ struct procstat *ps; /* status or processes when more than one */
+- short nprocs; /* number of processes */
+- short pgrp; /* process group of this job */
+- char state; /* true if job is finished */
+- char used; /* true if this entry is in used */
+- char changed; /* true if status has changed */
++ pid_t pgrp; /* process group of this job */
++ uint32_t
++ nprocs: 16, /* number of processes */
++ state: 8, /* true if job is finished */
+ #if JOBS
+- char jobctl; /* job running under job control */
++ jobctl: 1, /* job running under job control */
+ #endif
++ used: 1, /* true if this entry is in used */
++ changed: 1; /* true if status has changed */
+ };
+
+-extern short backgndpid; /* pid of last background process */
++extern pid_t backgndpid; /* pid of last background process */
+ extern int job_warning; /* user was warned about stopped jobs */
+
+ void setjobctl __P((int));
++int killcmd __P((int, char **));
+ int fgcmd __P((int, char **));
+ int bgcmd __P((int, char **));
+ int jobscmd __P((int, char **));
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/mail.c bin_NetBSD-1.6release/src/bin/sh/mail.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/mail.c 2001-01-12 16:50:36.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/mail.c 2003-02-08 14:35:42.000000000 +0000
+@@ -53,73 +53,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(silent)
+- int silent;
++chkmail()
+ {
+- 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();
++#endif
+ 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');
++ if (stat64(p, &statb) < 0) {
++ *mtp = 0;
++ continue;
+ }
+- 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');
++ if (!changed && statb.st_mtime != *mtp) {
++ outfmt(
++ &errout, snlfmt,
++ pathopt? pathopt : "you have mail"
++ );
+ }
+- mailtime[i] = statb.st_size;
+-#endif
++ *mtp = statb.st_mtime;
+ }
+- nmboxes = i;
++ changed = 0;
+ popstackmark(&smark);
+ }
++
++
++void
++changemail(const char *val)
++{
++ changed++;
++}
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/mail.h bin_NetBSD-1.6release/src/bin/sh/mail.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/mail.h 1995-10-14 00:44:08.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/mail.h 2003-02-08 14:35:42.000000000 +0000
+@@ -38,4 +38,5 @@
+ * @(#)mail.h 8.2 (Berkeley) 5/4/95
+ */
+
+-void chkmail __P((int));
++void chkmail __P((void));
++void changemail __P((const char *));
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/main.c bin_NetBSD-1.6release/src/bin/sh/main.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/main.c 2001-06-13 11:08:37.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/main.c 2003-02-08 14:35:42.000000000 +0000
+@@ -79,12 +79,14 @@
+ #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;
+ #if PROFILE
+ short profile_buf[16384];
+ extern int etext();
+@@ -108,53 +110,49 @@
+ int argc;
+ char **argv;
+ {
++ char *shinit;
++ volatile int state;
+ struct jmploc jmploc;
+ struct stackmark smark;
+- volatile int state;
+- char *shinit;
+
+ #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;
++ int status;
++ int e;
+
++ reset();
++
++ e = exception;
++ switch (exception) {
+ case EXEXEC:
+- exitstatus = exerrno;
++ status = exerrno;
+ break;
+
+ case EXERROR:
+- exitstatus = 2;
++ status = 2;
+ break;
+
+ default:
++ status = exitstatus;
+ break;
+ }
++ exitstatus = status;
+
+- if (exception != EXSHELLPROC) {
+- if (state == 0 || iflag == 0 || ! rootshell)
+- exitshell(exitstatus);
+- }
+- reset();
+- if (exception == EXINT
++ if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell)
++ 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 */
+@@ -186,29 +184,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]);
+- }
+-
+ if (minusc)
+ evalstring(minusc, 0);
+
+@@ -219,7 +206,7 @@
+ #if PROFILE
+ monitor(0);
+ #endif
+- exitshell(exitstatus);
++ exitshell();
+ /* NOTREACHED */
+ }
+
+@@ -240,6 +227,10 @@
+
+ TRACE(("cmdloop(%d) called\n", top));
+ setstackmark(&smark);
++#ifdef HETIO
++ if(iflag && top)
++ hetio_init();
++#endif
+ for (;;) {
+ if (pendingsigs)
+ dotrap();
+@@ -247,7 +238,7 @@
+ if (iflag && top) {
+ inter++;
+ showjobs(1);
+- chkmail(0);
++ chkmail();
+ flushout(&output);
+ }
+ n = parsecmd(inter);
+@@ -376,12 +367,8 @@
+ int argc;
+ char **argv;
+ {
+- struct strlist *sp;
+ exitstatus = 0;
+
+- for (sp = cmdenviron; sp ; sp = sp->next)
+- setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
+-
+ if (argc >= 2) { /* That's what SVR2 does */
+ char *fullname;
+ struct stackmark smark;
+@@ -403,12 +390,14 @@
+ int argc;
+ char **argv;
+ {
++ int status;
++
+ if (stoppedjobs())
+ return 0;
++ status = oexitstatus;
+ if (argc > 1)
+- exitstatus = number(argv[1]);
+- else
+- exitstatus = oexitstatus;
+- exitshell(exitstatus);
++ status = number(argv[1]);
++ exitstatus = status;
++ exraise(EXEXIT);
+ /* NOTREACHED */
+ }
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/Makefile bin_NetBSD-1.6release/src/bin/sh/Makefile
+--- bin_NetBSD-1.6release.orig/src/bin/sh/Makefile 2002-12-07 08:17:35.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/Makefile 2003-02-08 14:39:28.000000000 +0000
+@@ -1,40 +1,35 @@
+-# $NetBSD: Makefile,v 1.57.2.2 2002/12/06 23:29:26 he Exp $
++# $NetBSD: Makefile,v 1.59 2002/09/15 00:19:22 thorpej Exp $
+ # @(#)Makefile 8.4 (Berkeley) 5/5/95
+
+ .include <bsd.own.mk>
+
+ YHEADER=1
+ PROG= sh
+-SHSRCS= alias.c cd.c echo.c error.c eval.c exec.c expand.c \
++SHSRCS= alias.c arith_yylex.c cd.c echo.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 var.c \
+- test.c
+-GENSRCS=arith.c arith.h arith_lex.c builtins.c builtins.h init.c nodes.c \
+- nodes.h syntax.c syntax.h token.h
++ mystring.c options.c parser.c printf.c redir.c show.c trap.c output.c \
++ var.c test.c setmode.c times.c hetio.c
++GENSRCS=arith.c arith.h builtins.c builtins.h init.c nodes.c \
++ nodes.h signames.c syntax.c syntax.h token.h
+ SRCS= ${SHSRCS} ${GENSRCS}
+
+-LDADD+= -ll -ledit -ltermcap
+-DPADD+= ${LIBL} ${LIBEDIT} ${LIBTERMCAP}
+-
+-LFLAGS= -8 # 8-bit lex scanner for arithmetic
+ YFLAGS= -d
+
+ CPPFLAGS+=-DSHELL -I. -I${.CURDIR}
++CPPFLAGS+=-DSMALL -DHETIO
+
+-.ifdef SMALLPROG
+-CPPFLAGS+=-DSMALL
+-.endif
+-
+-.PATH: ${.CURDIR}/bltin ${.CURDIR}/../test
++.PATH: ${.CURDIR}/bltin ${NETBSDSRCDIR}/bin/test
+
+ CLEANFILES+= mkinit mknodes mksyntax
+ CLEANFILES+= ${GENSRCS} y.tab.h
+
++BLTINDEFS=specialbltins.def regularbltins.def assignbltins.def
++
+ token.h: mktokens
+ sh ${.ALLSRC}
+
+-builtins.c builtins.h: mkbuiltins shell.h builtins.def
+- sh ${.ALLSRC} ${.OBJDIR}
++builtins.c builtins.h: mkbuiltins shell.h builtins.def ${BLTINDEFS}
++ sh ${.ALLSRC} ${.OBJDIR} '${CFLAGS}'
+
+ init.c: mkinit ${SHSRCS}
+ ./${.ALLSRC}
+@@ -45,22 +40,18 @@
+ syntax.c syntax.h: mksyntax
+ ./${.ALLSRC}
+
++signames.c: mksignames
++ ./${.ALLSRC}
++
+ mkinit: mkinit.c
+ ${HOST_LINK.c} -o mkinit ${.IMPSRC}
+
+ mknodes: mknodes.c
+ ${HOST_LINK.c} -o mknodes ${.IMPSRC}
+
+-.if (${MACHINE_ARCH} == "powerpc") || \
+- (${MACHINE_CPU} == "arm")
+-TARGET_CHARFLAG= -DTARGET_CHAR="unsigned char"
+-.else
+-TARGET_CHARFLAG= -DTARGET_CHAR="signed char"
+-.endif
+-
+ mksyntax: mksyntax.c
+ ${HOST_LINK.c} ${TARGET_CHARFLAG} -o mksyntax ${.IMPSRC}
+
+ .include <bsd.prog.mk>
+
+-${OBJS}: builtins.h nodes.h syntax.h token.h
++${OBJS}: arith.h builtins.h nodes.h syntax.h token.h
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/memalloc.c bin_NetBSD-1.6release/src/bin/sh/memalloc.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/memalloc.c 2001-01-12 16:50:36.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/memalloc.c 2003-02-08 14:35:42.000000000 +0000
+@@ -60,15 +60,11 @@
+ */
+
+ pointer
+-ckmalloc(nbytes)
+- int nbytes;
++ckmalloc(size_t nbytes)
+ {
+ pointer p;
+
+- INTOFF;
+- p = malloc(nbytes);
+- INTON;
+- if (p == NULL)
++ if ((p = malloc(nbytes)) == NULL)
+ error("Out of space");
+ return p;
+ }
+@@ -79,9 +75,7 @@
+ */
+
+ pointer
+-ckrealloc(p, nbytes)
+- pointer p;
+- int nbytes;
++ckrealloc(pointer p, size_t nbytes)
+ {
+
+ if ((p = realloc(p, nbytes)) == NULL)
+@@ -96,12 +90,11 @@
+
+ char *
+ savestr(s)
+- char *s;
++ const char *s;
+ {
+- char *p;
+-
+- p = ckmalloc(strlen(s) + 1);
+- scopy(s, p);
++ char *p = strdup(s);
++ if (!p)
++ error("Out of space");
+ return p;
+ }
+
+@@ -115,7 +108,7 @@
+ * well.
+ */
+
+-#define MINSIZE 504 /* minimum size of a block */
++#define MINSIZE ALIGN(504) /* minimum size of a block */
+
+
+ struct stack_block {
+@@ -127,37 +120,42 @@
+ 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(nbytes)
+- int nbytes;
++stalloc(size_t nbytes)
+ {
+ char *p;
++ size_t aligned;
+
+- nbytes = ALIGN(nbytes);
++ aligned = ALIGN(nbytes);
+ if (nbytes > stacknleft) {
+- int blocksize;
++ 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)
++ 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;
+ }
+
+@@ -166,10 +164,12 @@
+ stunalloc(p)
+ 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;
+ }
+@@ -203,6 +203,7 @@
+ }
+ stacknxt = mark->stacknxt;
+ stacknleft = mark->stacknleft;
++ sstrend = mark->stacknxt + mark->stacknleft;
+ INTON;
+ }
+
+@@ -220,22 +221,31 @@
+ void
+ growstackblock() {
+ char *p;
+- int newlen = ALIGN(stacknleft * 2 + 100);
++ size_t newlen, grosslen;
+ char *oldspace = stacknxt;
+- int oldlen = stacknleft;
++ size_t oldlen = stacknleft;
+ struct stack_block *sp;
+ struct stack_block *oldstackp;
++ struct stack_block *prevstackp;
++
++ grosslen = stacknleft * 2;
++ if (grosslen < stacknleft)
++ error("Out of space");
++ if (grosslen < 128)
++ grosslen += 128 + sizeof(struct stack_block) - MINSIZE;
++ newlen = grosslen - (sizeof(struct stack_block) - MINSIZE);
+
+ if (stacknxt == stackp->space && stackp != &stackbase) {
+ INTOFF;
+ oldstackp = stackp;
+ sp = stackp;
+- stackp = sp->prev;
+- sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen);
+- sp->prev = stackp;
++ prevstackp = sp->prev;
++ 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
+ * must be relocated to point to the new block
+@@ -252,17 +262,16 @@
+ INTON;
+ } else {
+ p = stalloc(newlen);
+- 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(len)
+- int len;
++grabstackblock(size_t len)
+ {
+ len = ALIGN(len);
+ stacknxt += len;
+@@ -290,16 +299,14 @@
+ */
+
+
+-char *
++void *
+ growstackstr() {
+- 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;
+ }
+
+@@ -309,21 +316,33 @@
+ */
+
+ char *
+-makestrspace() {
+- int len = stackblocksize() - sstrnleft;
++makestrspace(size_t newlen, char *p) {
++ size_t len = p - stackblock();
++ size_t size = stackblocksize();
++
++ for (;;) {
++ size_t nleft;
++
++ size = stackblocksize();
++ nleft = size - len;
++ if (nleft >= newlen)
++ break;
+ growstackblock();
+- sstrnleft = stackblocksize() - len;
++ }
+ return stackblock() + len;
+ }
+
+
+
+-void
+-ungrabstackstr(s, p)
+- char *s;
+- char *p;
+- {
+- stacknleft += stacknxt - s;
+- stacknxt = s;
+- sstrnleft = stacknleft - (p - s);
++char *
++stnputs(const char *s, size_t n, char *p) {
++ 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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/memalloc.h bin_NetBSD-1.6release/src/bin/sh/memalloc.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/memalloc.h 2001-01-12 16:50:37.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/memalloc.h 2003-02-08 14:35:42.000000000 +0000
+@@ -38,44 +38,62 @@
+ * @(#)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 __P((int));
+-pointer ckrealloc __P((pointer, int));
+-char *savestr __P((char *));
+-pointer stalloc __P((int));
++pointer ckmalloc __P((size_t));
++pointer ckrealloc __P((pointer, size_t));
++char *savestr __P((const char *));
++pointer stalloc __P((size_t));
+ void stunalloc __P((pointer));
+ void setstackmark __P((struct stackmark *));
+ void popstackmark __P((struct stackmark *));
+ void growstackblock __P((void));
+-void grabstackblock __P((int));
+-char *growstackstr __P((void));
+-char *makestrspace __P((void));
+-void ungrabstackstr __P((char *, char *));
++void grabstackblock __P((size_t));
++void *growstackstr __P((void));
++char *makestrspace __P((size_t, char *));
++char *stnputs __P((const char *, size_t, char *));
++char *stputs __P((const char *, char *));
+
+
++static inline char *_STPUTC(char c, char *p) {
++ if (p == sstrend)
++ p = growstackstr();
++ *p++ = c;
++ return p;
++}
+
+ #define stackblock() 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) = ((void *) 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)) - stackblock())
++#define ungrabstackstr(s, p) stunalloc((s))
+
+ #define ckfree(p) free((pointer)(p))
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/miscbltin.c bin_NetBSD-1.6release/src/bin/sh/miscbltin.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/miscbltin.c 2001-02-05 11:15:31.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/miscbltin.c 2003-02-08 14:35:42.000000000 +0000
+@@ -70,6 +70,15 @@
+
+ #undef rflag
+
++#ifdef __GLIBC__
++mode_t getmode(const void *, mode_t);
++void *setmode(const char *);
++
++#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
++typedef enum __rlimit_resource rlim_t;
++#endif
++#endif
++
+
+
+ /*
+@@ -89,7 +98,7 @@
+ char c;
+ int rflag;
+ char *prompt;
+- char *ifs;
++ const char *ifs;
+ char *p;
+ int startword;
+ int status;
+@@ -105,12 +114,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;
++ if ((ifs = bltinlookup("IFS")) == NULL)
++ ifs = defifs;
+ status = 0;
+ startword = 1;
+ backslash = 0;
+@@ -125,7 +136,7 @@
+ if (backslash) {
+ backslash = 0;
+ if (c != '\n')
+- STPUTC(c, p);
++ goto put;
+ continue;
+ }
+ if (!rflag && c == '\\') {
+@@ -138,19 +149,14 @@
+ 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);
+ }
+ }
+@@ -225,7 +231,7 @@
+ mask = 0;
+ do {
+ if (*ap >= '8' || *ap < '0')
+- error("Illegal number: %s", argv[1]);
++ error(illnum, argv[1]);
+ mask = (mask << 3) + (*ap - '0');
+ } while (*++ap != '\0');
+ umask(mask);
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/mkbuiltins bin_NetBSD-1.6release/src/bin/sh/mkbuiltins
+--- bin_NetBSD-1.6release.orig/src/bin/sh/mkbuiltins 1999-07-09 12:02:07.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/mkbuiltins 2003-02-08 14:35:42.000000000 +0000
+@@ -37,21 +37,25 @@
+ #
+ # @(#)mkbuiltins 8.2 (Berkeley) 5/4/95
+
+-temp=/tmp/ka$$
++trap 'rm -f $temp $temp2' EXIT
++temp=$(tempfile)
++temp2=$(tempfile)
++
++shell=$1
++builtins=$2
++specialbltins=$3
++regularbltins=$4
++assignbltins=$5
++objdir=$6
+
+ havehist=1
+-if [ "X$1" = "X-h" ]; then
++if [ -z "${7##*-DSMALL*}" ]; then
+ havehist=0
+- shift
+ fi
+-shell=$1
+-builtins=$2
+-objdir=$3
+
+-havejobs=0
+-if grep '^#define JOBS[ ]*1' ${shell} > /dev/null
+-then
+- havejobs=1
++havejobs=1
++if [ -z "${7##*-DJOBS=0*}" ]; then
++ havejobs=0
+ fi
+
+ exec > ${objdir}/builtins.c
+@@ -65,19 +69,19 @@
+
+ !
+ awk '/^[^#]/ {if(('$havejobs' || $2 != "-j") && ('$havehist' || $2 != "-h")) \
+- print $0}' ${builtins} | sed 's/-j//' > $temp
++ print $0}' ${builtins} | sed 's/-[hj]//' > $temp
+ awk '{ printf "int %s __P((int, char **));\n", $1}' $temp
+ echo '
+-int (*const builtinfunc[]) __P((int, char **)) = {'
+-awk '/^[^#]/ { printf "\t%s,\n", $1}' $temp
+-echo '};
+-
+ const struct builtincmd builtincmd[] = {'
+ awk '{ for (i = 2 ; i <= NF ; i++) {
+- printf "\t{ \"%s\", %d },\n", $i, NR-1
+- }}' $temp
+-echo ' { NULL, 0 }
+-};'
++ print $i "\t" $1
++ }}' $temp | sort -k 1,1 | tee $temp2 |
++ join -a 1 -e 0 -o 1.1,1.2,2.1 - $specialbltins |
++ join -a 1 -e 0 -o 1.1,1.2,1.3,2.1 - $regularbltins |
++ join -a 1 -e 0 -o 1.1,1.2,1.3,1.4,2.1 - $assignbltins |
++ awk '{ printf "\t{ \"%s\", %s, %d },\n", $1, $2,
++ !!$3 + (!!$4 * 2) + (!!$5 * 4)}'
++echo '};'
+
+ exec > ${objdir}/builtins.h
+ cat <<\!
+@@ -87,14 +91,19 @@
+
+ #include <sys/cdefs.h>
+ !
+-tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp |
+- awk '{ printf "#define %s %d\n", $1, NR-1}'
++nl -v 0 $temp2 | 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 code;
++ int (*const builtinfunc) __P((int, char **));
++ unsigned flags;
+ };
+
+-extern int (*const builtinfunc[]) __P((int, char **));
+ extern const struct builtincmd builtincmd[];'
+-rm -f $temp
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/mkinit.c bin_NetBSD-1.6release/src/bin/sh/mkinit.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/mkinit.c 2001-01-12 16:50:37.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/mkinit.c 2003-02-08 14:35:42.000000000 +0000
+@@ -131,16 +131,10 @@
+ * interactive shell and control is returned to the main command loop.\n\
+ */\n";
+
+-char shellproc[] = "\
+-/*\n\
+- * This routine is called to initialize the shell to run a shell procedure.\n\
+- */\n";
+-
+
+ struct event event[] = {
+ {"INIT", "init", init},
+ {"RESET", "reset", reset},
+- {"SHELLPROC", "initshellproc", shellproc},
+ {NULL, NULL}
+ };
+
+@@ -218,8 +212,6 @@
+ doinclude(line);
+ if (line[0] == 'M' && match("MKINIT", line))
+ dodecl(line, fp);
+- if (line[0] == '#' && gooddefine(line))
+- addstr(line, &defines);
+ if (line[0] == '#' && gooddefine(line)) {
+ char *cp;
+ char line2[1024];
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/mknodes.c bin_NetBSD-1.6release/src/bin/sh/mknodes.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/mknodes.c 2001-01-12 16:50:38.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/mknodes.c 2003-02-08 14:35:42.000000000 +0000
+@@ -1,4 +1,4 @@
+-/* $NetBSD: mknodes.c,v 1.18 2000/07/27 04:06:49 cgd Exp $ */
++/* $NetBSD: mknodes.c,v 1.19 2002/05/25 23:09:06 wiz Exp $ */
+
+ /*-
+ * Copyright (c) 1991, 1993
+@@ -47,7 +47,7 @@
+ static char sccsid[] = "@(#)mknodes.c 8.2 (Berkeley) 5/4/95";
+ #else
+ static const char rcsid[] =
+- "$NetBSD: mknodes.c,v 1.18 2000/07/27 04:06:49 cgd Exp $";
++ "$NetBSD: mknodes.c,v 1.19 2002/05/25 23:09:06 wiz Exp $";
+ #endif
+ #endif /* not lint */
+
+@@ -59,12 +59,7 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+-#ifdef __STDC__
+ #include <stdarg.h>
+-#else
+-#include <varargs.h>
+-#endif
+-
+
+ #define MAXTYPES 50 /* max number of node types */
+ #define MAXFIELDS 20 /* max fields in a structure */
+@@ -270,13 +265,12 @@
+ fputs("\tstruct nodelist *next;\n", hfile);
+ fputs("\tunion node *n;\n", hfile);
+ fputs("};\n\n\n", hfile);
+- fputs("#ifdef __STDC__\n", hfile);
+- fputs("union node *copyfunc(union node *);\n", hfile);
+- fputs("void freefunc(union node *);\n", hfile);
+- fputs("#else\n", hfile);
+- fputs("union node *copyfunc();\n", hfile);
+- fputs("void freefunc();\n", hfile);
+- fputs("#endif\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) {
+@@ -451,21 +445,11 @@
+
+
+ static void
+-#ifdef __STDC__
+ error(const char *msg, ...)
+-#else
+-error(va_alist)
+- va_dcl
+-#endif
+ {
+ va_list va;
+-#ifdef __STDC__
++
+ va_start(va, msg);
+-#else
+- char *msg;
+- va_start(va);
+- msg = va_arg(va, char *);
+-#endif
+
+ (void) fprintf(stderr, "line %d: ", linno);
+ (void) vfprintf(stderr, msg, va);
+Files bin_NetBSD-1.6release.orig/src/bin/sh/mksignames and bin_NetBSD-1.6release/src/bin/sh/mksignames differ
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/mksignames.c bin_NetBSD-1.6release/src/bin/sh/mksignames.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/mksignames.c 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/mksignames.c 2003-02-08 14:35:42.000000000 +0000
+@@ -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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/mksyntax.c bin_NetBSD-1.6release/src/bin/sh/mksyntax.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/mksyntax.c 2002-06-02 02:11:43.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/mksyntax.c 2003-02-08 14:35:42.000000000 +0000
+@@ -1,4 +1,4 @@
+-/* $NetBSD: mksyntax.c,v 1.24.4.1 2002/05/31 16:42:05 tv Exp $ */
++/* $NetBSD: mksyntax.c,v 1.25 2002/05/31 16:18:48 christos Exp $ */
+
+ /*-
+ * Copyright (c) 1991, 1993
+@@ -47,7 +47,7 @@
+ static char sccsid[] = "@(#)mksyntax.c 8.2 (Berkeley) 5/4/95";
+ #else
+ static const char rcsid[] =
+- "$NetBSD: mksyntax.c,v 1.24.4.1 2002/05/31 16:42:05 tv Exp $";
++ "$NetBSD: mksyntax.c,v 1.25 2002/05/31 16:18:48 christos Exp $";
+ #endif
+ #endif /* not lint */
+
+@@ -73,6 +73,7 @@
+ { "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" },
+@@ -81,6 +82,7 @@
+ { "CEOF", "end of file" },
+ { "CCTL", "like CWORD, except it must be escaped" },
+ { "CSPCL", "these terminate a word" },
++ { "CIGN", "character should be ignored" },
+ { NULL, NULL }
+ };
+
+@@ -168,7 +170,7 @@
+ exit(2);
+ }
+ size = (1 << nbits) + 1;
+- base = 1;
++ base = 2;
+ if (sign)
+ base += 1 << (nbits - 1);
+ digit_contig = 1;
+@@ -179,6 +181,11 @@
+
+ fputs("#include <sys/cdefs.h>\n", hfile);
+ 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);
+@@ -201,10 +208,7 @@
+ putc('\n', hfile);
+ fprintf(hfile, "#define SYNBASE %d\n", base);
+ fprintf(hfile, "#define PEOF %d\n\n", -base);
+- if (sign)
+- fprintf(hfile, "#define UPEOF ((char)%d)\n\n", -base);
+- else
+- fprintf(hfile, "#define UPEOF ((unsigned char)%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);
+@@ -227,32 +231,31 @@
+ 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("\"", "CDQUOTE");
++ add("\"", "CENDQUOTE");
+ add("`", "CBQUOTE");
+ add("$", "CVAR");
+ add("}", "CENDVAR");
+ /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */
+- add("!*?[=~:/-", "CCTL");
++ add("!*?[=~:/-]", "CCTL");
+ print("dqsyntax");
+ init();
+ fputs("\n/* syntax table used when in single quotes */\n", cfile);
+ add("\n", "CNL");
+- add("'", "CSQUOTE");
++ add("'", "CENDQUOTE");
+ /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */
+- add("!*?[=~:/-", "CCTL");
++ add("!*?[=~:/-]\\", "CCTL");
+ print("sqsyntax");
+ init();
+ fputs("\n/* syntax table used when in arithmetic */\n", cfile);
+ add("\n", "CNL");
+ add("\\", "CBACK");
+ add("`", "CBQUOTE");
+- add("'", "CSQUOTE");
+- add("\"", "CDQUOTE");
+ add("$", "CVAR");
+ add("}", "CENDVAR");
+ add("(", "CLP");
+@@ -298,6 +301,7 @@
+ {
+ filltable("CWORD");
+ syntax[0] = "CEOF";
++ syntax[1] = "CIGN";
+ #ifdef TARGET_CHAR
+ syntax[base + (TARGET_CHAR)CTLESC] = "CCTL";
+ syntax[base + (TARGET_CHAR)CTLVAR] = "CCTL";
+@@ -374,9 +378,9 @@
+
+ static char *macro[] = {
+ "#define is_digit(c)\t((is_type+SYNBASE)[c] & ISDIGIT)",
+- "#define is_alpha(c)\t((c) != UPEOF && ((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c)))",
+- "#define is_name(c)\t((c) != UPEOF && ((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))",
+- "#define is_in_name(c)\t((c) != UPEOF && ((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))",
++ "#define is_alpha(c)\t(((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c)))",
++ "#define is_name(c)\t(((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))",
++ "#define is_in_name(c)\t(((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))",
+ "#define is_special(c)\t((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))",
+ NULL
+ };
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/mktokens bin_NetBSD-1.6release/src/bin/sh/mktokens
+--- bin_NetBSD-1.6release.orig/src/bin/sh/mktokens 1999-07-09 12:02:07.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/mktokens 2003-02-08 14:35:42.000000000 +0000
+@@ -55,21 +55,22 @@
+ TENDBQUOTE 1 "`"
+ TREDIR 0 redirection
+ TWORD 0 word
+-TIF 0 "if"
+-TTHEN 1 "then"
+-TELSE 1 "else"
++TNOT 0 "!"
++TCASE 0 "case"
++TDO 1 "do"
++TDONE 1 "done"
+ TELIF 1 "elif"
++TELSE 1 "else"
++TESAC 1 "esac"
+ TFI 1 "fi"
+-TWHILE 0 "while"
+-TUNTIL 0 "until"
+ TFOR 0 "for"
+-TDO 1 "do"
+-TDONE 1 "done"
++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
+@@ -87,10 +88,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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/mystring.c bin_NetBSD-1.6release/src/bin/sh/mystring.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/mystring.c 1999-07-09 12:02:07.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/mystring.c 2003-02-08 14:35:42.000000000 +0000
+@@ -60,9 +60,16 @@
+ #include "syntax.h"
+ #include "error.h"
+ #include "mystring.h"
++#include "memalloc.h"
++#include "parser.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
+@@ -73,6 +80,7 @@
+ */
+
+
++#if 0
+ /*
+ * scopyn - copy a string from "from" to "to", truncating the string
+ * if necessary. "To" is always nul terminated, even if
+@@ -92,6 +100,7 @@
+ }
+ *to = '\0';
+ }
++#endif
+
+
+ /*
+@@ -122,7 +131,7 @@
+ {
+
+ if (! is_number(s))
+- error("Illegal number: %s", s);
++ error(illnum, s);
+ return atoi(s);
+ }
+
+@@ -142,3 +151,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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/mystring.h bin_NetBSD-1.6release/src/bin/sh/mystring.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/mystring.h 1995-10-14 00:44:16.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/mystring.h 2003-02-08 14:35:42.000000000 +0000
+@@ -40,10 +40,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 __P((const char *, char *, int));
++#endif
+ int prefix __P((const char *, const char *));
+ int number __P((const char *));
+ int is_number __P((const char *));
++char *single_quote __P((const char *));
++char *sstrdup __P((const char *));
++int pstrcmp __P((const void *, const void *));
++const char *const *findstring __P((const char *, const char *const *, size_t));
+
+ #define equal(s1, s2) (strcmp(s1, s2) == 0)
+ #define scopy(s1, s2) ((void)strcpy(s2, s1))
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/nodes.c.pat bin_NetBSD-1.6release/src/bin/sh/nodes.c.pat
+--- bin_NetBSD-1.6release.orig/src/bin/sh/nodes.c.pat 1997-04-12 12:18:24.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/nodes.c.pat 2003-02-08 14:35:42.000000000 +0000
+@@ -70,18 +70,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;
+ }
+
+
+@@ -144,6 +148,12 @@
+ nodesavestr(s)
+ char *s;
+ {
++#ifdef _GNU_SOURCE
++ char *rtn = funcstring;
++
++ funcstring = stpcpy(funcstring, s) + 1;
++ return rtn;
++#else
+ register char *p = s;
+ register char *q = funcstring;
+ char *rtn = funcstring;
+@@ -152,6 +162,7 @@
+ continue;
+ funcstring = q;
+ return rtn;
++#endif
+ }
+
+
+@@ -161,9 +172,8 @@
+ */
+
+ void
+-freefunc(n)
+- union node *n;
++freefunc(struct funcnode *f)
+ {
+- if (n)
+- ckfree(n);
++ if (f && --f->count < 0)
++ ckfree(f);
+ }
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/nodetypes bin_NetBSD-1.6release/src/bin/sh/nodetypes
+--- bin_NetBSD-1.6release.orig/src/bin/sh/nodetypes 2002-05-16 11:41:21.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/nodetypes 2003-02-08 14:35:42.000000000 +0000
+@@ -52,14 +52,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
+
+@@ -79,6 +74,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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/options.c bin_NetBSD-1.6release/src/bin/sh/options.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/options.c 2001-02-27 11:04:47.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/options.c 2003-02-08 14:35:42.000000000 +0000
+@@ -75,11 +75,51 @@
+
+ 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",
++ "quietprofile",
++};
++
++const char optletters[NOPTS] = {
++ 'e',
++ 'f',
++ 'I',
++ 'i',
++ 'm',
++ 'n',
++ 's',
++ 'x',
++ 'v',
++ 'V',
++ 'E',
++ 'C',
++ 'a',
++ 'b',
++ 'u',
++ 'q',
++};
++
++char optlist[NOPTS];
++
+
+ STATIC void options __P((int));
+ STATIC void minus_o __P((char *, int));
+ STATIC void setoption __P((int, int));
+-STATIC int getopts __P((char *, char *, char **, char ***, char **));
++STATIC int getopts __P((char *, char *, char **, int *, int *));
+
+
+ /*
+@@ -97,7 +137,7 @@
+ if (argc > 0)
+ argptr++;
+ for (i = 0; i < NOPTS; i++)
+- optlist[i].val = 2;
++ optlist[i] = 2;
+ options(1);
+ if (*argptr == NULL && minusc == NULL)
+ sflag = 1;
+@@ -106,21 +146,22 @@
+ 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;
+ arg0 = argv[0];
+- if (sflag == 0 && minusc == NULL) {
+- commandname = argv[0];
+- arg0 = *argptr++;
+- setinputfile(arg0, 0);
+- commandname = arg0;
+- }
+ /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
+- if (argptr && minusc && *argptr)
+- arg0 = *argptr++;
++ if (*argptr) {
++ char *p = *argptr++;
++ if (!minusc)
++ setinputfile(p, 0);
++ arg0 = p;
++ }
++ if (!sflag)
++ commandname = arg0;
+
+ shellparam.p = argptr;
+- shellparam.reset = 1;
++ shellparam.optind = 1;
++ shellparam.optoff = -1;
+ /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
+ while (*argptr) {
+ shellparam.nparam++;
+@@ -210,12 +251,12 @@
+ 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)) {
+- setoption(optlist[i].letter, val);
++ if (equal(name, optnames[i])) {
++ setoption(optletters[i], val);
+ return;
+ }
+ error("Illegal option -o %s", name);
+@@ -231,8 +272,8 @@
+ int i;
+
+ for (i = 0; i < NOPTS; i++)
+- if (optlist[i].letter == flag) {
+- optlist[i].val = val;
++ if (optletters[i] == flag) {
++ optlist[i] = val;
+ if (val) {
+ /* #%$ hack for ksh semantics */
+ if (flag == 'V')
+@@ -248,20 +289,6 @@
+
+
+
+-#ifdef mkinit
+-INCLUDE "options.h"
+-
+-SHELLPROC {
+- int i;
+-
+- for (i = 0; i < NOPTS; i++)
+- optlist[i].val = 0;
+- optschanged();
+-
+-}
+-#endif
+-
+-
+ /*
+ * Set the shell parameters.
+ */
+@@ -284,7 +311,8 @@
+ shellparam.malloc = 1;
+ shellparam.nparam = nparam;
+ shellparam.p = newparam;
+- shellparam.optnext = NULL;
++ shellparam.optind = 1;
++ shellparam.optoff = -1;
+ }
+
+
+@@ -332,7 +360,8 @@
+ }
+ ap2 = shellparam.p;
+ while ((*ap2++ = *ap1++) != NULL);
+- shellparam.optnext = NULL;
++ shellparam.optind = 1;
++ shellparam.optoff = -1;
+ INTON;
+ return 0;
+ }
+@@ -365,10 +394,8 @@
+ getoptsreset(value)
+ const char *value;
+ {
+- if (number(value) == 1) {
+- shellparam.optnext = NULL;
+- shellparam.reset = 1;
+- }
++ shellparam.optind = number(value);
++ shellparam.optoff = -1;
+ }
+
+ /*
+@@ -387,50 +414,58 @@
+
+ if (argc < 3)
+ error("Usage: getopts optstring var [arg]");
+- else if (argc == 3)
++ 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(optstr, optvar, optfirst, optnext, optpptr)
++getopts(optstr, optvar, optfirst, optind, optoff)
+ char *optstr;
+ char *optvar;
+ char **optfirst;
+- char ***optnext;
+- char **optpptr;
++ int *optind;
++ int *optoff;
+ {
+ char *p, *q;
+ char c = '?';
+ int done = 0;
+- int ind = 0;
+ int err = 0;
+ char s[10];
++ char **optnext = optfirst + *optind - 1;
+
+- if ((p = *optpptr) == NULL || *p == '\0') {
++ if (*optind <= 1 || *optoff < 0 || !(*(optnext - 1)) ||
++ strlen(*(optnext - 1)) < *optoff)
++ p = NULL;
++ else
++ p = *(optnext - 1) + *optoff;
++ if (p == NULL || *p == '\0') {
+ /* Current word is done, advance */
+- if (*optnext == NULL)
++ if (optnext == NULL)
+ return 1;
+- p = **optnext;
++ p = *optnext;
+ if (p == NULL || *p != '-' || *++p == '\0') {
+ atend:
+- ind = *optnext - optfirst + 1;
+- *optnext = NULL;
++ *optind = optnext - optfirst + 1;
+ p = NULL;
+ done = 1;
+ goto out;
+ }
+- (*optnext)++;
++ optnext++;
+ if (p[0] == '-' && p[1] == '\0') /* check for "--" */
+ goto atend;
+ }
+@@ -455,7 +490,7 @@
+ }
+
+ if (*++q == ':') {
+- if (*p == '\0' && (p = **optnext) == NULL) {
++ if (*p == '\0' && (p = *optnext) == NULL) {
+ if (optstr[0] == ':') {
+ s[0] = c;
+ s[1] = '\0';
+@@ -470,30 +505,29 @@
+ goto bad;
+ }
+
+- if (p == **optnext)
+- (*optnext)++;
++ if (p == *optnext)
++ optnext++;
+ setvarsafe("OPTARG", p, 0);
+ p = NULL;
+ }
+ else
+- setvarsafe("OPTARG", "", 0);
+- ind = *optnext - optfirst + 1;
++ setvarsafe("OPTARG", nullstr, 0);
++ *optind = optnext - optfirst + 1;
+ goto out;
+
+ bad:
+- ind = 1;
+- *optnext = NULL;
++ *optind = 1;
+ p = NULL;
+ out:
+- *optpptr = p;
+- fmtstr(s, sizeof(s), "%d", ind);
++ *optoff = p ? p - *(optnext - 1) : -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);
+ }
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/options.h bin_NetBSD-1.6release/src/bin/sh/options.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/options.h 2001-02-05 11:15:31.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/options.h 2003-02-08 14:35:42.000000000 +0000
+@@ -41,61 +41,34 @@
+ 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 */
++ int optind; /* next parameter to be processed by getopts */
++ int optoff; /* used by getopts */
+ };
+
+
+
+-#define eflag optlist[0].val
+-#define fflag optlist[1].val
+-#define Iflag optlist[2].val
+-#define iflag optlist[3].val
+-#define mflag optlist[4].val
+-#define nflag optlist[5].val
+-#define sflag optlist[6].val
+-#define xflag optlist[7].val
+-#define vflag optlist[8].val
+-#define Vflag optlist[9].val
+-#define Eflag optlist[10].val
+-#define Cflag optlist[11].val
+-#define aflag optlist[12].val
+-#define bflag optlist[13].val
+-#define uflag optlist[14].val
+-#define qflag optlist[15].val
++#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 qflag optlist[15]
+
+ #define NOPTS 16
+
+-struct optent {
+- const char *name;
+- const char letter;
+- char val;
+-};
+-
+-#ifdef DEFINE_OPTIONS
+-struct optent optlist[NOPTS] = {
+- { "errexit", 'e', 0 },
+- { "noglob", 'f', 0 },
+- { "ignoreeof", 'I', 0 },
+- { "interactive",'i', 0 },
+- { "monitor", 'm', 0 },
+- { "noexec", 'n', 0 },
+- { "stdin", 's', 0 },
+- { "xtrace", 'x', 0 },
+- { "verbose", 'v', 0 },
+- { "vi", 'V', 0 },
+- { "emacs", 'E', 0 },
+- { "noclobber", 'C', 0 },
+- { "allexport", 'a', 0 },
+- { "notify", 'b', 0 },
+- { "nounset", 'u', 0 },
+- { "quietprofile", 'q', 0 },
+-};
+-#else
+-extern struct optent optlist[NOPTS];
+-#endif
++extern const char optletters[NOPTS];
++extern char optlist[NOPTS];
+
+
+ extern char *minusc; /* argument to -c option */
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/output.c bin_NetBSD-1.6release/src/bin/sh/output.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/output.c 2002-04-09 11:25:14.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/output.c 2003-02-08 14:35:42.000000000 +0000
+@@ -1,4 +1,4 @@
+-/* $NetBSD: output.c,v 1.25 2002/04/09 00:52:05 thorpej Exp $ */
++/* $NetBSD: output.c,v 1.26 2002/05/25 23:09:06 wiz Exp $ */
+
+ /*-
+ * Copyright (c) 1991, 1993
+@@ -41,7 +41,7 @@
+ #if 0
+ static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95";
+ #else
+-__RCSID("$NetBSD: output.c,v 1.25 2002/04/09 00:52:05 thorpej Exp $");
++__RCSID("$NetBSD: output.c,v 1.26 2002/05/25 23:09:06 wiz Exp $");
+ #endif
+ #endif /* not lint */
+
+@@ -65,6 +65,10 @@
+ #include <errno.h>
+ #include <unistd.h>
+ #include <stdlib.h>
++#ifdef USE_GLIBC_STDIO
++#include <fcntl.h>
++#endif
++#include <limits.h>
+
+ #include "shell.h"
+ #include "syntax.h"
+@@ -74,69 +78,126 @@
+
+
+ #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};
++#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
++};
++#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
+
+
+-#ifdef notdef /* no longer used */
+-/*
+- * Set up an output file to write to memory rather than a file.
+- */
+-
+-void
+-open_mem(block, length, file)
+- char *block;
+- int length;
+- struct output *file;
+- {
+- file->nextc = block;
+- file->nleft = --length;
+- file->fd = BLOCK_OUT;
+- file->flags = 0;
+-}
+-#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;
++ }
+
+-void
+-out1str(p)
+- const char *p;
+- {
+- outstr(p, out1);
+-}
++ 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;
+
+-void
+-out2str(p)
+- const char *p;
+- {
+- outstr(p, out2);
++ if ((xwrite(dest->fd, p, len))) {
++err:
++ dest->flags |= OUTPUT_ERR;
++ }
+ }
++#endif
+
+
+ void
+@@ -144,51 +205,36 @@
+ const char *p;
+ struct output *file;
+ {
+- while (*p)
+- outc(*p++, file);
+- if (file == out2)
+- flushout(file);
++#ifdef USE_GLIBC_STDIO
++ INTOFF;
++ fputs(p, file->stream);
++ INTON;
++#else
++ size_t len;
++
++ len = strlen(p);
++ __outstr(p, len, file);
++#endif
+ }
+
+
+-char out_junk[16];
++#ifndef USE_GLIBC_STDIO
+
+
+ void
+-emptyoutbuf(dest)
+- 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--;
++outcslow(char c, struct output *dest)
++{
++ __outstr(&c, 1, dest);
+ }
++#endif
+
+
+ void
+ flushall() {
+ flushout(&output);
++#ifdef FLUSHERR
+ flushout(&errout);
++#endif
+ }
+
+
+@@ -196,343 +242,93 @@
+ flushout(dest)
+ struct output *dest;
+ {
++#ifdef USE_GLIBC_STDIO
++ INTOFF;
++ fflush(dest->stream);
++ INTON;
++#else
++ size_t len;
+
+- if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
++ len = dest->nextc - dest->buf;
++ if (dest->buf == NULL || !len || 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() {
+- INTOFF;
+- if (output.buf) {
+- ckfree(output.buf);
+- output.buf = NULL;
+- output.nleft = 0;
+- }
+- INTON;
++ if ((xwrite(dest->fd, dest->buf, len)))
++ dest->flags |= OUTPUT_ERR;
++#endif
+ }
+
+
+ void
+-#ifdef __STDC__
+ outfmt(struct output *file, const char *fmt, ...)
+-#else
+-void
+-outfmt(va_alist)
+- va_dcl
+-#endif
+ {
+ va_list ap;
+-#ifndef __STDC__
+- struct output *file;
+- const char *fmt;
+
+- va_start(ap);
+- file = va_arg(ap, struct output *);
+- fmt = va_arg(ap, const char *);
+-#else
+ va_start(ap, fmt);
+-#endif
+ doformat(file, fmt, ap);
+ va_end(ap);
+ }
+
+
+ void
+-#ifdef __STDC__
+ out1fmt(const char *fmt, ...)
+-#else
+-out1fmt(va_alist)
+- va_dcl
+-#endif
+ {
+ va_list ap;
+-#ifndef __STDC__
+- const char *fmt;
+
+- va_start(ap);
+- fmt = va_arg(ap, const char *);
+-#else
+ va_start(ap, fmt);
+-#endif
+ doformat(out1, fmt, ap);
+ va_end(ap);
+ }
+
+-void
+-#ifdef __STDC__
+-dprintf(const char *fmt, ...)
+-#else
+-dprintf(va_alist)
+- va_dcl
+-#endif
+-{
+- va_list ap;
+-#ifndef __STDC__
+- const char *fmt;
+-
+- va_start(ap);
+- fmt = va_arg(ap, const char *);
+-#else
+- va_start(ap, fmt);
+-#endif
+- doformat(out2, fmt, ap);
+- va_end(ap);
+- flushout(out2);
+-}
+
+-void
+-#ifdef __STDC__
++int
+ fmtstr(char *outbuf, size_t length, const char *fmt, ...)
+-#else
+-fmtstr(va_alist)
+- va_dcl
+-#endif
+ {
+ va_list ap;
+- struct output strout;
+-#ifndef __STDC__
+- char *outbuf;
+- size_t length;
+- const char *fmt;
++ int ret;
+
+- va_start(ap);
+- outbuf = va_arg(ap, char *);
+- length = va_arg(ap, size_t);
+- fmt = va_arg(ap, const char *);
+-#else
+ va_start(ap, fmt);
+-#endif
+- 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';
++ ret = xvsnprintf(outbuf, length, fmt, ap);
+ va_end(ap);
++ return ret;
+ }
+
+-/*
+- * 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
+
++#ifndef USE_GLIBC_STDIO
+ void
+ doformat(dest, f, ap)
+ struct output *dest;
+ const char *f; /* format string */
+ va_list ap;
+ {
+-#if HAVE_VASPRINTF
++ struct stackmark smark;
+ char *s;
++ int len, ret;
++ size_t size;
++ va_list ap2;
+
+- 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++;
++ 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;
+ }
+- 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;
++ if (len < size) {
++ dest->nextc += len;
++ return;
+ }
+- 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);
++ setstackmark(&smark);
++ s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1);
++ ret = xvsnprintf(s, len + 1, f, ap);
++ if (ret == len)
++ __outstr(s, len, dest);
+ 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 */
++ dest->flags |= OUTPUT_ERR;
++ popstackmark(&smark);
+ }
++#endif
+
+
+
+@@ -541,47 +337,63 @@
+ */
+
+ int
+-xwrite(fd, buf, nbytes)
+- int fd;
+- char *buf;
+- int nbytes;
+- {
+- int ntry;
+- int i;
+- int n;
++xwrite(int fd, const void *p, size_t n)
++{
++ const char *buf = p;
+
+- 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) {
++ 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;
+ }
+
+
+-/*
+- * Version of ioctl that retries after a signal is caught.
+- * XXX unused function
+- */
++#ifdef notyet
++#ifdef USE_GLIBC_STDIO
++void initstreams() {
++ output.stream = stdout;
++ errout.stream = stderr;
++}
++
++
++void
++openmemout() {
++ INTOFF;
++ memout.stream = open_memstream(&memout.buf, &memout.bufsize);
++ INTON;
++}
++
+
+ int
+-xioctl(fd, request, arg)
+- int fd;
+- unsigned long request;
+- char * arg;
++__closememout() {
++ 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 i;
++ int ret;
+
+- while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
+- return i;
++ INTOFF;
++ ret = vsnprintf(outbuf, length, fmt, ap);
++ INTON;
++ return ret;
+ }
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/output.h bin_NetBSD-1.6release/src/bin/sh/output.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/output.h 1998-01-31 18:28:11.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/output.h 2003-02-08 14:35:42.000000000 +0000
+@@ -1,4 +1,4 @@
+-/* $NetBSD: output.h,v 1.14 1998/01/31 12:37:55 christos Exp $ */
++/* $NetBSD: output.h,v 1.15 2002/05/25 23:13:26 wiz Exp $ */
+
+ /*-
+ * Copyright (c) 1991, 1993
+@@ -40,50 +40,76 @@
+
+ #ifndef OUTPUT_INCL
+
+-#ifdef __STDC__
+ #include <stdarg.h>
+-#else
+-#include <varargs.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;
++#ifdef notyet
+ extern struct output memout;
++#endif
+ extern struct output *out1;
+ extern struct output *out2;
+
+-void open_mem __P((char *, int, struct output *));
+-void out1str __P((const char *));
+-void out2str __P((const char *));
+ void outstr __P((const char *, struct output *));
+-void emptyoutbuf __P((struct output *));
++#ifndef USE_GLIBC_STDIO
++void outcslow __P((char, struct output *));
++#endif
+ void flushall __P((void));
+ void flushout __P((struct output *));
+-void freestdout __P((void));
+ void outfmt __P((struct output *, const char *, ...))
+ __attribute__((__format__(__printf__,2,3)));
+ void out1fmt __P((const char *, ...))
+ __attribute__((__format__(__printf__,1,2)));
+-void dprintf __P((const char *, ...))
+- __attribute__((__format__(__printf__,1,2)));
+-void fmtstr __P((char *, size_t, const char *, ...))
++int fmtstr __P((char *, size_t, const char *, ...))
+ __attribute__((__format__(__printf__,3,4)));
++#ifndef USE_GLIBC_STDIO
+ void doformat __P((struct output *, const char *, va_list));
+-int xwrite __P((int, char *, int));
+-int xioctl __P((int, unsigned long, char *));
++#endif
++int xwrite __P((int, const void *, size_t));
++#ifdef notyet
++#ifdef USE_GLIBC_STDIO
++void initstreams __P((void));
++void openmemout __P((void));
++int __closememout __P((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) outc((c), out2)
++#define out1str(s) outstr((s), out1)
++#define out2str(s) outstr((s), out2)
++#define outerr(f) ((f)->flags & OUTPUT_ERR)
+
+ #define OUTPUT_INCL
+ #endif
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/parser.c bin_NetBSD-1.6release/src/bin/sh/parser.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/parser.c 2002-05-16 11:41:21.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/parser.c 2003-02-08 14:35:42.000000000 +0000
+@@ -53,6 +53,7 @@
+ #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"
+@@ -63,6 +64,7 @@
+ #include "mystring.h"
+ #include "alias.h"
+ #include "show.h"
++#include "builtins.h"
+ #ifndef SMALL
+ #include "myhistedit.h"
+ #endif
+@@ -76,8 +78,6 @@
+ /* values returned by readtoken */
+ #include "token.h"
+
+-#define OPENBRACE '{'
+-#define CLOSEBRACE '}'
+
+
+ struct heredoc {
+@@ -89,7 +89,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 */
+@@ -97,7 +96,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;
+@@ -109,7 +108,7 @@
+ STATIC union node *andor __P((void));
+ STATIC union node *pipeline __P((void));
+ STATIC union node *command __P((void));
+-STATIC union node *simplecmd __P((union node **, union node *));
++STATIC union node *simplecmd __P((void));
+ STATIC union node *makename __P((void));
+ STATIC void parsefname __P((void));
+ STATIC void parseheredoc __P((void));
+@@ -123,6 +122,22 @@
+ STATIC void setprompt __P((int));
+
+
++static inline int
++goodname(const char *p)
++{
++ return !*endofname(p);
++}
++
++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.)
+@@ -136,9 +151,7 @@
+ tokpushback = 0;
+ doprompt = interact;
+ if (doprompt)
+- setprompt(1);
+- else
+- setprompt(0);
++ setprompt(doprompt);
+ needprompt = 0;
+ t = readtoken();
+ if (t == TEOF)
+@@ -157,25 +170,25 @@
+ 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;
++ 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) {
+ n1 = n2;
+@@ -195,12 +208,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 +224,7 @@
+ pungetc(); /* push back EOF on input */
+ return n1;
+ default:
+- if (nlflag)
++ if (nlflag == 1)
+ synexpect(-1);
+ tokpushback++;
+ return n1;
+@@ -236,6 +249,7 @@
+ tokpushback++;
+ return n1;
+ }
++ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ n2 = pipeline();
+ n3 = (union node *)stalloc(sizeof (struct nbinary));
+ n3->type = t;
+@@ -255,8 +269,10 @@
+
+ negate = 0;
+ TRACE(("pipeline: entered\n"));
+- while (readtoken() == TNOT)
++ if (readtoken() == TNOT) {
+ negate = !negate;
++ checkkwd = CHKKWD | CHKALIAS;
++ } else
+ tokpushback++;
+ n1 = command();
+ if (readtoken() == TPIPE) {
+@@ -269,6 +285,7 @@
+ do {
+ prev = lp;
+ lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
++ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ lp->n = command();
+ prev->next = lp;
+ } while (readtoken() == TPIPE);
+@@ -293,28 +310,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;
+@@ -338,9 +343,7 @@
+ n2->nif.elsepart = NULL;
+ tokpushback++;
+ }
+- if (readtoken() != TFI)
+- synexpect(TFI);
+- checkkwd = 1;
++ t = TFI;
+ break;
+ case TWHILE:
+ case TUNTIL: {
+@@ -353,9 +356,7 @@
+ synexpect(TDO);
+ }
+ n1->nbinary.ch2 = list(0);
+- if (readtoken() != TDONE)
+- synexpect(TDONE);
+- checkkwd = 1;
++ t = TDONE;
+ break;
+ }
+ case TFOR:
+@@ -364,7 +365,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));
+@@ -379,11 +381,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;
+@@ -394,17 +394,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));
+@@ -416,13 +410,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;
+@@ -431,73 +430,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;
++ cpp = &cp->nclist.next;
++
++ checkkwd = CHKNL | CHKKWD;
+ if ((t = readtoken()) != TESAC) {
+- if (t != TENDCASE) {
+- noalias = 0;
++ if (t != TENDCASE)
+ synexpect(TENDCASE);
+- } else {
+- noalias = 1;
+- checkkwd = 2;
+- readtoken();
++ else
++ goto next_case;
+ }
+ }
+- cpp = &cp->nclist.next;
+- } while(lasttoken != TESAC);
+- noalias = 0;
+ *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;
+@@ -515,92 +493,87 @@
+ 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;
+ }
+
+
+ STATIC union node *
+-simplecmd(rpp, redir)
+- union node **rpp, *redir;
+- {
++simplecmd() {
+ 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;
+-
+- while (readtoken() == TNOT) {
+- TRACE(("command: TNOT recognized\n"));
+- negate = !negate;
+- }
+- tokpushback++;
++ vars = NULL;
++ vpp = &vars;
++ redir = NULL;
++ rpp = &redir;
+
++ 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;
++ if (savecheckkwd && isassignment(wordtext)) {
++ *vpp = n;
++ vpp = &n->narg.next;
++ } else {
+ *app = n;
+ app = &n->narg.next;
+- } else if (lasttoken == TREDIR) {
++ 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) {
++ 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);
+-#ifdef notdef
+- if (! goodname(n->narg.text))
++ name = n->narg.text;
++ if (
++ !goodname(name) || (
++ (bcmd = find_builtin(name)) &&
++ bcmd->flags & BUILTIN_SPECIAL
++ )
++ )
+ synerror("Bad function name");
+-#endif
+ n->type = NDEFUN;
++ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ n->narg.next = command();
+- goto checkneg;
+- } else {
++ return n;
++ }
++ /* fall through */
++ default:
+ tokpushback++;
+- break;
++ 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;
+ }
+
+@@ -682,9 +655,10 @@
+ struct heredoc *here;
+ union node *n;
+
+- while (heredoclist) {
+ here = heredoclist;
+- heredoclist = here->next;
++ heredoclist = 0;
++
++ while (here) {
+ if (needprompt) {
+ setprompt(2);
+ needprompt = 0;
+@@ -697,6 +671,7 @@
+ n->narg.text = wordtext;
+ n->narg.backquote = backquotelist;
+ here->here->nhere.doc = n;
++ here = here->next;
+ }
+ }
+
+@@ -712,53 +687,51 @@
+ STATIC int
+ readtoken() {
+ 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;
++ if (checkkwd & CHKNL) {
+ while (t == TNL) {
+ parseheredoc();
+ t = xxreadtoken();
+ }
+- } else
+- checkkwd = 0;
++ }
++
++ if (t != TWORD || quoteflag) {
++ goto out;
++ }
++
+ /*
+- * check for keywords and aliases
++ * check for keywords
+ */
+- if (t == TWORD && !quoteflag)
+- {
++ if (checkkwd & CHKKWD) {
+ const char *const *pp;
+
+- for (pp = parsekwd; *pp; pp++) {
+- if (**pp == *wordtext && equal(*pp, wordtext))
+- {
+- lasttoken = t = pp -
+- parsekwd + KWDOFFSET;
++ if ((pp = findkwd(wordtext))) {
++ lasttoken = t = pp - parsekwd + KWDOFFSET;
+ TRACE(("keyword %s recognized\n", tokname[t]));
+ goto out;
+ }
+ }
+- if(!noalias &&
+- (ap = lookupalias(wordtext, 1)) != NULL) {
+- pushstring(ap->val, strlen(ap->val), ap);
+- checkkwd = savecheckkwd;
++
++ if (checkkwd & CHKALIAS) {
++ struct alias *ap;
++ if ((ap = lookupalias(wordtext, 1)) != NULL) {
++ if (*ap->val) {
++ pushstring(ap->val, ap);
++ }
+ goto top;
+ }
+ }
+ out:
+- checkkwd = (t == TNOT) ? savecheckkwd : 0;
+- }
++ checkkwd = 0;
+ #ifdef DEBUG
+ if (!alreadyseen)
+ TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
+@@ -804,10 +777,9 @@
+ 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);
+@@ -818,8 +790,6 @@
+ startlinno = ++plinno;
+ if (doprompt)
+ setprompt(2);
+- else
+- setprompt(0);
+ continue;
+ }
+ pungetc();
+@@ -879,28 +849,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(firstc, syntax, eofmark, striptabs)
+ int firstc;
+@@ -914,24 +862,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;
+@@ -939,14 +885,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 */
+@@ -962,7 +908,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)
+@@ -971,82 +917,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;
++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()) {
++ if (dqvarnest == 0) {
+ syntax = BASESYNTAX;
+- CLRDBLQUOTE();
+- } else {
+- syntax = DQSYNTAX;
+- SETDBLQUOTE();
+- USTPUTC(CTLQUOTEMARK, out);
+- }
++ 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);
+@@ -1066,9 +1006,9 @@
+ USTPUTC(CTLENDARI, out);
+ syntax = prevsyntax;
+ if (syntax == DQSYNTAX)
+- SETDBLQUOTE();
++ dblquote = 1;
+ else
+- CLRDBLQUOTE();
++ dblquote = 0;
+ } else
+ USTPUTC(')', out);
+ } else {
+@@ -1086,11 +1026,15 @@
+ break;
+ case CEOF:
+ goto endword; /* exit outer loop */
++ case CIGN:
++ break;
+ default:
+ if (varnest == 0)
+ goto endword; /* exit outer loop */
++ if (c != PEOA) {
+ USTPUTC(c, out);
+ }
++ }
+ c = pgetc_macro();
+ }
+ }
+@@ -1122,8 +1066,6 @@
+ backquotelist = bqlist;
+ grabstackblock(len);
+ wordtext = out;
+- if (dblquotep != NULL)
+- ckfree(dblquotep);
+ return lasttoken = TWORD;
+ /* end of readtoken routine */
+
+@@ -1137,9 +1079,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) {
+@@ -1152,7 +1098,7 @@
+ plinno++;
+ needprompt = doprompt;
+ } else {
+- pushstring(line, strlen(line), NULL);
++ pushstring(line, NULL);
+ }
+ }
+ }
+@@ -1238,7 +1184,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)) */
+@@ -1253,10 +1202,10 @@
+ typeloc = out - 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;
+@@ -1264,11 +1213,11 @@
+ 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);
+@@ -1313,15 +1262,13 @@
+ } else {
+ pungetc();
+ }
+- if (ISDBLQUOTE() || arinest)
++ if (dblquote || arinest)
+ flags |= VSQUOTE;
+ *(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++;
+ }
+ }
+ }
+@@ -1343,7 +1290,7 @@
+ char *volatile str;
+ struct jmploc jmploc;
+ struct jmploc *volatile savehandler;
+- int savelen;
++ size_t savelen;
+ int saveprompt;
+ #ifdef __GNUC__
+ (void) &saveprompt;
+@@ -1373,7 +1320,7 @@
+ reread it as input, interpreting it normally. */
+ char *pout;
+ int pc;
+- int psavelen;
++ size_t psavelen;
+ char *pstr;
+
+
+@@ -1392,8 +1339,6 @@
+ plinno++;
+ if (doprompt)
+ setprompt(2);
+- else
+- setprompt(0);
+ /*
+ * If eating a newline, avoid putting
+ * the newline into the new character
+@@ -1403,18 +1348,21 @@
+ continue;
+ }
+ if (pc != '\\' && pc != '`' && pc != '$'
+- && (!ISDBLQUOTE() || pc != '"'))
++ && (!dblquote || pc != '"'))
+ STPUTC('\\', pout);
++ if (pc > PEOA) {
+ break;
+-
+- case '\n':
+- plinno++;
+- needprompt = doprompt;
+- break;
++ }
++ /* fall through */
+
+ case PEOF:
++ case PEOA:
+ startlinno = plinno;
+ synerror("EOF in backquote substitution");
++
++ case '\n':
++ plinno++;
++ needprompt = doprompt;
+ break;
+
+ default:
+@@ -1427,7 +1375,7 @@
+ psavelen = pout - stackblock();
+ if (psavelen > 0) {
+ pstr = grabstackstr(pout);
+- setinputstring(pstr, 1);
++ setinputstring(pstr);
+ }
+ }
+ nlpp = &bqlist;
+@@ -1442,7 +1390,7 @@
+ doprompt = 0;
+ }
+
+- n = list(0);
++ n = list(2);
+
+ if (oldstyle)
+ doprompt = saveprompt;
+@@ -1473,7 +1421,7 @@
+ }
+ parsebackquote = savepbq;
+ handler = savehandler;
+- if (arinest || ISDBLQUOTE())
++ if (arinest || dblquote)
+ USTPUTC(CTLBACKQ | CTLQUOTE, out);
+ else
+ USTPUTC(CTLBACKQ, out);
+@@ -1492,7 +1440,7 @@
+ prevsyntax = syntax;
+ syntax = ARISYNTAX;
+ USTPUTC(CTLARI, out);
+- if (ISDBLQUOTE())
++ if (dblquote)
+ USTPUTC('"',out);
+ else
+ USTPUTC(' ',out);
+@@ -1511,6 +1459,7 @@
+
+
+ #ifdef mkinit
++INCLUDE "parser.h"
+ RESET {
+ tokpushback = 0;
+ checkkwd = 0;
+@@ -1543,23 +1492,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;
+ }
+
+
+@@ -1587,13 +1536,9 @@
+
+
+ STATIC void
+-synerror(msg)
+- const char *msg;
+- {
+- if (commandname)
+- outfmt(&errout, "%s: %d: ", commandname, startlinno);
+- outfmt(&errout, "Syntax error: %s\n", msg);
+- error((char *)NULL);
++synerror(const char *msg)
++{
++ error("Syntax error: %s", msg);
+ /* NOTREACHED */
+ }
+
+@@ -1618,7 +1563,7 @@
+ {
+ switch (whichprompt) {
+ case 0:
+- return "";
++ return nullstr;
+ case 1:
+ return ps1val();
+ case 2:
+@@ -1627,3 +1572,10 @@
+ return "<internal prompt error>";
+ }
+ }
++
++const char *const *
++findkwd(const char *s) {
++ return findstring(
++ s, parsekwd, sizeof(parsekwd) / sizeof(const char *)
++ );
++}
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/parser.h bin_NetBSD-1.6release/src/bin/sh/parser.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/parser.h 2001-01-12 16:50:39.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/parser.h 2003-02-08 14:35:42.000000000 +0000
+@@ -60,12 +60,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
+@@ -75,9 +80,12 @@
+ 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 *);
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/redir.c bin_NetBSD-1.6release/src/bin/sh/redir.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/redir.c 2002-05-16 11:41:22.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/redir.c 2003-02-08 14:35:42.000000000 +0000
+@@ -45,6 +45,7 @@
+ #endif
+ #endif /* not lint */
+
++#include <sys/stat.h>
+ #include <sys/types.h>
+ #include <sys/param.h> /* PIPE_BUF */
+ #include <signal.h>
+@@ -80,21 +81,22 @@
+ MKINIT
+ struct redirtab {
+ struct redirtab *next;
+- short renamed[10];
++ int renamed[10];
++ int nullredirs;
+ };
+
+
+ MKINIT struct redirtab *redirlist;
++MKINIT int nullredirs;
+
+-/*
+- * 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 __P((union node *, char[10 ]));
++STATIC int openredirect __P((union node *));
++#ifdef notyet
++STATIC void dupredirect __P((union node *, int, char[10 ]));
++#else
++STATIC void dupredirect __P((union node *, int));
++#endif
+ STATIC int openhere __P((union node *));
++STATIC int noclobberopen __P((const char *));
+
+
+ /*
+@@ -111,137 +113,173 @@
+ int flags;
+ {
+ union node *n;
+- struct redirtab *sv = NULL;
++ struct redirtab *sv;
+ int i;
+ int fd;
+- int try;
++ 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
++ if (!redir) {
++ if (flags & REDIR_PUSH)
++ nullredirs++;
++ return;
++ }
++ sv = NULL;
++ INTOFF;
+ if (flags & REDIR_PUSH) {
+- sv = ckmalloc(sizeof (struct redirtab));
++ struct redirtab *q;
++ q = ckmalloc(sizeof (struct redirtab));
++ q->next = redirlist;
++ redirlist = q;
++ q->nullredirs = nullredirs;
+ for (i = 0 ; i < 10 ; i++)
+- sv->renamed[i] = EMPTY;
+- sv->next = redirlist;
+- redirlist = sv;
++ q->renamed[i] = EMPTY;
++ nullredirs = 0;
++ sv = q;
+ }
+- for (n = redir ; n ; n = n->nfile.next) {
++ n = redir;
++ do {
+ 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));
++ 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);
++ error("%d: %s", fd, m);
+ /* NOTREACHED */
+ }
+- }
+- if (!try) {
+- sv->renamed[fd] = i;
++ } else {
++ *p = i;
+ close(fd);
+ }
+- INTON;
+ } else {
+ close(fd);
+ }
+- if (fd == 0)
+- fd0_redirected++;
+- if (!try)
+- openredirect(n, memory);
+- }
++#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
+ }
+
+
+-STATIC void
+-openredirect(redir, memory)
++STATIC int
++openredirect(redir)
+ 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)
++ if ((f = open64(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)
++ if ((f = open64(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
+ goto ecreate;
+ break;
+ case NTO:
+- if (Cflag)
+- flags |= O_EXCL;
++ /* 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 = open(fname, flags, 0666)) < 0)
++ if ((f = open64(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+ goto ecreate;
+ break;
+ case NAPPEND:
+ fname = redir->nfile.expfname;
+- if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
++ 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:
++ error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
++eopen:
++ 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);
+ }
+- 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));
+ }
+
+
+@@ -256,7 +294,7 @@
+ union node *redir;
+ {
+ int pip[2];
+- int len = 0;
++ size_t len = 0;
+
+ if (pipe(pip) < 0)
+ error("Pipe call failed");
+@@ -294,24 +332,26 @@
+ */
+
+ void
+-popredir() {
++popredir(int drop) {
+ struct redirtab *rp = redirlist;
+ int i;
+
++ INTOFF;
++ if (--nullredirs >= 0)
++ goto out;
+ for (i = 0 ; i < 10 ; i++) {
+ if (rp->renamed[i] != EMPTY) {
+- if (i == 0)
+- fd0_redirected--;
++ if (!drop) {
+ close(i);
+- if (rp->renamed[i] >= 0) {
+ copyfd(rp->renamed[i], i);
+- close(rp->renamed[i]);
+ }
++ close(rp->renamed[i]);
+ }
+ }
+- INTOFF;
+ redirlist = rp->next;
++ nullredirs = rp->nullredirs;
+ ckfree(rp);
++out:
+ INTON;
+ }
+
+@@ -324,38 +364,22 @@
+ INCLUDE "redir.h"
+
+ RESET {
+- while (redirlist)
+- popredir();
+-}
+-
+-SHELLPROC {
+- clearredir();
++ 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() {
+- 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]);
+- }
+- rp->renamed[i] = EMPTY;
+- }
++clearredir(int drop) {
++ for (;;) {
++ nullredirs = 0;
++ if (!redirlist)
++ break;
++ popredir(drop);
+ }
+ }
+
+@@ -368,18 +392,104 @@
+ */
+
+ int
+-copyfd(from, to)
+- int from;
+- int to;
++copyfd(int from, int to)
+ {
+ int newfd;
+
+ newfd = fcntl(from, F_DUPFD, to);
+ if (newfd < 0) {
+- if (errno == EMFILE)
++ int errno2 = errno;
++ if (errno2 == EMFILE)
+ return EMPTY;
+ else
+- error("%d: %s", from, strerror(errno));
++ 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;
++ int e;
++ volatile int saveint;
++ struct jmploc *volatile savehandler = handler;
++ struct jmploc jmploc;
++
++ SAVEINT(saveint);
++ exception = -1;
++ if (setjmp(jmploc.loc))
++ err = 2;
++ else {
++ handler = &jmploc;
++ redirect(redir, flags);
++ err = 0;
++ }
++ handler = savehandler;
++ e = exception;
++ if (e >= 0 && e != EXERROR)
++ longjmp(handler->loc, 1);
++ RESTOREINT(saveint);
++ return err;
++}
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/redir.h bin_NetBSD-1.6release/src/bin/sh/redir.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/redir.h 2000-05-23 11:03:19.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/redir.h 2003-02-08 14:35:42.000000000 +0000
+@@ -40,12 +40,14 @@
+
+ /* 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 */
++#endif
+
+ union node;
+ void redirect __P((union node *, int));
+-void popredir __P((void));
+-int fd0_redirected_p __P((void));
+-void clearredir __P((void));
++void popredir __P((int));
++void clearredir __P((int));
+ int copyfd __P((int, int));
++int redirectsafe __P((union node *, int));
+
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/regularbltins.def bin_NetBSD-1.6release/src/bin/sh/regularbltins.def
+--- bin_NetBSD-1.6release.orig/src/bin/sh/regularbltins.def 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/regularbltins.def 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,16 @@
++alias
++bg
++cd
++command
++false
++fc
++fg
++getopts
++jobs
++kill
++newgrp
++read
++true
++umask
++unalias
++wait
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/setmode.c bin_NetBSD-1.6release/src/bin/sh/setmode.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/setmode.c 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/setmode.c 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,486 @@
++/* $NetBSD: setmode.c,v 1.28 2000/01/25 15:43:43 enami Exp $ */
++
++/*
++ * Copyright (c) 1989, 1993, 1994
++ * The Regents of the University of California. All rights reserved.
++ *
++ * This code is derived from software contributed to Berkeley by
++ * Dave Borman at Cray Research, 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. 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.
++ */
++
++#include <sys/cdefs.h>
++#if defined(LIBC_SCCS) && !defined(lint)
++#if 0
++static char sccsid[] = "@(#)setmode.c 8.2 (Berkeley) 3/25/94";
++#else
++__RCSID("$NetBSD: setmode.c,v 1.28 2000/01/25 15:43:43 enami Exp $");
++#endif
++#endif /* LIBC_SCCS and not lint */
++
++#include <sys/types.h>
++#include <sys/stat.h>
++
++#include <assert.h>
++#include <ctype.h>
++#include <errno.h>
++#include <signal.h>
++#include <stdlib.h>
++#include <unistd.h>
++
++#ifdef SETMODE_DEBUG
++#include <stdio.h>
++#endif
++
++#ifdef __weak_alias
++__weak_alias(getmode,_getmode)
++__weak_alias(setmode,_setmode)
++#endif
++
++#ifdef __GLIBC__
++#define S_ISTXT __S_ISVTX
++#endif
++
++#define SET_LEN 6 /* initial # of bitcmd struct to malloc */
++#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */
++
++typedef struct bitcmd {
++ char cmd;
++ char cmd2;
++ mode_t bits;
++} BITCMD;
++
++#define CMD2_CLR 0x01
++#define CMD2_SET 0x02
++#define CMD2_GBITS 0x04
++#define CMD2_OBITS 0x08
++#define CMD2_UBITS 0x10
++
++static BITCMD *addcmd __P((BITCMD *, int, int, int, u_int));
++static void compress_mode __P((BITCMD *));
++#ifdef SETMODE_DEBUG
++static void dumpmode __P((BITCMD *));
++#endif
++
++/*
++ * Given the old mode and an array of bitcmd structures, apply the operations
++ * described in the bitcmd structures to the old mode, and return the new mode.
++ * Note that there is no '=' command; a strict assignment is just a '-' (clear
++ * bits) followed by a '+' (set bits).
++ */
++mode_t
++getmode(bbox, omode)
++ const void *bbox;
++ mode_t omode;
++{
++ const BITCMD *set;
++ mode_t clrval, newmode, value;
++
++ _DIAGASSERT(bbox != NULL);
++
++ set = (const BITCMD *)bbox;
++ newmode = omode;
++ for (value = 0;; set++)
++ switch(set->cmd) {
++ /*
++ * When copying the user, group or other bits around, we "know"
++ * where the bits are in the mode so that we can do shifts to
++ * copy them around. If we don't use shifts, it gets real
++ * grundgy with lots of single bit checks and bit sets.
++ */
++ case 'u':
++ value = (newmode & S_IRWXU) >> 6;
++ goto common;
++
++ case 'g':
++ value = (newmode & S_IRWXG) >> 3;
++ goto common;
++
++ case 'o':
++ value = newmode & S_IRWXO;
++common: if (set->cmd2 & CMD2_CLR) {
++ clrval =
++ (set->cmd2 & CMD2_SET) ? S_IRWXO : value;
++ if (set->cmd2 & CMD2_UBITS)
++ newmode &= ~((clrval<<6) & set->bits);
++ if (set->cmd2 & CMD2_GBITS)
++ newmode &= ~((clrval<<3) & set->bits);
++ if (set->cmd2 & CMD2_OBITS)
++ newmode &= ~(clrval & set->bits);
++ }
++ if (set->cmd2 & CMD2_SET) {
++ if (set->cmd2 & CMD2_UBITS)
++ newmode |= (value<<6) & set->bits;
++ if (set->cmd2 & CMD2_GBITS)
++ newmode |= (value<<3) & set->bits;
++ if (set->cmd2 & CMD2_OBITS)
++ newmode |= value & set->bits;
++ }
++ break;
++
++ case '+':
++ newmode |= set->bits;
++ break;
++
++ case '-':
++ newmode &= ~set->bits;
++ break;
++
++ case 'X':
++ if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
++ newmode |= set->bits;
++ break;
++
++ case '\0':
++ default:
++#ifdef SETMODE_DEBUG
++ (void)printf("getmode:%04o -> %04o\n", omode, newmode);
++#endif
++ return (newmode);
++ }
++}
++
++#define ADDCMD(a, b, c, d) do { \
++ if (set >= endset) { \
++ BITCMD *newset; \
++ setlen += SET_LEN_INCR; \
++ newset = realloc(saveset, sizeof(BITCMD) * setlen); \
++ if (newset == NULL) { \
++ free(saveset); \
++ return (NULL); \
++ } \
++ set = newset + (set - saveset); \
++ saveset = newset; \
++ endset = newset + (setlen - 2); \
++ } \
++ set = addcmd(set, (a), (b), (c), (d)); \
++} while (/*CONSTCOND*/0)
++
++#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
++
++void *
++setmode(p)
++ const char *p;
++{
++ int perm, who;
++ char op, *ep;
++ BITCMD *set, *saveset, *endset;
++ sigset_t sigset, sigoset;
++ mode_t mask;
++ int equalopdone = 0; /* pacify gcc */
++ int permXbits, setlen;
++
++ if (!*p)
++ return (NULL);
++
++ /*
++ * Get a copy of the mask for the permissions that are mask relative.
++ * Flip the bits, we want what's not set. Since it's possible that
++ * the caller is opening files inside a signal handler, protect them
++ * as best we can.
++ */
++ sigfillset(&sigset);
++ (void)sigprocmask(SIG_BLOCK, &sigset, &sigoset);
++ (void)umask(mask = umask(0));
++ mask = ~mask;
++ (void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
++
++ setlen = SET_LEN + 2;
++
++ if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
++ return (NULL);
++ saveset = set;
++ endset = set + (setlen - 2);
++
++ /*
++ * If an absolute number, get it and return; disallow non-octal digits
++ * or illegal bits.
++ */
++ if (isdigit((unsigned char)*p)) {
++ perm = (mode_t)strtol(p, &ep, 8);
++ if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) {
++ free(saveset);
++ return (NULL);
++ }
++ ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
++ set->cmd = 0;
++ return (saveset);
++ }
++
++ /*
++ * Build list of structures to set/clear/copy bits as described by
++ * each clause of the symbolic mode.
++ */
++ for (;;) {
++ /* First, find out which bits might be modified. */
++ for (who = 0;; ++p) {
++ switch (*p) {
++ case 'a':
++ who |= STANDARD_BITS;
++ break;
++ case 'u':
++ who |= S_ISUID|S_IRWXU;
++ break;
++ case 'g':
++ who |= S_ISGID|S_IRWXG;
++ break;
++ case 'o':
++ who |= S_IRWXO;
++ break;
++ default:
++ goto getop;
++ }
++ }
++
++getop: if ((op = *p++) != '+' && op != '-' && op != '=') {
++ free(saveset);
++ return (NULL);
++ }
++ if (op == '=')
++ equalopdone = 0;
++
++ who &= ~S_ISTXT;
++ for (perm = 0, permXbits = 0;; ++p) {
++ switch (*p) {
++ case 'r':
++ perm |= S_IRUSR|S_IRGRP|S_IROTH;
++ break;
++ case 's':
++ /*
++ * If specific bits where requested and
++ * only "other" bits ignore set-id.
++ */
++ if (who == 0 || (who & ~S_IRWXO))
++ perm |= S_ISUID|S_ISGID;
++ break;
++ case 't':
++ /*
++ * If specific bits where requested and
++ * only "other" bits ignore set-id.
++ */
++ if (who == 0 || (who & ~S_IRWXO)) {
++ who |= S_ISTXT;
++ perm |= S_ISTXT;
++ }
++ break;
++ case 'w':
++ perm |= S_IWUSR|S_IWGRP|S_IWOTH;
++ break;
++ case 'X':
++ permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
++ break;
++ case 'x':
++ perm |= S_IXUSR|S_IXGRP|S_IXOTH;
++ break;
++ case 'u':
++ case 'g':
++ case 'o':
++ /*
++ * When ever we hit 'u', 'g', or 'o', we have
++ * to flush out any partial mode that we have,
++ * and then do the copying of the mode bits.
++ */
++ if (perm) {
++ ADDCMD(op, who, perm, mask);
++ perm = 0;
++ }
++ if (op == '=')
++ equalopdone = 1;
++ if (op == '+' && permXbits) {
++ ADDCMD('X', who, permXbits, mask);
++ permXbits = 0;
++ }
++ ADDCMD(*p, who, op, mask);
++ break;
++
++ default:
++ /*
++ * Add any permissions that we haven't already
++ * done.
++ */
++ if (perm || (op == '=' && !equalopdone)) {
++ if (op == '=')
++ equalopdone = 1;
++ ADDCMD(op, who, perm, mask);
++ perm = 0;
++ }
++ if (permXbits) {
++ ADDCMD('X', who, permXbits, mask);
++ permXbits = 0;
++ }
++ goto apply;
++ }
++ }
++
++apply: if (!*p)
++ break;
++ if (*p != ',')
++ goto getop;
++ ++p;
++ }
++ set->cmd = 0;
++#ifdef SETMODE_DEBUG
++ (void)printf("Before compress_mode()\n");
++ dumpmode(saveset);
++#endif
++ compress_mode(saveset);
++#ifdef SETMODE_DEBUG
++ (void)printf("After compress_mode()\n");
++ dumpmode(saveset);
++#endif
++ return (saveset);
++}
++
++static BITCMD *
++addcmd(set, op, who, oparg, mask)
++ BITCMD *set;
++ int oparg, who;
++ int op;
++ u_int mask;
++{
++
++ _DIAGASSERT(set != NULL);
++
++ switch (op) {
++ case '=':
++ set->cmd = '-';
++ set->bits = who ? who : STANDARD_BITS;
++ set++;
++
++ op = '+';
++ /* FALLTHROUGH */
++ case '+':
++ case '-':
++ case 'X':
++ set->cmd = op;
++ set->bits = (who ? who : mask) & oparg;
++ break;
++
++ case 'u':
++ case 'g':
++ case 'o':
++ set->cmd = op;
++ if (who) {
++ set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
++ ((who & S_IRGRP) ? CMD2_GBITS : 0) |
++ ((who & S_IROTH) ? CMD2_OBITS : 0);
++ set->bits = (mode_t)~0;
++ } else {
++ set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
++ set->bits = mask;
++ }
++
++ if (oparg == '+')
++ set->cmd2 |= CMD2_SET;
++ else if (oparg == '-')
++ set->cmd2 |= CMD2_CLR;
++ else if (oparg == '=')
++ set->cmd2 |= CMD2_SET|CMD2_CLR;
++ break;
++ }
++ return (set + 1);
++}
++
++#ifdef SETMODE_DEBUG
++static void
++dumpmode(set)
++ BITCMD *set;
++{
++
++ _DIAGASSERT(set != NULL);
++
++ for (; set->cmd; ++set)
++ (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
++ set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
++ set->cmd2 & CMD2_CLR ? " CLR" : "",
++ set->cmd2 & CMD2_SET ? " SET" : "",
++ set->cmd2 & CMD2_UBITS ? " UBITS" : "",
++ set->cmd2 & CMD2_GBITS ? " GBITS" : "",
++ set->cmd2 & CMD2_OBITS ? " OBITS" : "");
++}
++#endif
++
++/*
++ * Given an array of bitcmd structures, compress by compacting consecutive
++ * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
++ * 'g' and 'o' commands continue to be separate. They could probably be
++ * compacted, but it's not worth the effort.
++ */
++static void
++compress_mode(set)
++ BITCMD *set;
++{
++ BITCMD *nset;
++ int setbits, clrbits, Xbits, op;
++
++ _DIAGASSERT(set != NULL);
++
++ for (nset = set;;) {
++ /* Copy over any 'u', 'g' and 'o' commands. */
++ while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
++ *set++ = *nset++;
++ if (!op)
++ return;
++ }
++
++ for (setbits = clrbits = Xbits = 0;; nset++) {
++ if ((op = nset->cmd) == '-') {
++ clrbits |= nset->bits;
++ setbits &= ~nset->bits;
++ Xbits &= ~nset->bits;
++ } else if (op == '+') {
++ setbits |= nset->bits;
++ clrbits &= ~nset->bits;
++ Xbits &= ~nset->bits;
++ } else if (op == 'X')
++ Xbits |= nset->bits & ~setbits;
++ else
++ break;
++ }
++ if (clrbits) {
++ set->cmd = '-';
++ set->cmd2 = 0;
++ set->bits = clrbits;
++ set++;
++ }
++ if (setbits) {
++ set->cmd = '+';
++ set->cmd2 = 0;
++ set->bits = setbits;
++ set++;
++ }
++ if (Xbits) {
++ set->cmd = 'X';
++ set->cmd2 = 0;
++ set->bits = Xbits;
++ set++;
++ }
++ }
++}
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/sh.1 bin_NetBSD-1.6release/src/bin/sh/sh.1
+--- bin_NetBSD-1.6release.orig/src/bin/sh/sh.1 2002-05-16 11:41:22.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/sh.1 2003-02-08 14:35:42.000000000 +0000
+@@ -188,7 +188,7 @@
+ or
+ .Ic until ;
+ or if the command is the left hand operand of an
+-.Dq \*[Am]\*[Am]
++.Dq &&
+ or
+ .Dq ||
+ operator.
+@@ -256,9 +256,9 @@
+ operators (their meaning is discussed later). 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
+@@ -381,13 +381,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) from 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).
+@@ -525,27 +525,27 @@
+ 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.
+ .Pp
+ A ; or \*[Lt]newline\*[Gt] 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
+@@ -558,17 +558,17 @@
+ 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]
++.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 iff the exit status of the first
+ command is nonzero.
+-.Dq \*[Am]\*[Am]
++.Dq &&
+ and
+ .Dq ||
+ both have the same priority.
+@@ -646,7 +646,7 @@
+ 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
+@@ -961,11 +961,11 @@
+ .Ss Shell Patterns
+ A pattern consists of normal characters, which match themselves,
+ and meta-characters. The meta-characters are
+-.Dq ! ,
++.Dq \&! ,
+ .Dq * ,
+-.Dq ? ,
++.Dq \&? ,
+ and
+-.Dq [ .
++.Dq \&[ .
+ These characters lose their special meanings if they are quoted. When
+ command or variable substitution is performed and the dollar sign or back
+ quotes are not double quoted, the value of the variable or the output of
+@@ -999,14 +999,10 @@
+ .Ss Builtins
+ This section lists the builtin commands which are builtin because they
+ need to perform some operation that can't be performed by a separate
+-process. In addition to these, there are several other commands that may
+-be builtin for efficiency (e.g.
+-.Xr printf 1 ,
+-.Xr echo 1 ,
+-.Xr test 1 ,
+-etc).
++process.
+ .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.
+@@ -1032,9 +1028,12 @@
+ .It command Ar command Ar arg ...
+ Execute the specified builtin command. (This is useful when you
+ have a shell function with the same name as a builtin command.)
+-.It cd Op Ar directory
++.It cd Ar -
++.It Xo cd Op Fl LP
++.Op Ar directory
++.Xc
+ Switch to the specified directory (default
+-.Ev $HOME ) .
++.Ev HOME ) .
+ If an entry for
+ .Ev CDPATH
+ appears in the environment of the
+@@ -1048,13 +1047,61 @@
+ .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 repective 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.
+@@ -1217,7 +1264,7 @@
+ will set the variable
+ .Va var
+ to a
+-.Dq ? ;
++.Dq \&? ;
+ .Ic getopts
+ will then unset
+ .Ev OPTARG
+@@ -1236,7 +1283,7 @@
+ otherwise, it will set
+ .Va var
+ to
+-.Dq ? .
++.Dq \&? .
+ .Pp
+ The following code fragment shows how one might process the arguments
+ for a command that can take the options
+@@ -1295,7 +1342,7 @@
+ .It jobs
+ This command lists out all the background processes
+ which are children of the current shell process.
+-.It pwd
++.It pwd Op Fl LP
+ Print the current directory. The builtin command may
+ differ from the program of the same name because the
+ builtin command remembers what the current directory
+@@ -1304,6 +1351,14 @@
+ renamed, the builtin version of
+ .Ic pwd
+ will continue to print the old name for the directory.
++The
++.Fl P
++option causes the phyical value of the current working directory to be shown,
++that is, all symbolic links are resolved to their repective 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
+@@ -1314,7 +1369,9 @@
+ option is specified and the standard input is a terminal. Then a line is
+ read from the standard input. 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. If there are
++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
+@@ -1346,6 +1403,225 @@
+ .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 \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\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 c ,
++.Cm d ,
++and
++.Cm s ,
++formats, this option has no effect. For the
++.Cm o
++formats 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; 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 as in
++.Ic echo .
++.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; however if the
++precision is 0 or missing, 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 -- }
+@@ -1392,15 +1668,248 @@
+ and so on, decreasing
+ the value of
+ .Va $#
+-by one. If there are zero positional parameters,
++by one. If n is greater than the number of positional parameters,
+ .Ic shift
+-does nothing.
+-.It Xo trap
+-.Op Fl l
+-.Xc
++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 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. The signals are specified by signal number or as
+@@ -1420,11 +1929,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
+@@ -1436,10 +1940,6 @@
+ .Pp
+ List trapped signals and their corresponding action
+ .Pp
+-.Dl trap -l
+-.Pp
+-Print a list of valid signals
+-.Pp
+ .Dl trap '' SIGINT QUIT tstp 30
+ .Pp
+ Ignore signals INT QUIT TSTP USR1
+@@ -1614,6 +2114,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
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/shell.h bin_NetBSD-1.6release/src/bin/sh/shell.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/shell.h 2000-05-23 11:03:19.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/shell.h 2003-02-08 14:35:42.000000000 +0000
+@@ -1,4 +1,4 @@
+-/* $NetBSD: shell.h,v 1.13 2000/05/22 10:18:47 elric Exp $ */
++/* $NetBSD: shell.h,v 1.14 2002/05/25 23:09:06 wiz Exp $ */
+
+ /*-
+ * Copyright (c) 1991, 1993
+@@ -52,23 +52,18 @@
+ */
+
+
++#ifndef JOBS
+ #define JOBS 1
++#endif
+ #ifndef BSD
+ #define BSD 1
+ #endif
+
+-#ifdef __STDC__
+ typedef void *pointer;
+ #ifndef NULL
+ #define NULL (void *)0
+ #endif
+-#else /* not __STDC__ */
+-typedef char *pointer;
+-#ifndef NULL
+-#define NULL 0
+-#endif
+-#endif /* not __STDC__ */
+-#define STATIC /* empty */
++#define STATIC static
+ #define MKINIT /* empty */
+
+ #include <sys/cdefs.h>
+@@ -81,3 +76,7 @@
+ #else
+ #define TRACE(param)
+ #endif
++
++#if defined(__GNUC__) && __GNUC__ < 3
++#define va_copy __va_copy
++#endif
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/show.c bin_NetBSD-1.6release/src/bin/sh/show.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/show.c 2002-05-16 11:41:22.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/show.c 2003-02-08 14:35:42.000000000 +0000
+@@ -1,4 +1,4 @@
+-/* $NetBSD: show.c,v 1.21 2002/05/15 16:33:35 christos Exp $ */
++/* $NetBSD: show.c,v 1.22 2002/05/25 23:09:06 wiz Exp $ */
+
+ /*-
+ * Copyright (c) 1991, 1993
+@@ -41,16 +41,12 @@
+ #if 0
+ static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95";
+ #else
+-__RCSID("$NetBSD: show.c,v 1.21 2002/05/15 16:33:35 christos Exp $");
++__RCSID("$NetBSD: show.c,v 1.22 2002/05/25 23:09:06 wiz Exp $");
+ #endif
+ #endif /* not lint */
+
+ #include <stdio.h>
+-#ifdef __STDC__
+ #include <stdarg.h>
+-#else
+-#include <varargs.h>
+-#endif
+
+ #include "shell.h"
+ #include "parser.h"
+@@ -276,7 +272,6 @@
+ putc('\t', fp);
+ }
+ }
+-#endif
+
+
+
+@@ -294,7 +289,6 @@
+ #endif
+
+
+-#ifdef DEBUG
+ void
+ trputc(c)
+ int c;
+@@ -305,36 +299,22 @@
+ if (c == '\n')
+ fflush(tracefile);
+ }
+-#endif
+
+ void
+-#ifdef __STDC__
+ trace(const char *fmt, ...)
+-#else
+-trace(va_alist)
+- va_dcl
+-#endif
+ {
+-#ifdef DEBUG
+ va_list va;
+-#ifdef __STDC__
++
+ va_start(va, fmt);
+-#else
+- char *fmt;
+- va_start(va);
+- fmt = va_arg(va, char *);
+-#endif
+ if (tracefile != NULL) {
+ (void) vfprintf(tracefile, fmt, va);
+ if (strchr(fmt, '\n'))
+ (void) fflush(tracefile);
+ }
+ va_end(va);
+-#endif
+ }
+
+
+-#ifdef DEBUG
+ void
+ trputs(s)
+ const char *s;
+@@ -386,14 +366,12 @@
+ }
+ putc('"', tracefile);
+ }
+-#endif
+
+
+ void
+ trargs(ap)
+ char **ap;
+ {
+-#ifdef DEBUG
+ if (tracefile == NULL)
+ return;
+ while (*ap) {
+@@ -404,11 +382,9 @@
+ putc('\n', tracefile);
+ }
+ fflush(tracefile);
+-#endif
+ }
+
+
+-#ifdef DEBUG
+ void
+ opentrace() {
+ char s[100];
+@@ -421,7 +397,7 @@
+ #ifdef not_this_way
+ {
+ char *p;
+- if ((p = getenv("HOME")) == NULL) {
++ if ((p = getenv(homestr)) == NULL) {
+ if (geteuid() == 0)
+ p = "/";
+ else
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/specialbltins.def bin_NetBSD-1.6release/src/bin/sh/specialbltins.def
+--- bin_NetBSD-1.6release.orig/src/bin/sh/specialbltins.def 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/specialbltins.def 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,16 @@
++.
++:
++break
++builtin
++continue
++eval
++exec
++exit
++export
++readonly
++return
++set
++shift
++times
++trap
++unset
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/trap.c bin_NetBSD-1.6release/src/bin/sh/trap.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/trap.c 2001-03-18 11:04:44.000000000 +0000
++++ bin_NetBSD-1.6release/src/bin/sh/trap.c 2003-02-08 14:35:42.000000000 +0000
+@@ -48,6 +48,7 @@
+ #include <signal.h>
+ #include <unistd.h>
+ #include <stdlib.h>
++#include <string.h>
+
+ #include "shell.h"
+ #include "main.h"
+@@ -63,6 +64,9 @@
+ #include "trap.h"
+ #include "mystring.h"
+
++#ifdef HETIO
++#include "hetio.h"
++#endif
+
+ /*
+ * Sigmode records the current value of the signal handlers for the various
+@@ -77,59 +81,24 @@
+ #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 __P((int, sig_t *));
+-static int signame_to_signum __P((const char *));
+-void printsignals __P((void));
+-
+-/*
+- * 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(p)
+- const char *p;
+-{
+- int i;
+-
+- if (is_number(p))
+- return number(p);
+-
+- if (strcasecmp(p, "exit") == 0 )
+- return 0;
++/* trap handler commands */
++char *trap[NSIG];
++/* current value of signal */
++STATIC char sigmode[NSIG - 1];
++/* indicates specified signal received */
++STATIC char gotsig[NSIG - 1];
++/* last pending signal */
++volatile sig_atomic_t pendingsigs;
+
+- if (strncasecmp(p, "sig", 3) == 0)
+- p += 3;
++extern char *signal_names[];
+
+- for (i = 0; i < NSIG; ++i)
+- if (strcasecmp (p, sys_signame[i]) == 0)
+- return i;
+- return -1;
++#ifdef mkinit
++INCLUDE <signal.h>
++INIT {
++ signal(SIGCHLD, SIG_DFL);
+ }
++#endif
+
+-/*
+- * Print a list of valid signal names
+- */
+-void
+-printsignals(void)
+-{
+- int n;
+-
+- out1str("EXIT ");
+-
+- for (n = 1; n < NSIG; n++) {
+- out1fmt("%s", sys_signame[n]);
+- if ((n == NSIG/2) || n == (NSIG - 1))
+- out1str("\n");
+- else
+- out1c(' ');
+- }
+-}
+
+ /*
+ * The trap builtin.
+@@ -144,55 +113,37 @@
+ char **ap;
+ int signo;
+
+- if (argc <= 1) {
+- for (signo = 0 ; signo <= NSIG ; signo++) {
+- if (trap[signo] != NULL)
+- out1fmt("trap -- '%s' %s\n", trap[signo],
+- (signo) ? sys_signame[signo] : "EXIT");
++ 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;
+ }
+- 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] == NULL)
+- ap++;
+- else if ((*ap)[1] == 'l' && (*ap)[2] == NULL) {
+- printsignals();
+ return 0;
+ }
+- else
+- error("bad option %s\n", *ap);
+- }
++ if (!ap[1])
++ action = NULL;
+ else
+ action = *ap++;
+- }
+-
+ while (*ap) {
+- if (is_number(*ap))
+- signo = number(*ap);
+- else
+- signo = signame_to_signum(*ap);
+-
+- if (signo < 0 || signo > NSIG)
++ if ((signo = decode_signal(*ap, 0)) < 0)
+ error("%s: bad trap", *ap);
+-
+ INTOFF;
+- if (action)
++ 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;
+@@ -211,7 +162,7 @@
+ clear_traps() {
+ char **tp;
+
+- for (tp = trap ; tp <= &trap[NSIG] ; tp++) {
++ for (tp = trap ; tp < &trap[NSIG] ; tp++) {
+ if (*tp && **tp) { /* trap not NULL or SIG_IGN */
+ INTOFF;
+ ckfree(*tp);
+@@ -230,13 +181,13 @@
+ * out what it should be set to.
+ */
+
+-long
++void
+ setsignal(signo)
+ int signo;
+ {
+ int action;
+- sig_t sigact = SIG_DFL;
+ char *t;
++ struct sigaction act;
+
+ if ((t = trap[signo]) == NULL)
+ action = S_DFL;
+@@ -279,15 +230,15 @@
+ /*
+ * current setting unknown
+ */
+- if (!getsigaction(signo, &sigact)) {
++ 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 0;
++ return;
+ }
+- if (sigact == SIG_IGN) {
++ if (act.sa_handler == SIG_IGN) {
+ if (mflag && (signo == SIGTSTP ||
+ signo == SIGTTIN || signo == SIGTTOU)) {
+ *t = S_IGN; /* don't hard ignore these */
+@@ -298,31 +249,21 @@
+ }
+ }
+ if (*t == S_HARD_IGN || *t == action)
+- return 0;
++ return;
+ switch (action) {
+- case S_DFL: sigact = SIG_DFL; break;
+- case S_CATCH: sigact = onsig; break;
+- case S_IGN: sigact = SIG_IGN; break;
++ 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;
+- siginterrupt(signo, 1);
+- return (long)signal(signo, sigact);
+-}
+-
+-/*
+- * Return the current setting for sig w/o changing it.
+- */
+-static int
+-getsigaction(signo, sigact)
+- 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;
++ act.sa_flags = 0;
++ sigfillset(&act.sa_mask);
++ sigaction(signo, &act, 0);
+ }
+
+ /*
+@@ -340,22 +281,6 @@
+ }
+
+
+-#ifdef mkinit
+-INCLUDE <signal.h>
+-INCLUDE "trap.h"
+-
+-SHELLPROC {
+- char *sm;
+-
+- clear_traps();
+- for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
+- if (*sm == S_IGN)
+- *sm = S_HARD_IGN;
+- }
+-}
+-#endif
+-
+-
+
+ /*
+ * Signal handler.
+@@ -365,13 +290,15 @@
+ onsig(signo)
+ int signo;
+ {
+- signal(signo, onsig);
+ if (signo == SIGINT && trap[SIGINT] == NULL) {
+- onint();
++ if (suppressint) {
++ (*(sig_atomic_t *) &intpending)++;
+ return;
+ }
++ onint();
++ }
+ gotsig[signo - 1] = 1;
+- pendingsigs++;
++ pendingsigs = signo;
+ }
+
+
+@@ -383,23 +310,15 @@
+
+ void
+ dotrap() {
+- int i;
++ char *p;
+ int savestatus;
+
+- for (;;) {
+- for (i = 1 ; ; i++) {
+- if (gotsig[i - 1])
+- break;
+- if (i >= NSIG)
+- goto done;
+- }
+- gotsig[i - 1] = 0;
++ while (pendingsigs = 0, barrier(), (p = memchr(gotsig, 1, NSIG - 1))) {
++ *p = 0;
+ savestatus=exitstatus;
+- evalstring(trap[i], 0);
++ evalstring(trap[p - gotsig + 1], 0);
+ exitstatus=savestatus;
+ }
+-done:
+- pendingsigs = 0;
+ }
+
+
+@@ -415,12 +334,12 @@
+ {
+ static int is_interactive;
+
+- if (on == is_interactive)
++ if (++on == is_interactive)
+ return;
++ is_interactive = on;
+ setsignal(SIGINT);
+ setsignal(SIGQUIT);
+ setsignal(SIGTERM);
+- is_interactive = on;
+ }
+
+
+@@ -430,29 +349,48 @@
+ */
+
+ void
+-exitshell(status)
+- int status;
++exitshell(void)
+ {
+- struct jmploc loc1, loc2;
++ struct jmploc loc;
+ char *p;
++ int status;
+
++ status = exitstatus;
+ TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
+- if (setjmp(loc1.loc)) {
+- goto l1;
+- }
+- if (setjmp(loc2.loc)) {
+- goto l2;
++#ifdef HETIO
++ hetio_reset_term();
++#endif
++ if (setjmp(loc.loc)) {
++ goto out;
+ }
+- handler = &loc1;
++ handler = &loc;
+ 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);
++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 --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/trap.h bin_NetBSD-1.6release/src/bin/sh/trap.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/trap.h 2000-05-23 11:03:19.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/trap.h 2003-02-08 14:35:42.000000000 +0000
+@@ -38,13 +38,17 @@
+ * @(#)trap.h 8.3 (Berkeley) 6/5/95
+ */
+
+-extern int pendingsigs;
++#include <signal.h>
++
++extern volatile sig_atomic_t pendingsigs;
++extern char *trap[];
+
+ int trapcmd __P((int, char **));
+ void clear_traps __P((void));
+-long setsignal __P((int));
++void setsignal __P((int));
+ void ignoresig __P((int));
+ void onsig __P((int));
+ void dotrap __P((void));
+ void setinteractive __P((int));
+-void exitshell __P((int)) __attribute__((noreturn));
++void exitshell __P((void)) __attribute__((noreturn));
++int decode_signal __P((const char *, int));
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/var.c bin_NetBSD-1.6release/src/bin/sh/var.c
+--- bin_NetBSD-1.6release.orig/src/bin/sh/var.c 2002-05-16 11:41:23.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/var.c 2003-02-08 14:35:42.000000000 +0000
+@@ -67,6 +67,7 @@
+ #include "error.h"
+ #include "mystring.h"
+ #include "parser.h"
++#include "mail.h"
+ #ifndef SMALL
+ #include "myhistedit.h"
+ #endif
+@@ -75,76 +76,56 @@
+ #define VTABSIZE 39
+
+
+-struct varinit {
+- struct var *var;
+- int flags;
+- const char *text;
+- void (*func) __P((const char *));
+-};
+-
++struct localvar *localvars;
+
+-#if ATTY
+-struct var vatty;
+-#endif
+-#ifndef SMALL
+-struct var vhistsize;
+-struct var vterm;
++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 vifs;
+-struct var vmail;
+-struct var vmpath;
+-struct var vpath;
+-struct var vps1;
+-struct var vps2;
+-struct var vvers;
+-struct var voptind;
+
+-const struct varinit varinit[] = {
++struct var varinit[] = {
+ #if ATTY
+- { &vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY=",
+- NULL },
++ { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY=", 0 },
+ #endif
+-#ifndef SMALL
+- { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=",
+- sethistsize },
++#ifdef IFS_BROKEN
++ { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 },
++#else
++ { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS=", 0 },
+ #endif
+- { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n",
+- NULL },
+- { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=",
+- NULL },
+- { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=",
+- NULL },
+- { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH,
+- changepath },
+- /*
+- * vps1 depends on uid
+- */
+- { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ",
+- NULL },
++ { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", changemail },
++ { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", changemail },
++ { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
++ { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
++ { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
++ { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
+ #ifndef SMALL
+- { &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=",
+- setterm },
++ { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=", 0 },
++ { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", sethistsize },
+ #endif
+- { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1",
+- getoptsreset },
+- { NULL, 0, NULL,
+- NULL }
+ };
+
+-struct var *vartab[VTABSIZE];
++STATIC struct var *vartab[VTABSIZE];
+
+ STATIC struct var **hashvar __P((const char *));
+-STATIC int varequal __P((const char *, const char *));
++STATIC void showvars __P((const char *, int, int));
++STATIC struct var **findvar __P((struct var **, const char *));
+
+ /*
+ * Initialize the varable symbol tables and import the environment
+ */
+
+ #ifdef mkinit
++INCLUDE <unistd.h>
++INCLUDE "cd.h"
++INCLUDE "output.h"
+ INCLUDE "var.h"
+ MKINIT char **environ;
+ INIT {
+ char **envp;
++ static char ppid[32 + 5] = "PPID=";
+
+ initvar();
+ for (envp = environ ; *envp ; envp++) {
+@@ -152,41 +133,37 @@
+ setvareq(*envp, VEXPORT|VTEXTFIXED);
+ }
+ }
++
++ fmtstr(ppid + 5, sizeof(ppid) - 5, "%ld", (long) getppid());
++ setvareq(ppid, VTEXTFIXED);
++ setpwd(0, 0);
+ }
+ #endif
+
+
+ /*
+ * This routine initializes the builtin variables. It is called when the
+- * shell is initialized and again when a shell procedure is spawned.
++ * shell is initialized.
+ */
+
+ void
+ initvar() {
+- const struct varinit *ip;
+ struct var *vp;
++ struct var *end;
+ struct var **vpp;
+
+- for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
+- if ((vp->flags & VEXPORT) == 0) {
+- vpp = hashvar(ip->text);
++ vp = varinit;
++ end = vp + sizeof(varinit) / sizeof(varinit[0]);
++ do {
++ vpp = hashvar(vp->text);
+ vp->next = *vpp;
+ *vpp = vp;
+- vp->text = strdup(ip->text);
+- vp->flags = ip->flags;
+- vp->func = ip->func;
+- }
+- }
++ } while (++vp < end);
+ /*
+ * 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;
+- }
++ if (!geteuid())
++ vps1.text = "PS1=# ";
+ }
+
+ /*
+@@ -198,20 +175,21 @@
+ const char *name, *val;
+ int flags;
+ {
+- struct jmploc jmploc;
++ int err;
++ volatile int saveint;
+ struct jmploc *volatile savehandler = handler;
+- int err = 0;
+-#ifdef __GNUC__
+- (void) &err;
+-#endif
++ 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;
+ }
+
+@@ -225,45 +203,31 @@
+ const char *name, *val;
+ int flags;
+ {
+- const char *p;
+- const char *q;
+- char *d;
+- int len;
+- int namelen;
++ char *p, *q;
++ size_t namelen;
+ char *nameeq;
+- int isbad;
++ size_t vallen;
+
+- 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++;
+- }
++ q = endofname(name);
++ p = strchrnul(q, '=');
+ namelen = p - name;
+- if (isbad)
++ if (!namelen || p != q)
+ error("%.*s: bad variable name", namelen, name);
+- len = namelen + 2; /* 2 is space for '=' and '\0' */
++ vallen = 0;
+ if (val == NULL) {
+ flags |= VUNSET;
+ } else {
+- len += strlen(val);
++ vallen = strlen(val);
+ }
+- d = nameeq = ckmalloc(len);
+- q = name;
+- while (--namelen >= 0)
+- *d++ = *q++;
+- *d++ = '=';
+- *d = '\0';
+- if (val)
+- scopy(val, d);
++ INTOFF;
++ p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen);
++ *p++ = '=';
++ if (vallen) {
++ p = mempcpy(p, val, vallen);
++ }
++ *p = '\0';
+ setvareq(nameeq, flags);
++ INTON;
+ }
+
+
+@@ -282,16 +246,15 @@
+ {
+ struct var *vp, **vpp;
+
+- if (aflag)
+- flags |= VEXPORT;
+ vpp = hashvar(s);
+- for (vp = *vpp ; vp ; vp = vp->next) {
+- if (varequal(s, vp->text)) {
++ flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
++ vp = *findvar(vpp, s);
++ INTOFF;
++ if (vp) {
+ if (vp->flags & VREADONLY) {
+ size_t len = strchr(s, '=') - s;
+ error("%.*s: is read only", len, s);
+ }
+- INTOFF;
+
+ if (vp->func && (flags & VNOFUNC) == 0)
+ (*vp->func)(strchr(s, '=') + 1);
+@@ -299,27 +262,17 @@
+ if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+ ckfree(vp->text);
+
+- vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
+- vp->flags |= flags;
+- vp->text = s;
+-
+- /*
+- * We could roll this to a function, to handle it as
+- * a regular variable function callback, but why bother?
+- */
+- if (vp == &vmpath || (vp == &vmail && ! mpathset()))
+- chkmail(1);
+- INTON;
+- return;
+- }
+- }
++ flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VUNSET);
++ } else {
+ /* not found */
+ vp = ckmalloc(sizeof (*vp));
+- vp->flags = flags;
+- vp->text = s;
+ vp->next = *vpp;
+ vp->func = NULL;
+ *vpp = vp;
++ }
++ vp->text = s;
++ vp->flags = flags;
++ INTON;
+ }
+
+
+@@ -329,14 +282,13 @@
+ */
+
+ void
+-listsetvar(list)
+- struct strlist *list;
+- {
++listsetvar(struct strlist *list, int flags)
++{
+ struct strlist *lp;
+
+ INTOFF;
+ for (lp = list ; lp ; lp = lp->next) {
+- setvareq(savestr(lp->text), 0);
++ setvareq(savestr(lp->text), flags);
+ }
+ INTON;
+ }
+@@ -353,45 +305,29 @@
+ {
+ struct var *v;
+
+- for (v = *hashvar(name) ; v ; v = v->next) {
+- if (varequal(v->text, name)) {
+- if (v->flags & VUNSET)
+- return NULL;
++ if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
+ 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.
++ * Search the environment of a builtin command.
+ */
+
+ char *
+-bltinlookup(name, doall)
++bltinlookup(name)
+ 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;
++ return lookupvar(name);
+ }
+
+
+@@ -403,66 +339,28 @@
+
+ char **
+ environment() {
+- int nenv;
+ struct var **vpp;
+ struct var *vp;
+- char **env;
+ char **ep;
++ void *epend;
+
+- 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++) {
++ STARTSTACKSTR(ep);
++ epend = sstrend;
++ vpp = vartab;
++ do {
+ 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 __P((void));
+-
+-SHELLPROC {
+- shprocvar();
+-}
+-#endif
+-
+-void
+-shprocvar() {
+- 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;
+- }
++ if (vp->flags & VEXPORT && !(vp->flags & VUNSET)) {
++ if (ep == epend) {
++ ep = growstackstr();
++ epend = sstrend;
+ }
++ *ep++ = (char *) vp->text;
+ }
+- initvar();
++ } while (++vpp < vartab + VTABSIZE);
++ if (ep == epend)
++ ep = growstackstr();
++ *ep++ = NULL;
++ return grabstackstr(ep);
+ }
+
+
+@@ -478,15 +376,7 @@
+ int argc;
+ char **argv;
+ {
+- struct var **vpp;
+- struct var *vp;
+-
+- for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+- for (vp = *vpp ; vp ; vp = vp->next) {
+- if ((vp->flags & VUNSET) == 0)
+- out1fmt("%s\n", vp->text);
+- }
+- }
++ showvars(nullstr, VUNSET, VUNSET);
+ return 0;
+ }
+
+@@ -501,45 +391,28 @@
+ int argc;
+ char **argv;
+ {
+- struct var **vpp;
+ struct var *vp;
+ char *name;
+ const char *p;
++ char **aptr;
+ int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
+- int pflag;
++ int notp;
+
+- listsetvar(cmdenviron);
+- pflag = (nextopt("p") == 'p');
+- if (argc > 1 && !pflag) {
+- while ((name = *argptr++) != NULL) {
++ notp = nextopt("p") - 'p';
++ if (notp && ((name = *(aptr = argptr)))) {
++ do {
+ if ((p = strchr(name, '=')) != NULL) {
+ p++;
+ } else {
+- vpp = hashvar(name);
+- for (vp = *vpp ; vp ; vp = vp->next) {
+- if (varequal(vp->text, name)) {
++ if ((vp = *findvar(hashvar(name), name))) {
+ vp->flags |= flag;
+- goto found;
+- }
++ continue;
+ }
+ }
+ setvar(name, p, flag);
+-found:;
+- }
+- } else {
+- for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+- for (vp = *vpp ; vp ; vp = vp->next) {
+- if ((vp->flags & flag) == 0)
+- continue;
+- if (pflag) {
+- out1fmt("%s %s\n", argv[0], vp->text);
++ } while ((name = *++aptr) != NULL);
+ } else {
+- for (p = vp->text ; *p != '=' ; p++)
+- out1c(*p);
+- out1c('\n');
+- }
+- }
+- }
++ showvars(argv[0], flag, 0);
+ }
+ return 0;
+ }
+@@ -589,7 +462,7 @@
+ vp = NULL;
+ } else {
+ vpp = hashvar(name);
+- for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
++ vp = *findvar(vpp, name);
+ if (vp == NULL) {
+ if (strchr(name, '='))
+ setvareq(savestr(name), VSTRFIXED);
+@@ -684,7 +557,7 @@
+
+ for (ap = argptr; *ap ; ap++) {
+ if (flg_func)
+- ret |= unsetfunc(*ap);
++ unsetfunc(*ap);
+ if (flg_var)
+ ret |= unsetvar(*ap);
+ }
+@@ -703,9 +576,9 @@
+ struct var **vpp;
+ struct var *vp;
+
+- vpp = hashvar(s);
+- for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
+- if (varequal(vp->text, s)) {
++ vpp = findvar(hashvar(s), s);
++ vp = *vpp;
++ if (vp) {
+ if (vp->flags & VREADONLY)
+ return (1);
+ INTOFF;
+@@ -722,9 +595,8 @@
+ INTON;
+ return (0);
+ }
+- }
+
+- return (1);
++ return (0);
+ }
+
+
+@@ -753,7 +625,7 @@
+ * either '=' or '\0'.
+ */
+
+-STATIC int
++int
+ varequal(p, q)
+ const char *p, *q;
+ {
+@@ -765,3 +637,44 @@
+ return 1;
+ return 0;
+ }
++
++STATIC void
++showvars(const char *prefix, int mask, int xor)
++{
++ struct var **vpp;
++ struct var *vp;
++ const char *sep = *prefix ? spcstr : prefix;
++
++ vpp = vartab;
++ do {
++ for (vp = *vpp ; vp ; vp = vp->next) {
++ if ((vp->flags & mask) ^ xor) {
++ const char *p;
++ const char *q;
++ int len;
++
++ p = strchr(vp->text, '=');
++ q = nullstr;
++ if (!(vp->flags & VUNSET))
++ q = single_quote(++p);
++ len = p - vp->text;
++
++ out1fmt(
++ "%s%s%.*s%s\n", prefix, sep, len,
++ vp->text, q
++ );
++ }
++ }
++ } while (++vpp < vartab + VTABSIZE);
++}
++
++STATIC struct var **
++findvar(struct var **vpp, const char *name)
++{
++ for (; *vpp; vpp = &(*vpp)->next) {
++ if (varequal((*vpp)->text, name)) {
++ break;
++ }
++ }
++ return vpp;
++}
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/var.h bin_NetBSD-1.6release/src/bin/sh/var.h
+--- bin_NetBSD-1.6release.orig/src/bin/sh/var.h 2000-05-23 11:03:19.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/var.h 2003-02-08 14:35:42.000000000 +0000
+@@ -55,7 +55,7 @@
+ 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) __P((const char *));
+ /* function to be called when */
+ /* the variable gets set/unset */
+@@ -66,26 +66,38 @@
+ 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;
++#define vmail (&vifs)[1]
++#define vmpath (&vmail)[1]
++#define vpath (&vmpath)[1]
++#define vps1 (&vpath)[1]
++#define vps2 (&vps1)[1]
++#define voptind (&vps2)[1]
+ #ifndef SMALL
+-extern struct var vterm;
+-extern struct var vtermcap;
+-extern struct var vhistsize;
++#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.
+@@ -115,11 +127,10 @@
+ void setvar __P((const char *, const char *, int));
+ void setvareq __P((char *, int));
+ struct strlist;
+-void listsetvar __P((struct strlist *));
++void listsetvar __P((struct strlist *, int));
+ char *lookupvar __P((const char *));
+-char *bltinlookup __P((const char *, int));
++char *bltinlookup __P((const char *));
+ char **environment __P((void));
+-void shprocvar __P((void));
+ int showvarscmd __P((int, char **));
+ int exportcmd __P((int, char **));
+ int localcmd __P((int, char **));
+@@ -129,3 +140,4 @@
+ int unsetcmd __P((int, char **));
+ int unsetvar __P((const char *));
+ int setvarsafe __P((const char *, const char *, int));
++int varequal __P((const char *, const char *));
+diff --minimal -ruPb bin_NetBSD-1.6release.orig/src/bin/sh/yaccfe.sh bin_NetBSD-1.6release/src/bin/sh/yaccfe.sh
+--- bin_NetBSD-1.6release.orig/src/bin/sh/yaccfe.sh 1970-01-01 01:00:00.000000000 +0100
++++ bin_NetBSD-1.6release/src/bin/sh/yaccfe.sh 2003-02-08 14:35:42.000000000 +0000
+@@ -0,0 +1,29 @@
++#!/bin/sh
++#
++# Quick script to reformat yacc commands
++# so byacc isnt required.
++#
++# Expects:
++# yacc -d -o yourfile.c yourfile.y
++# Executes:
++# yacc -d -b yourfile yourfile.y
++# mv yourfile.tab.c yourfile.c
++# mv yourfile.tab.h yourfile.h
++#
++
++yaccbin="/usr/bin/yacc"
++yaccflags="-d"
++
++[ ! ${#} -gt 0 ] && exit 1
++while getopts ":o:" opt
++do
++ case $opt in
++ o) output=${OPTARG%*.c} ;;
++ ?) ;;
++ esac
++done
++${yaccbin} ${yaccflags} -b ${output} ${output}.y || exit 1
++for files in ${output}.tab.{c,h}
++do
++ mv ${files} $(sed 's/.tab//' <<< ${files}) || exit 1
++done
diff --git a/app-shells/ash/files/digest-ash-1.6 b/app-shells/ash/files/digest-ash-1.6
new file mode 100644
index 000000000000..5eb33abde8d6
--- /dev/null
+++ b/app-shells/ash/files/digest-ash-1.6
@@ -0,0 +1 @@
+MD5 7e8fbb3654eeb6b57c1ec384c2d1b156 bin.tar.gz 850570