Subject: bin/2808: many fixes for sh(1) /bin/sh aka ash
To: None <gnats-bugs@gnats.netbsd.org>
From: VaX#n8 <vax@linkdead.paranoia.com>
List: netbsd-bugs
Date: 10/06/1996 08:57:06
>Number:         2808
>Category:       bin
>Synopsis:       many bugs in sh
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Oct  6 07:35:01 1996
>Last-Modified:
>Originator:     VaX#n8
>Organization:
	
>Release:        1.2_BETA
>Environment:
	
System: NetBSD linkdead.paranoia.com 1.2_BETA NetBSD 1.2_BETA (LINKDEAD) #12: Thu Oct 3 03:05:44 CDT 1996 bashroot@linkdead.paranoia.com:/usr/src/sys/arch/i386/compile/LINKDEAD i386


>Description:
	
The manpage specifically states that the LHS of short-circuited
operators is considered "tested" and shouldn't cause an exit.
The oversight is that the exitval of the whole expression is
false in this case, and thus the overall expression (not the LHS)
causes the exit.  This is obviously not intended, since many
people routinely use the short-circuit operators as short "if" statements.

>How-To-Repeat:
	
Well, for just one of the bugs, try:
/bin/sh -ec 'false && true; echo hi'

>Fix:
	

In the patch below, I have merged all the fixes I could find in the
FreeBSD tree via the FreeBSD CVS web server.  I used the web equivalent
of "cvs diff -r HEAD -r bsd_4_4_lite_2" as of todays date to get all
the fixes.  I did not incorporate any enhancements that I know of.
Here is the TODO list:

		add locale.h to var.c
		rewrite localevar slightly better

		adding ulimit to sh.1 manpage
		move alias discussion after discussion of how to form commands, lists

		add another type into the parser, VSBRACKETED instead of VSNORMAL,
		so that you can do ${13} and such.
		This would be better in the long run than kluging it like FreeBSD

		add ulimit stuff to miscbltin.c (c.f. FreeBSD code)

diff -ur ../sh.netbsd/Makefile ./Makefile
--- ../sh.netbsd/Makefile	Mon Feb 19 06:09:55 1996
+++ ./Makefile	Sun Oct  6 08:15:54 1996
@@ -2,45 +2,39 @@
 #	@(#)Makefile	8.4 (Berkeley) 5/5/95
 
 PROG=	sh
-SRCS=	alias.c builtins.c cd.c echo.c error.c eval.c exec.c expand.c \
+SHSRCS=	alias.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 nodes.c options.c parser.c redir.c show.c syntax.c \
+	mystring.c options.c parser.c redir.c show.c \
 	trap.c output.c var.c
-OBJS+=	arith.o arith_lex.o init.o
+GENSRCS= arith.c arith_lex.c builtins.c init.c nodes.c syntax.c
+SRCS= ${SHSRCS} ${GENSRCS}
 LDADD+=	-ll -ledit -ltermcap
 DPADD+=	${LIBL} ${LIBEDIT} ${LIBTERMCAP}
 LFLAGS= -8	# 8-bit lex scanner for arithmetic
 CFLAGS+=-DSHELL -I. -I${.CURDIR}
 .PATH:	${.CURDIR}/bltin ${.CURDIR}/../../usr.bin/printf
-CLEANFILES+=\
-	arith.c arith_lex.c builtins.c builtins.h init.c mkinit mknodes \
-	mksyntax nodes.c nodes.h printf.o syntax.c syntax.h token.def \
-	y.tab.c y.tab.h
 
-.depend parser.o: token.def
+CLEANFILES+=   builtins.h mkinit mknodes mksyntax \
+               nodes.h syntax.h token.h y.tab.h
+CLEANFILES+=   ${GENSRCS}
 
-token.def: mktokens
+beforedepend: token.h
+
+token.h: mktokens
 	sh ${.CURDIR}/mktokens
 
-builtins.h builtins.c: ${.CURDIR}/mkbuiltins ${.CURDIR}/builtins.def
+builtins.h builtins.c: mkbuiltins builtins.def
 	cd ${.CURDIR}; sh mkbuiltins ${.OBJDIR}
 
-init.c: mkinit ${SRCS}
+init.c: mkinit ${SHSRCS}
 	./mkinit ${.ALLSRC:S/^mkinit$//}
 
-mkinit: ${.CURDIR}/mkinit.c
-	${CC} ${CFLAGS} ${.CURDIR}/mkinit.c -o $@
-
-nodes.c nodes.h: mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat
+nodes.c nodes.h: mknodes nodetypes nodes.c.pat
 	./mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat
 
-mknodes: ${.CURDIR}/mknodes.c
-	${CC} ${CFLAGS} ${.CURDIR}/mknodes.c -o $@
-
 syntax.c syntax.h: mksyntax
 	./mksyntax
 
-mksyntax: ${.CURDIR}/mksyntax.c ${.CURDIR}/parser.h
-	${CC} ${CFLAGS} ${.CURDIR}/mksyntax.c -o $@
+parser.o: token.h
 
 .include <bsd.prog.mk>
Only in .: TODO
diff -ur ../sh.netbsd/TOUR ./TOUR
--- ../sh.netbsd/TOUR	Fri Oct 13 18:43:53 1995
+++ ./TOUR	Sun Oct  6 07:47:52 1996
@@ -29,7 +29,7 @@
         mknodes         nodetypes           nodes.h nodes.c
         mksignames          -               signames.h signames.c
         mksyntax            -               syntax.h syntax.c
-        mktokens            -               token.def
+        mktokens            -               token.h
         bltin/mkexpr    unary_op binary_op  operators.h operators.c
 
 There are undoubtedly too many of these.  Mkinit searches all the
diff -ur ../sh.netbsd/bltin/bltin.h ./bltin/bltin.h
--- ../sh.netbsd/bltin/bltin.h	Fri Oct 13 18:44:29 1995
+++ ./bltin/bltin.h	Sun Oct  6 07:56:17 1996
@@ -48,15 +48,24 @@
 #include "../mystring.h"
 #ifdef SHELL
 #include "../output.h"
+#undef stdout
 #define stdout out1
+#undef stderr
 #define stderr out2
 #define printf out1fmt
+#undef putc
 #define putc(c, file)	outc(c, file)
+#undef putchar
 #define putchar(c)	out1c(c)
 #define fprintf outfmt
 #define fputs outstr
 #define fflush flushout
 #define INITARGS(argv)
+#define warnx(a, b, c) {                                               \
+       char buf[64];                                                   \
+       (void)snprintf(buf, sizeof(buf), a, b, c);                      \
+       error(buf);                                                     \
+}
 #else
 #undef NULL
 #include <stdio.h>
diff -ur ../sh.netbsd/bltin/echo.1 ./bltin/echo.1
--- ../sh.netbsd/bltin/echo.1	Fri Oct 13 18:44:30 1995
+++ ./bltin/echo.1	Sun Oct  6 07:57:46 1996
@@ -44,11 +44,11 @@
 .Nm echo
 .Nd produce message in a shell script
 .Sh SYNOPSIS
-.Nm echo
+.Nm
 .Op Fl n | Fl e
 .Ar args... 
 .Sh DESCRIPTION
-.Nm Echo
+.Nm
 prints its arguments on the standard output, separated by spaces.
 Unless the
 .Fl n
@@ -56,7 +56,7 @@
 The
 .Fl e
 option causes
-.Nm echo
+.Nm
 to treat the escape sequences specially, as described in the following
 paragraph.
 The
@@ -78,7 +78,7 @@
 .It Li \ec
 Subsequent output is suppressed.  This is normally used at the end of the
 last argument to suppress the trailing newline that
-.Nm echo
+.Nm
 would otherwise output.
 .It Li \ef
 Output a form feed.
@@ -108,6 +108,6 @@
 C language mechanism.
 .Pp
 There is no way to force
-.Nm echo
+.Nm
 to treat its arguments literally, rather than interpreting them as
 options and escape sequences.
diff -ur ../sh.netbsd/bltin/echo.c ./bltin/echo.c
--- ../sh.netbsd/bltin/echo.c	Fri Oct 13 18:44:31 1995
+++ ./bltin/echo.c	Sun Oct  6 07:58:17 1996
@@ -41,7 +41,9 @@
 /*
  * Echo command.
  */
-
+#ifdef main
+#undef main
+#endif
 #define main echocmd
 
 #include "bltin.h"
diff -ur ../sh.netbsd/cd.c ./cd.c
--- ../sh.netbsd/cd.c	Fri Mar  1 06:07:53 1996
+++ ./cd.c	Sun Oct  6 07:41:50 1996
@@ -90,9 +90,14 @@
 	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 = ".";
 	}
 	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
 		path = nullstr;
@@ -125,6 +130,7 @@
 STATIC int
 docd(dest, print)
 	char *dest;
+	int print;
 {
 
 	TRACE(("docd(\"%s\", %d) called\n", dest, print));
diff -ur ../sh.netbsd/error.c ./error.c
--- ../sh.netbsd/error.c	Fri Oct 13 18:43:58 1995
+++ ./error.c	Sun Oct  6 07:36:29 1996
@@ -116,14 +116,6 @@
 
 
 
-void
-error2(a, b)
-	char *a, *b;
-	{
-	error("%s: %s", a, b);
-}
-
-
 /*
  * Error is called to raise the error exception.  If the first argument
  * is not NULL then error prints an error message using printf style
diff -ur ../sh.netbsd/error.h ./error.h
--- ../sh.netbsd/error.h	Fri Oct 13 18:43:58 1995
+++ ./error.h	Sun Oct  6 07:35:27 1996
@@ -91,7 +91,6 @@
 
 void exraise __P((int));
 void onint __P((void));
-void error2 __P((char *, char *));
 void error __P((char *, ...));
 char *errmsg __P((int, int));
 
diff -ur ../sh.netbsd/eval.c ./eval.c
--- ../sh.netbsd/eval.c	Tue Jun 11 06:14:10 1996
+++ ./eval.c	Sun Oct  6 08:43:15 1996
@@ -207,8 +207,11 @@
 		break;
 	case NAND:
 		evaltree(n->nbinary.ch1, EV_TESTED);
-		if (evalskip || exitstatus != 0)
+		if (evalskip || exitstatus != 0) {
+			/* don't bomb out on "set -e; false && true" */
+			flags |= EV_TESTED;
 			goto out;
+		}
 		evaltree(n->nbinary.ch2, flags);
 		break;
 	case NOR:
@@ -735,7 +738,9 @@
 	/* This is the child process if a fork occurred. */
 	/* Execute the command. */
 	if (cmdentry.cmdtype == CMDFUNCTION) {
+#ifdef DEBUG
 		trputs("Shell function:  ");  trargs(argv);
+#endif
 		redirect(cmd->ncmd.redirect, REDIR_PUSH);
 		saveparam = shellparam;
 		shellparam.malloc = 0;
@@ -780,7 +785,9 @@
 		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;
@@ -834,7 +841,9 @@
 			memout.buf = NULL;
 		}
 	} else {
+#ifdef DEBUG
 		trputs("normal command:  ");  trargs(argv);
+#endif
 		clearredir();
 		redirect(cmd->ncmd.redirect, 0);
 		for (sp = varlist.list ; sp ; sp = sp->next)
diff -ur ../sh.netbsd/exec.c ./exec.c
--- ../sh.netbsd/exec.c	Fri Oct 13 18:44:01 1995
+++ ./exec.c	Sun Oct  6 07:34:15 1996
@@ -136,7 +136,7 @@
 			stunalloc(cmdname);
 		}
 	}
-	error2(argv[0], errmsg(e, E_EXEC));
+	error("%s: %s", argv[0], errmsg(e, E_EXEC));
 }
 
 
@@ -755,7 +755,7 @@
 void
 getcmdentry(name, entry)
 	char *name;
-	struct cmdentry *entry; 
+	struct cmdentry *entry;
 	{
 	struct tblentry *cmdp = cmdlookup(name, 0);
 
diff -ur ../sh.netbsd/expand.c ./expand.c
--- ../sh.netbsd/expand.c	Wed Feb 14 06:13:38 1996
+++ ./expand.c	Sun Oct  6 08:02:04 1996
@@ -100,8 +100,8 @@
 STATIC void expbackq __P((union node *, int, int));
 STATIC int subevalvar __P((char *, char *, int, int, int, int));
 STATIC char *evalvar __P((char *, int));
-STATIC int varisset __P((int));
-STATIC void varvalue __P((int, int, int));
+STATIC int varisset __P((char *));
+STATIC void varvalue __P((char *, int, int));
 STATIC void recordregion __P((int, int, int));
 STATIC void ifsbreakup __P((char *, struct arglist *));
 STATIC void expandmeta __P((struct strlist *, int));
@@ -433,7 +433,7 @@
 	int varflags;
 {
 	char *startp;
-	char *loc;
+	char *loc = NULL;
 	int c = 0;
 	int saveherefd = herefd;
 	struct nodelist *saveargbackq = argbackq;
@@ -558,7 +558,7 @@
 	p = strchr(p, '=') + 1;
 again: /* jump here after setting a variable with ${var=text} */
 	if (special) {
-		set = varisset(*var);
+		set = varisset(var);
 		val = NULL;
 	} else {
 		val = lookupvar(var);
@@ -574,7 +574,7 @@
 		/* insert the value of the variable */
 		if (special) {
 			char *exp, *oexpdest = expdest;
-			varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL);
+			varvalue(var, varflags & VSQUOTE, flag & EXP_FULL);
 			if (subtype == VSLENGTH) {
 				for (exp = oexpdest;exp != expdest; exp++)
 					varlen++;
@@ -689,22 +689,23 @@
 
 STATIC int
 varisset(name)
-	char name;
-	{
+	char *name;
+{
 	char **ap;
+	int num;
 
-	if (name == '!') {
+	if (*name == '!') {
 		if (backgndpid == -1)
 			return 0;
-	} else if (name == '@' || name == '*') {
+	} else if (*name == '@' || *name == '*') {
 		if (*shellparam.p == NULL)
 			return 0;
-	} else if ((unsigned)(name -= '1') <= '9' - '1') {
+	} else if (is_digit(name)) {
+		num = atoi(name);
 		ap = shellparam.p;
-		do {
+		while (--num > 0)
 			if (*ap++ == NULL)
 				return 0;
-		} while (--name >= 0);
 	}
 	return 1;
 }
@@ -717,7 +718,7 @@
 
 STATIC void
 varvalue(name, quoted, allow_split)
-	char name;
+	char *name;
 	int quoted;
 	int allow_split;
 {
@@ -744,7 +745,7 @@
 	} while (0)
 
 
-	switch (name) {
+	switch (*name) {
 	case '$':
 		num = rootpid;
 		goto numvar;
@@ -785,9 +786,12 @@
 		STRTODEST(p);
 		break;
 	default:
-		if ((unsigned)(name -= '1') <= '9' - '1') {
-			p = shellparam.p[name];
-			STRTODEST(p);
+                if (is_digit(*name)) {
+                        num = atoi(name);
+                        if (num > 0 && num <= shellparam.nparam) {
+                                p = shellparam.p[num - 1];
+                                STRTODEST(p);
+                        }
 		}
 		break;
 	}
@@ -1117,7 +1121,7 @@
 	struct strlist *list;
 	int len;
 {
-	struct strlist *p, *q;
+	struct strlist *p, *q = NULL;
 	struct strlist **lpp;
 	int half;
 	int n;
diff -ur ../sh.netbsd/histedit.c ./histedit.c
--- ../sh.netbsd/histedit.c	Fri Oct 13 18:44:03 1995
+++ ./histedit.c	Sun Oct  6 07:22:21 1996
@@ -156,7 +156,7 @@
 
 	if (hist != NULL) {
 		cp = lookupvar("HISTSIZE");
-		if (cp == NULL || *cp == '\0' || 
+		if (cp == NULL || *cp == '\0' ||
 		   (histsize = atoi(cp)) < 0)
 			histsize = 100;
 		history(hist, H_EVENT, histsize);
@@ -205,7 +205,7 @@
 
 	if (hist == NULL)
 		error("history not active");
-	
+
 	if (argc == 1)
 		error("missing history argument");
 
@@ -278,7 +278,7 @@
 	/*
 	 * If executing, parse [old=new] now
 	 */
-	if (lflg == 0 && argc > 0 && 
+	if (lflg == 0 && argc > 0 &&
 	     ((repl = strchr(argv[0], '=')) != NULL)) {
 		pat = argv[0];
 		*repl++ = '\0';
@@ -316,7 +316,7 @@
 	}
 	/*
 	 * XXX - this should not depend on the event numbers
-	 * always increasing.  Add sequence numbers or offset 
+	 * always increasing.  Add sequence numbers or offset
 	 * to the history element in next (diskbased) release.
 	 */
 	direction = first < last ? H_PREV : H_NEXT;
@@ -332,7 +332,7 @@
 			error("can't create temporary file %s", editfile);
 		if ((efp = fdopen(fd, "w")) == NULL) {
 			close(fd);
-			error("can't allocate stdio buffer for temp\n");
+			error("can't allocate stdio buffer for temp");
 		}
 	}
 
@@ -352,7 +352,7 @@
 				out1fmt("%5d ", he->num);
 			out1str(he->str);
 		} else {
-			char *s = pat ? 
+			char *s = pat ?
 			   fc_replace(he->str, pat, repl) : (char *)he->str;
 
 			if (sflg) {
@@ -362,7 +362,7 @@
 				evalstring(s);
 				if (displayhist && hist) {
 					/*
-					 *  XXX what about recursive and 
+					 *  XXX what about recursive and
 					 *  relative histnums.
 					 */
 					history(hist, H_ENTER, s);
@@ -388,7 +388,7 @@
 		readcmdfile(editfile);	/* XXX - should read back - quick tst */
 		unlink(editfile);
 	}
-		
+
 	if (lflg == 0 && active > 0)
 		--active;
 	if (displayhist)
@@ -424,8 +424,9 @@
 not_fcnumber(s)
         char *s;
 {
+        /* NULL is not an fc number */
 	if (s == NULL)
-		return 0;
+		return (1);
         if (*s == '-')
                 s++;
 	return (!is_number(s));
@@ -472,7 +473,7 @@
 			       str);
 	} else {
 		/*
-		 * pattern 
+		 * pattern
 		 */
 		he = history(hist, H_PREV_STR, str);
 		if (he == NULL)
diff -ur ../sh.netbsd/jobs.c ./jobs.c
--- ../sh.netbsd/jobs.c	Fri Oct 13 18:44:06 1995
+++ ./jobs.c	Sun Oct  6 07:13:55 1996
@@ -56,10 +56,15 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 #endif
+#include <sys/ioctl.h>
 
 #include "shell.h"
 #if JOBS
+#ifdef OLD_TTY_DRIVER
 #include "sgtty.h"
+#else
+#include <termios.h>
+#endif
 #undef CEOF			/* syntax.h redefines this */
 #endif
 #include "redir.h"
@@ -519,7 +524,7 @@
 	TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
 	    jp - jobtab + 1));
 	return jp;
-}	
+}
 
 
 /*
@@ -580,7 +585,7 @@
 			if (mode == FORK_FG) {
 				/*** this causes superfluous TIOCSPGRPS ***/
 				if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
-					error("TIOCSPGRP failed, errno=%d\n", errno);
+					error("TIOCSPGRP failed, errno=%d", errno);
 			}
 			setsignal(SIGTSTP);
 			setsignal(SIGTTOU);
@@ -674,7 +679,7 @@
 #if JOBS
 	if (jp->jobctl) {
 		if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
-			error("TIOCSPGRP failed, errno=%d\n", errno);
+			error("TIOCSPGRP failed, errno=%d", errno);
 	}
 	if (jp->state == JOBSTOPPED)
 		curjob = jp - jobtab + 1;
diff -ur ../sh.netbsd/main.c ./main.c
--- ../sh.netbsd/main.c	Mon Dec 11 14:04:03 1995
+++ ./main.c	Sun Oct  6 07:05:07 1996
@@ -167,7 +167,7 @@
 state1:
 		state = 2;
 		read_profile(".profile");
-	} 
+	}
 state2:
 	state = 3;
 	if (getuid() == geteuid() && getgid() == getegid()) {
diff -ur ../sh.netbsd/mkinit.c ./mkinit.c
--- ../sh.netbsd/mkinit.c	Mon Feb 19 06:09:57 1996
+++ ./mkinit.c	Sun Oct  6 07:01:22 1996
@@ -94,7 +94,7 @@
 	int nleft;
 	struct block *start;
 	struct block *last;
-};      
+};
 
 struct block {
 	struct block *next;
@@ -197,6 +197,7 @@
 	{
 	FILE *fp;
 	char line[1024];
+	char line2[1024];
 	struct event *ep;
 
 	fp = ckfopen(fname, "r");
@@ -215,8 +216,20 @@
 			doinclude(line);
 		if (line[0] == 'M' && match("MKINIT", line))
 			dodecl(line, fp);
-		if (line[0] == '#' && gooddefine(line))
+		if (line[0] == '#' && gooddefine(line)) {
+		        char *cp;
+
+			strcpy(line2, line);
+			memcpy(line2, "#undef ", strlen("#undef "));
+			cp = line2 + strlen("#undef ");
+			while(*cp && (*cp == ' ' || *cp == '\t'))
+			        cp++;
+			while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n')
+			        cp++;
+			*cp++ = '\n'; *cp = '\0';
+			addstr(line2, &defines);		  
 			addstr(line, &defines);
+		}
 	}
 	fclose(fp);
 }
@@ -350,7 +363,7 @@
 		if (! amiddecls)
 			addchar('\n', &decls);
 		q = NULL;
-		for (p = line1 + 6 ; *p != '\0' && *p != '=' && *p != '/' ; p++);
+		for (p = line1 + 6 ; *p != '\0' && *p != '=' && *p != '/' && *p != '\n'; p++);
 		if (*p == '=') {		/* eliminate initialization */
 			for (q = p ; *q && *q != ';' ; q++);
 			if (*q == '\0')
diff -ur ../sh.netbsd/mknodes.c ./mknodes.c
--- ../sh.netbsd/mknodes.c	Fri Oct 13 18:44:13 1995
+++ ./mknodes.c	Sun Oct  6 06:53:07 1996
@@ -123,7 +123,7 @@
 	char **argv;
 {
 	if (argc != 3)
-		error("usage: mknodes file\n");
+		error("usage: mknodes file");
 	if ((infp = fopen(argv[1], "r")) == NULL)
 		error("Can't open %s", argv[1]);
 	while (readline()) {
diff -ur ../sh.netbsd/mksyntax.c ./mksyntax.c
--- ../sh.netbsd/mksyntax.c	Fri Oct 13 18:44:14 1995
+++ ./mksyntax.c	Sun Oct  6 06:51:56 1996
@@ -170,6 +170,7 @@
 			digit_contig = 0;
 	}
 
+	fputs("#include <ctype.h>\n", hfile);
 	fputs("#include <sys/cdefs.h>\n", hfile);
 
 	/* Generate the #define statements in the header file */
@@ -349,9 +350,9 @@
 
 static char *macro[] = {
 	"#define is_digit(c)\t((is_type+SYNBASE)[c] & ISDIGIT)",
-	"#define is_alpha(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER))",
-	"#define is_name(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER|ISUNDER))",
-	"#define is_in_name(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER|ISUNDER|ISDIGIT))",
+	"#define is_alpha(c)\t((c) != PEOF && ((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c)))",
+	"#define is_name(c)\t((c) != PEOF && ((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))",
+	"#define is_in_name(c)\t((c) != PEOF && ((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))",
 	"#define is_special(c)\t((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))",
 	NULL
 };
diff -ur ../sh.netbsd/mktokens ./mktokens
--- ../sh.netbsd/mktokens	Fri Oct 13 18:44:14 1995
+++ ./mktokens	Sun Oct  6 06:48:34 1996
@@ -72,7 +72,7 @@
 TNOT	0	"!"
 !
 nl=`wc -l /tmp/ka$$`
-exec > token.def
+exec > token.h
 awk '{print "#define " $1 " " NR-1}' /tmp/ka$$
 echo '
 /* Array indicating which tokens mark the end of a list */
diff -ur ../sh.netbsd/mystring.c ./mystring.c
--- ../sh.netbsd/mystring.c	Fri Oct 13 18:44:16 1995
+++ ./mystring.c	Sun Oct  6 08:17:09 1996
@@ -121,7 +121,7 @@
 	{
 
 	if (! is_number(s))
-		error2("Illegal number", (char *)s);
+		error("Illegal number: %s", (char *)s);
 	return atoi(s);
 }
 
diff -ur ../sh.netbsd/options.c ./options.c
--- ../sh.netbsd/options.c	Fri Oct 13 18:44:18 1995
+++ ./options.c	Sun Oct  6 06:35:50 1996
@@ -111,6 +111,10 @@
 		commandname = arg0 = *argptr++;
 		setinputfile(commandname, 0);
 	}
+	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
+	if (argptr && minusc)
+	        arg0 = *argptr++;
+
 	shellparam.p = argptr;
 	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
 	while (*argptr) {
@@ -397,12 +401,11 @@
 			q++;
 	}
 	if (*++q == ':') {
-		if (*p == '\0' && (p = *shellparam.optnext) == NULL) {
+		if (*p == '\0' && (p = *shellparam.optnext++) == NULL) {
 			out1fmt("No arg for -%c option\n", c);
 			c = '?';
 			goto out;
 		}
-		shellparam.optnext++;
 		setvar("OPTARG", p, 0);
 		p = NULL;
 	}
diff -ur ../sh.netbsd/output.c ./output.c
--- ../sh.netbsd/output.c	Fri Oct 13 18:44:19 1995
+++ ./output.c	Sun Oct  6 06:20:23 1996
@@ -354,7 +354,7 @@
 #define TEMPSIZE 24
 
 #ifdef __STDC__
-static const char digit[16] = "0123456789ABCDEF";
+static const char digit[] = "0123456789ABCDEF";
 #else
 static const char digit[17] = "0123456789ABCDEF";
 #endif
diff -ur ../sh.netbsd/parser.c ./parser.c
--- ../sh.netbsd/parser.c	Fri May 10 06:09:00 1996
+++ ./parser.c	Sun Oct  6 08:02:50 1996
@@ -72,7 +72,7 @@
 #define EOFMARKLEN 79
 
 /* values returned by readtoken */
-#include "token.def"
+#include "token.h"
 
 
 
@@ -99,7 +99,6 @@
 int quoteflag;			/* set if (part of) last token was quoted */
 int startlinno;			/* line # where last token started */
 
-
 #define GDB_HACK 1 /* avoid local declarations which gdb can't handle */
 #ifdef GDB_HACK
 static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'};
@@ -473,6 +472,8 @@
 		 */
 		if (!redir)
 			synexpect(-1);
+	case TAND:
+	case TOR:
 	case TNL:
 	case TEOF:
 	case TWORD:
@@ -510,7 +511,7 @@
 	{
 	union node *args, **app;
 	union node **orig_rpp = rpp;
-	union node *n;
+	union node *n = NULL;
 
 	/* If we don't have any redirections already, then we must reset */
 	/* rpp to be the address of the local redir variable.  */
@@ -1296,6 +1297,11 @@
 			case '\n':
 				plinno++;
 				needprompt = doprompt;
+				break;
+
+			case PEOF:
+			        startlinno = plinno;
+				synerror("EOF in backquote substitution");
 				break;
 
 			default:
diff -ur ../sh.netbsd/redir.c ./redir.c
--- ../sh.netbsd/redir.c	Fri Oct 13 18:44:23 1995
+++ ./redir.c	Thu Oct  3 08:28:51 1996
@@ -104,7 +104,7 @@
 	int flags;
 	{
 	union node *n;
-	struct redirtab *sv;
+	struct redirtab *sv = NULL;
 	int i;
 	int fd;
 	char memory[10];		/* file descriptors to write to memory */
@@ -121,6 +121,9 @@
 	}
 	for (n = redir ; n ; n = n->nfile.next) {
 		fd = n->nfile.fd;
+		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;
 			if ((i = copyfd(fd, 10)) != EMPTY) {
@@ -343,7 +346,11 @@
 	int newfd;
 
 	newfd = fcntl(from, F_DUPFD, to);
-	if (newfd < 0 && errno == EMFILE)
+	if (newfd < 0) {
+	  if (errno == EMFILE)
 		return EMPTY;
+	  else
+	    error("%d: %s", from, strerror(errno));
+	}
 	return newfd;
 }
diff -ur ../sh.netbsd/sh.1 ./sh.1
--- ../sh.netbsd/sh.1	Fri Oct 13 18:44:24 1995
+++ ./sh.1	Sun Oct  6 06:45:57 1996
@@ -885,6 +885,7 @@
 Enclosing the full parameter expansion string in double-quotes does not
 cause the following four varieties of pattern characters to be quoted,
 whereas quoting characters within the braces has this effect.
+(UNIMPLEMENTED IN 4.4alpha)
 .TP
 ${parameter%word}
 Remove Smallest Suffix Pattern.  The word
@@ -997,7 +998,7 @@
 .LP
 A pattern consists of normal characters, which match themselves, and meta-characters.   The meta-characters are
 ``!'', ``*'', ``?'', and ``[''.  These  characters lose
-there special meanings if they are quoted.  When command
+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 the command is scanned for these
@@ -1169,6 +1170,14 @@
 .TP
 getopts optstring var
 The POSIX getopts command.
+The getopts command deprecates the older getopt command.
+The first argument should be a series of letters, each possibly
+followed by a colon which indicates that the option takes an argument.
+The specified variable is set to the parsed option.  The index of
+the next argument is placed into the shell variable OPTIND.
+If an option takes an argument, it is placed into the shell variable
+OPTARG.  If an invalid option is encountered, var is set to '?'.
+It returns a false value (1) when it encounters the end of the options.
 .TP
 hash -rv command...
 The shell maintains a hash table which remembers the
@@ -1178,7 +1187,8 @@
 the last cd command are marked with an asterisk; it
 is possible for these entries to be invalid.
 .sp
-With arguments, the hash command removes the specified commands from the hash table (unless they are
+With arguments, the hash command removes the specified commands from the
+hash table (unless they are
 functions) and then locates them.   With the -v
 option, hash prints the locations of the commands as
 it finds them.  The -r option causes the hash command
@@ -1217,7 +1227,8 @@
 be treated specially.  If a backslash is followed by
 a newline, the backslash and the newline will be
 deleted.   If a backslash is followed by any other
-character, the backslash will be deleted and the following character will be treated as though it were
+character, the backslash will be deleted and the following
+character will be treated as though it were
 not in IFS, even if it is.
 .TP
 readonly name...
@@ -1243,7 +1254,8 @@
 The third use of the set command is to set the values
 of the shell's positional parameters to the specified
 args.   To change the positional parameters without
-changing any options, use ``--'' as the first argument to set.  If no args are present, the set command
+changing any options, use ``--'' as the first argument to set.
+If no args are present, the set command
 will clear all the positional parameters (equivalent
 to executing ``shift $#''.
 .TP
@@ -1308,3 +1320,9 @@
 document. It's similar to vi: typing <ESC> will throw you into
 command VI command mode. Hitting <return> while in command mode
 will pass the line to the shell.
+.SH HISTORY
+A
+.I sh
+command appeared in
+Version 1 AT&T UNIX.
+It was, however, unmaintainable so we wrote this one.
\ No newline at end of file
Only in ../sh.netbsd: sh.cat1
diff -ur ../sh.netbsd/shell.h ./shell.h
--- ../sh.netbsd/shell.h	Fri Mar  1 06:07:53 1996
+++ ./shell.h	Thu Oct  3 08:17:50 1996
@@ -56,7 +56,7 @@
 #ifndef BSD
 #define BSD 1
 #endif
-#define DEBUG 1
+/* #define DEBUG 1 */
 
 #ifdef __STDC__
 typedef void *pointer;
diff -ur ../sh.netbsd/show.c ./show.c
--- ../sh.netbsd/show.c	Fri Oct 13 18:44:25 1995
+++ ./show.c	Thu Oct  3 08:16:26 1996
@@ -291,18 +291,18 @@
 #endif
 
 
+#ifdef DEBUG
 void
 trputc(c) 
 	int c;
 {
-#ifdef DEBUG
 	if (tracefile == NULL)
 		return;
 	putc(c, tracefile);
 	if (c == '\n')
 		fflush(tracefile);
-#endif
 }
+#endif
 
 void
 #if __STDC__
@@ -331,17 +331,16 @@
 }
 
 
+#ifdef DEBUG
 void
 trputs(s)
 	char *s;
 {
-#ifdef DEBUG
 	if (tracefile == NULL)
 		return;
 	fputs(s, tracefile);
 	if (strchr(s, '\n'))
 		fflush(tracefile);
-#endif
 }
 
 
@@ -352,7 +351,6 @@
 	register char *p;
 	char c;
 
-#ifdef DEBUG
 	if (tracefile == NULL)
 		return;
 	putc('"', tracefile);
@@ -384,8 +382,8 @@
 		}
 	}
 	putc('"', tracefile);
-#endif
 }
+#endif
 
 
 void
@@ -407,6 +405,7 @@
 }
 
 
+#ifdef DEBUG
 void
 opentrace() {
 	char s[100];
@@ -415,7 +414,6 @@
 	int flags;
 #endif
 
-#ifdef DEBUG
 	if (!debug)
 		return;
 #ifdef not_this_way
@@ -443,5 +441,5 @@
 #endif
 	fputs("\nTracing started.\n", tracefile);
 	fflush(tracefile);
-#endif /* DEBUG */
 }
+#endif /* DEBUG */
diff -ur ../sh.netbsd/show.h ./show.h
--- ../sh.netbsd/show.h	Fri Oct 13 18:44:25 1995
+++ ./show.h	Thu Oct  3 08:15:40 1996
@@ -36,8 +36,10 @@
  */
 
 void showtree __P((union node *));
-void trputc __P((int));
 void trace __P((const char *, ...));
-void trputs __P((char *));
 void trargs __P((char **));
+#ifdef DEBUG
+void trputs __P((char *));
+void trputc __P((int));
 void opentrace __P((void));
+#endif
diff -ur ../sh.netbsd/var.c ./var.c
--- ../sh.netbsd/var.c	Fri Oct 13 18:44:27 1995
+++ ./var.c	Thu Oct  3 07:59:13 1996
@@ -60,6 +60,7 @@
 #include "syntax.h"
 #include "options.h"
 #include "mail.h"
+#include "parser.h"
 #include "var.h"
 #include "memalloc.h"
 #include "error.h"
@@ -193,8 +194,9 @@
 
 	isbad = 0;
 	p = name;
-	if (! is_name(*p++))
+	if (! is_name(*p))
 		isbad = 1;
+	p++;
 	for (;;) {
 		if (! is_in_name(*p)) {
 			if (*p == '\0' || *p == '=')
@@ -681,9 +683,9 @@
 	{
 	unsigned int hashval;
 
-	hashval = *p << 4;
+	hashval = ((unsigned char)*p) << 4;
 	while (*p && *p != '=')
-		hashval += *p++;
+		hashval += (unsigned char)*p++;
 	return &vartab[hashval % VTABSIZE];
 }
 
>Audit-Trail:
>Unformatted: