[comp.os.minix] shell part 1 of 2

ast@cs.vu.nl (Andy Tanenbaum) (10/06/88)

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'makefile'
sed 's/^X//' > 'makefile' << '+ END-OF-FILE ''makefile'
XCFLAGS=-F
Xl=/lib
X
Xshobj = sh1.s sh2.s sh3.s sh4.s sh5.s sh6.s
Xsh:	$(shobj) sh.h
X	@cc -i -o sh -T.  $(shobj)
X	@chmem =13000 sh
X
+ END-OF-FILE makefile
chmod 'u=rw,g=r,o=r' 'makefile'
set `wc -c 'makefile'`
count=$1
case $count in
127)	:;;
*)	echo 'Bad character count in ''makefile' >&2
		echo 'Count should be 127' >&2
esac
echo Extracting 'sh.h'
sed 's/^X//' > 'sh.h' << '+ END-OF-FILE ''sh.h'
X/* -------- sh.h -------- */
X/*
X * shell
X */
X
X#define	NULL	0
X#define	LINELIM	1000
X#define	NPUSH	8	/* limit to input nesting */
X
X#define	NOFILE	20	/* Number of open files */
X#define	NUFILE	10	/* Number of user-accessible files */
X#define	FDBASE	10	/* First file usable by Shell */
X
X/*
X * values returned by wait
X */
X#define	WAITSIG(s) ((s)&0177)
X#define	WAITVAL(s) (((s)>>8)&0377)
X#define	WAITCORE(s) (((s)&0200)!=0)
X
X/*
X * library and system defintions
X */
Xtypedef int xint;	/* base type of jmp_buf, for broken compilers */
X
X/*
X * shell components
X */
X/* #include "area.h" */
X/* #include "word.h" */
X/* #include "io.h" */
X/* #include "var.h" */
X
X#define	QUOTE	0200
X
X#define	NOBLOCK	((struct op *)NULL)
X#define	NOWORD	((char *)NULL)
X#define	NOWORDS	((char **)NULL)
X#define	NOPIPE	((int *)NULL)
X
X/*
X * Description of a command or an operation on commands.
X * Might eventually use a union.
X */
Xstruct op {
X	int	type;	/* operation type, see below */
X	char	**words;	/* arguments to a command */
X	struct	ioword	**ioact;	/* IO actions (eg, < > >>) */
X	struct op *left;
X	struct op *right;
X	char	*str;	/* identifier for case and for */
X};
X
X#define	TCOM	1	/* command */
X#define	TPAREN	2	/* (c-list) */
X#define	TPIPE	3	/* a | b */
X#define	TLIST	4	/* a [&;] b */
X#define	TOR	5	/* || */
X#define	TAND	6	/* && */
X#define	TFOR	7
X#define	TDO	8
X#define	TCASE	9
X#define	TIF	10
X#define	TWHILE	11
X#define	TUNTIL	12
X#define	TELIF	13
X#define	TPAT	14	/* pattern in case */
X#define	TBRACE	15	/* {c-list} */
X#define	TASYNC	16	/* c & */
X
X/*
X * actions determining the environment of a process
X */
X#define	BIT(i)	(1<<(i))
X#define	FEXEC	BIT(0)	/* execute without forking */
X
X/*
X * flags to control evaluation of words
X */
X#define	DOSUB	1	/* interpret $, `, and quotes */
X#define	DOBLANK	2	/* perform blank interpretation */
X#define	DOGLOB	4	/* interpret [?* */
X#define	DOKEY	8	/* move words with `=' to 2nd arg. list */
X#define	DOTRIM	16	/* trim resulting string */
X
X#define	DOALL	(DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM)
X
XExtern	char	**dolv;
XExtern	int	dolc;
XExtern	int	exstat;
XExtern  char	gflg;
XExtern  int	talking;	/* interactive (talking-type wireless) */
XExtern  int	execflg;
XExtern  int	multiline;	/* \n changed to ; */
XExtern  struct	op	*outtree;	/* result from parser */
X
XExtern	xint	*failpt;
XExtern	xint	*errpt;
X
Xstruct	brkcon {
X	jmp_buf	brkpt;
X	struct	brkcon	*nextlev;
X} ;
XExtern	struct brkcon	*brklist;
XExtern	int	isbreak;
X
X/*
X * redirection
X */
Xstruct ioword {
X	short	io_unit;	/* unit affected */
X	short	io_flag;	/* action (below) */
X	char	*io_name;	/* file name */
X};
X#define	IOREAD	1	/* < */
X#define	IOHERE	2	/* << (here file) */
X#define	IOWRITE	4	/* > */
X#define	IOCAT	8	/* >> */
X#define	IOXHERE	16	/* ${}, ` in << */
X#define	IODUP	32	/* >&digit */
X#define	IOCLOSE	64	/* >&- */
X
X#define	IODEFAULT (-1)	/* token for default IO unit */
X
XExtern	struct	wdblock	*wdlist;
XExtern	struct	wdblock	*iolist;
X
X/*
X * parsing & execution environment
X */
Xextern struct	env {
X	char	*linep;
X	struct	io	*iobase;
X	struct	io	*iop;
X	xint	*errpt;
X	int	iofd;
X	struct	env	*oenv;
X} e;
X
X/*
X * flags:
X * -e: quit on error
X * -k: look for name=value everywhere on command line
X * -n: no execution
X * -t: exit after reading and executing one command
X * -v: echo as read
X * -x: trace
X * -u: unset variables net diagnostic
X */
Xextern	char	*flag;
X
Xextern	char	*null;	/* null value for variable */
Xextern	int	intr;	/* interrupt pending */
X
XExtern	char	*trap[NSIG];
XExtern	char	ourtrap[NSIG];
XExtern	int	trapset;	/* trap pending */
X
Xextern	int	inword;	/* defer traps and interrupts */
X
XExtern	int	yynerrs;	/* yacc */
X
XExtern	char	line[LINELIM];
Xextern	char	*elinep;
X
X/*
X * other functions
X */
Xint	(*inbuilt())();	/* find builtin command */
Xchar	*rexecve();
Xchar	*space();
Xchar	*getwd();
Xchar	*strsave();
Xchar	*evalstr();
Xchar	*putn();
Xchar	*itoa();
Xchar	*unquote();
Xstruct	var	*lookup();
Xstruct	wdblock	*add2args();
Xstruct	wdblock	*glob();
Xchar	**makenv();
Xstruct	ioword	*addio();
Xchar	**eval();
Xint	setstatus();
Xint	waitfor();
X
Xint	onintr();	/* SIGINT handler */
X
X/*
X * error handling
X */
Xvoid	leave();	/* abort shell (or fail in subshell) */
Xvoid	fail();		/* fail but return to process next command */
Xint	sig();		/* default signal handler */
X
X/*
X * library functions and system calls
X */
Xlong	lseek();
Xchar	*strncpy();
Xint	strlen();
Xextern int errno;
X
X/* -------- var.h -------- */
X
Xstruct	var {
X	char	*value;
X	char	*name;
X	struct	var	*next;
X	char	status;
X};
X#define	COPYV	1	/* flag to setval, suggesting copy */
X#define	RONLY	01	/* variable is read-only */
X#define	EXPORT	02	/* variable is to be exported */
X#define	GETCELL	04	/* name & value space was got with getcell */
X
XExtern	struct	var	*vlist;		/* dictionary */
X
XExtern	struct	var	*homedir;	/* home directory */
XExtern	struct	var	*prompt;	/* main prompt */
XExtern	struct	var	*cprompt;	/* continuation prompt */
XExtern	struct	var	*path;		/* search path for commands */
XExtern	struct	var	*shell;		/* shell to interpret command files */
XExtern	struct	var	*ifs;		/* field separators */
X
Xstruct	var	*lookup(/* char *s */);
Xvoid	setval(/* struct var *, char * */);
Xvoid	nameval(/* struct var *, char *val, *name */);
Xvoid	export(/* struct var * */);
Xvoid	ronly(/* struct var * */);
Xint	isassign(/* char *s */);
Xint	checkname(/* char *name */);
Xint	assign(/* char *s, int copyflag */);
Xvoid	putvlist(/* int key, int fd */);
Xint	eqname(/* char *n1, char *n2 */);
X
X/* -------- io.h -------- */
X/* possible arguments to an IO function */
Xstruct ioarg {
X	char	*aword;
X	char	**awordlist;
X	int	afile;	/* file descriptor */
X};
X
X/* an input generator's state */
Xstruct	io {
X	int	(*iofn)();
X	struct	ioarg	arg;
X	int	peekc;
X	char	nlcount;	/* for `'s */
X	char	xchar;		/* for `'s */
X	char	task;		/* reason for pushed IO */
X};
XExtern	struct	io	iostack[NPUSH];
X#define	XOTHER	0	/* none of the below */
X#define	XDOLL	1	/* expanding ${} */
X#define	XGRAVE	2	/* expanding `'s */
X#define	XIO	3	/* file IO */
X
X/* in substitution */
X#define	INSUB()	(e.iop->task == XGRAVE || e.iop->task == XDOLL)
X
X/*
X * input generators for IO structure
X */
Xint	nlchar();
Xint	strchar();
Xint	filechar();
Xint	herechar();
Xint	linechar();
Xint	nextchar();
Xint	gravechar();
Xint	qgravechar();
Xint	dolchar();
Xint	wdchar();
X
X/*
X * IO functions
X */
Xint	getc();
Xint	readc();
Xvoid	unget();
Xvoid	ioecho();
Xvoid	prs();
Xvoid	putc();
Xvoid	prn();
Xvoid	closef();
Xvoid	closeall();
X
X/*
X * IO control
X */
Xvoid	pushio(/* struct ioarg arg, int (*gen)() */);
Xint	remap();
Xint	openpipe();
Xvoid	closepipe();
Xstruct io *setbase(/* struct io * */);
X
XExtern	struct	ioarg	temparg;	/* temporary for PUSHIO */
X#define	PUSHIO(what,arg,gen) ((temparg.what = (arg)),pushio(temparg,(gen)))
X#define	RUN(what,arg,gen) ((temparg.what = (arg)), run(temparg,(gen)))
X
X/* -------- word.h -------- */
X#ifndef WORD_H
X#define	WORD_H	1
Xstruct	wdblock {
X	short	w_bsize;
X	short	w_nword;
X	/* bounds are arbitrary */
X	char	*w_words[1];
X};
X
Xstruct	wdblock	*addword();
Xstruct	wdblock	*newword();
Xchar	**getwords();
X#endif
X
X/* -------- area.h -------- */
X
X/*
X * storage allocation
X */
Xchar	*getcell(/* unsigned size */);
Xvoid	garbage();
Xvoid	setarea(/* char *obj, int to */);
Xint	getarea(/* char *obj */);
Xvoid	freearea(/* int area */);
Xvoid	freecell(/* char *obj */);
X
XExtern	int	areanum;	/* current allocation area */
X
X#define	NEW(type) (type *)getcell(sizeof(type))
X#define	DELETE(obj)	freecell((char *)obj)
X
+ END-OF-FILE sh.h
chmod 'u=rw,g=r,o=r' 'sh.h'
set `wc -c 'sh.h'`
count=$1
case $count in
7321)	:;;
*)	echo 'Bad character count in ''sh.h' >&2
		echo 'Count should be 7321' >&2
esac
echo Extracting 'sh1.c'
sed 's/^X//' > 'sh1.c' << '+ END-OF-FILE ''sh1.c'
X#define Extern extern
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include "sh.h"
X/* -------- sh.c -------- */
X/*
X * shell
X */
X
X/* #include "sh.h" */
X
Xint	intr;
Xint	inparse;
Xchar	flags['z'-'a'+1];
Xchar	*flag = flags-'a';
Xchar	*elinep = line+sizeof(line)-5;
Xchar	*null	= "";
Xint	inword	=1;
Xstruct	env	e ={line, iostack, iostack-1, NULL, FDBASE, NULL};
X
Xextern	char	**environ;	/* environment pointer */
X
X/*
X * default shell, search rules
X */
Xchar	shellname[] = "/bin/sh";
Xchar	search[] = ":/bin:/usr/bin";
X
Xint	(*qflag)() = SIG_IGN;
X
Xmain(argc, argv)
Xint argc;
Xregister char **argv;
X{
X	register int f;
X	register char *s;
X	int cflag;
X	char *name, **ap;
X	int (*iof)();
X
X	initarea();
X	if ((ap = environ) != NULL) {
X		while (*ap)
X			assign(*ap++, !COPYV);
X		for (ap = environ; *ap;)
X			export(lookup(*ap++));
X	}
X	closeall();
X	areanum = 1;
X
X	shell = lookup("SHELL");
X	if (shell->value == null)
X		setval(shell, shellname);
X	export(shell);
X
X	homedir = lookup("HOME");
X	if (homedir->value == null)
X		setval(homedir, "/");
X	export(homedir);
X
X	setval(lookup("$"), itoa(getpid(), 5));
X
X	path = lookup("PATH");
X	if (path->value == null)
X		setval(path, search);
X	export(path);
X
X	ifs = lookup("IFS");
X	if (ifs->value == null)
X		setval(ifs, " \t\n");
X
X	prompt = lookup("PS1");
X	if (prompt->value == null)
X#ifndef UNIXSHELL
X		setval(prompt, "$ ");
X#else
X		setval(prompt, "% ");
X#endif
X	if (geteuid() == 0) {
X		setval(prompt, "# ");
X		prompt->status &= ~EXPORT;
X	}
X	cprompt = lookup("PS2");
X	if (cprompt->value == null)
X		setval(cprompt, "> ");
X
X	iof = filechar;
X	cflag = 0;
X	name = *argv++;
X	if (--argc >= 1) {
X		if(argv[0][0] == '-' && argv[0][1] != '\0') {
X			for (s = argv[0]+1; *s; s++)
X				switch (*s) {
X				case 'c':
X					prompt->status &= ~EXPORT;
X					cprompt->status &= ~EXPORT;
X					setval(prompt, "");
X					setval(cprompt, "");
X					cflag = 1;
X					if (--argc > 0)
X						PUSHIO(aword, *++argv, iof = nlchar);
X					break;
X	
X				case 'q':
X					qflag = SIG_DFL;
X					break;
X
X				case 's':
X					/* standard input */
X					break;
X
X				case 't':
X					prompt->status &= ~EXPORT;
X					setval(prompt, "");
X					iof = linechar;
X					break;
X	
X				case 'i':
X					talking++;
X				default:
X					if (*s>='a' && *s<='z')
X						flag[*s]++;
X				}
X		} else {
X			argv--;
X			argc++;
X		}
X		if (iof == filechar && --argc > 0) {
X			setval(prompt, "");
X			setval(cprompt, "");
X			prompt->status &= ~EXPORT;
X			cprompt->status &= ~EXPORT;
X			if (newfile(*++argv))
X				exit(1);
X		}
X	}
X	setdash();
X	if (e.iop < iostack) {
X		PUSHIO(afile, 0, iof);
X		if (isatty(0) && isatty(1) && !cflag)
X			talking++;
X	}
X	signal(SIGQUIT, qflag);
X	if (name && name[0] == '-') {
X		talking++;
X		if ((f = open("/etc/profile", 0)) >= 0)
X			next(remap(f));
X		if ((f = open(".profile", 0)) >= 0)
X			next(remap(f));
X	}
X	if (talking) {
X		signal(SIGTERM, sig);
X		signal(SIGINT, SIG_IGN);
X	}
X	dolv = argv;
X	dolc = argc;
X	dolv[0] = name;
X	if (dolc > 1)
X		for (ap = ++argv; --argc > 0;)
X			if (assign(*ap = *argv++, !COPYV))
X				dolc--;	/* keyword */
X			else
X				ap++;
X	setval(lookup("#"), putn(--dolc));
X
X	for (;;) {
X		if (talking && e.iop <= iostack)
X			prs(prompt->value);
X		onecommand();
X	}
X}
X
Xsetdash()
X{
X	register char *cp, c;
X	char m['z'-'a'+1];
X
X	cp = m;
X	for (c='a'; c<='z'; c++)
X		if (flag[c])
X			*cp++ = c;
X	*cp = 0;
X	setval(lookup("-"), m);
X}
X
Xnewfile(s)
Xregister char *s;
X{
X	register f;
X
X	if (strcmp(s, "-") != 0) {
X		f = open(s, 0);
X		if (f < 0) {
X			prs(s);
X			err(": cannot open");
X			return(1);
X		}
X	} else
X		f = 0;
X	next(remap(f));
X	return(0);
X}
X
Xonecommand()
X{
X	register i;
X	jmp_buf m1;
X
X	inword++;
X	while (e.oenv)
X		quitenv();
X	areanum = 1;
X	freehere(areanum);
X	freearea(areanum);
X	garbage();
X	wdlist = 0;
X	iolist = 0;
X	e.errpt = 0;
X	e.linep = line;
X	yynerrs = 0;
X	multiline = 0;
X	inparse = 1;
X	setjmp(failpt = m1);	/* Bruce Evans' fix */
X	if (talking)
X		signal(SIGINT, onintr);
X	if (setjmp(failpt = m1) || yyparse() || intr) {
X		while (e.oenv)
X			quitenv();
X		scraphere();
X		inparse = 0;
X		intr = 0;
X		return;
X	}
X	inparse = 0;
X	inword = 0;
X	if ((i = trapset) != 0) {
X		trapset = 0;
X		runtrap(i);
X	}
X	brklist = 0;
X	intr = 0;
X	execflg = 0;
X	if (!flag['n']) {
X		if (talking)
X			signal(SIGINT, onintr);
X		execute(outtree, NOPIPE, NOPIPE, 0);
X		intr = 0;
X		if (talking)
X			signal(SIGINT, SIG_IGN);
X	}
X}
X
Xvoid
Xfail()
X{
X	longjmp(failpt, 1);
X	/* NOTREACHED */
X}
X
Xvoid
Xleave()
X{
X	if (execflg)
X		fail();
X	runtrap(0);
X	sync();
X	exit(exstat);
X	/* NOTREACHED */
X}
X
Xwarn(s)
Xregister char *s;
X{
X	if(*s) {
X		prs(s);
X		exstat = -1;
X	}
X	prs("\n");
X	if (flag['e'])
X		leave();
X}
X
Xerr(s)
Xchar *s;
X{
X	warn(s);
X	if (flag['n'])
X		return;
X	if (!talking)
X		leave();
X	if (e.errpt)
X		longjmp(e.errpt, 1);
X	closeall();
X	e.iop = e.iobase = iostack;
X}
X
Xnewenv(f)
X{
X	register struct env *ep;
X
X	if (f) {
X		quitenv();
X		return(1);
X	}
X	ep = (struct env *) space(sizeof(*ep));
X	if (ep == NULL) {
X		while (e.oenv)
X			quitenv();
X		fail();
X	}
X	*ep = e;
X	e.oenv = ep;
X	e.errpt = errpt;
X	return(0);
X}
X
Xquitenv()
X{
X	register struct env *ep;
X	register fd;
X
X	if ((ep = e.oenv) != NULL) {
X		fd = e.iofd;
X		e = *ep;
X		/* should close `'d files */
X		DELETE(ep);
X		while (--fd >= e.iofd)
X			close(fd);
X	}
X}
X
X/*
X * Is any character from s1 in s2?
X */
Xint
Xanys(s1, s2)
Xregister char *s1, *s2;
X{
X	while (*s1)
X		if (any(*s1++, s2))
X			return(1);
X	return(0);
X}
X
X/*
X * Is character c in s?
X */
Xint
Xany(c, s)
Xregister int c;
Xregister char *s;
X{
X	while (*s)
X		if (*s++ == c)
X			return(1);
X	return(0);
X}
X
Xchar *
Xputn(n)
Xregister n;
X{
X	return(itoa(n, -1));
X}
X
Xchar *
Xitoa(u, n)
Xregister unsigned u;
X{
X	register char *cp;
X	static char s[20];
X	int m;
X
X	m = 0;
X	if (n < 0 && (int) u < 0) {
X		m++;
X		u = -u;
X	}
X	cp = s+sizeof(s);
X	*--cp = 0;
X	do {
X		*--cp = u%10 + '0';
X		u /= 10;
X	} while (--n > 0 || u);
X	if (m)
X		*--cp = '-';
X	return(cp);
X}
X
Xnext(f)
X{
X	PUSHIO(afile, f, nextchar);
X}
X
Xonintr()
X{
X	signal(SIGINT, SIG_IGN);
X	if (inparse) {
X		prs("\n");
X		fail();
X	}
X	intr++;
X}
X
Xletter(c)
Xregister c;
X{
X	return(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
X}
X
Xdigit(c)
Xregister c;
X{
X	return(c >= '0' && c <= '9');
X}
X
Xletnum(c)
Xregister c;
X{
X	return(letter(c) || digit(c));
X}
X
Xchar *
Xspace(n)
Xint n;
X{
X	register char *cp;
X
X	inword++;
X	if ((cp = getcell(n)) == 0)
X		err("out of string space");
X	inword--;
X	return(cp);
X}
X
Xchar *
Xstrsave(s, a)
Xregister char *s;
X{
X	register char *cp, *xp;
X
X	if ((cp = space(strlen(s)+1)) != NULL) {
X		setarea((char *)cp, a);
X		for (xp = cp; (*xp++ = *s++) != '\0';)
X			;
X		return(cp);
X	}
X	return("");
X}
X
X/*
X * if inword is set, traps
X * are delayed, avoiding
X * having two people allocating
X * at once.
X */
Xxfree(s)
Xregister char *s;
X{
X	inword++;
X	DELETE(s);
X	inword--;
X}
X
X/*
X * trap handling
X */
Xsig(i)
Xregister i;
X{
X	if (inword == 0) {
X		signal(i, SIG_IGN);
X		runtrap(i);
X	} else
X		trapset = i;
X	signal(i, sig);
X}
X
Xruntrap(i)
X{
X	char *trapstr;
X
X	if ((trapstr = trap[i]) == NULL)
X		return;
X	if (i == 0)
X		trap[i] = 0;
X	RUN(aword, trapstr, nlchar);
X}
X
X/* -------- var.c -------- */
X/* #include "sh.h" */
X
Xstatic	char	*findeq();
X
X/*
X * Find the given name in the dictionary
X * and return its value.  If the name was
X * not previously there, enter it now and
X * return a null value.
X */
Xstruct var *
Xlookup(n)
Xregister char *n;
X{
X	register struct var *vp;
X	register char *cp;
X	register int c;
X	static struct var dummy;
X
X	if (digit(*n)) {
X		dummy.name = n;
X		for (c = 0; digit(*n) && c < 1000; n++)
X			c = c*10 + *n-'0';
X		dummy.status = RONLY;
X		dummy.value = c <= dolc? dolv[c]: null;
X		return(&dummy);
X	}
X	for (vp = vlist; vp; vp = vp->next)
X		if (eqname(vp->name, n))
X			return(vp);
X	cp = findeq(n);
X	vp = (struct var *)space(sizeof(*vp));
X	if (vp == 0 || (vp->name = space((int)(cp-n)+2)) == 0) {
X		dummy.name = dummy.value = "";
X		return(&dummy);
X	}
X	for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++)
X		;
X	if (*cp == 0)
X		*cp = '=';
X	*++cp = 0;
X	setarea((char *)vp, 0);
X	setarea((char *)vp->name, 0);
X	vp->value = null;
X	vp->next = vlist;
X	vp->status = GETCELL;
X	vlist = vp;
X	return(vp);
X}
X
X/*
X * give variable at `vp' the value `val'.
X */
Xvoid
Xsetval(vp, val)
Xstruct var *vp;
Xchar *val;
X{
X	nameval(vp, val, (char *)NULL);
X}
X
X/*
X * if name is not NULL, it must be
X * a prefix of the space `val',
X * and end with `='.
X * this is all so that exporting
X * values is reasonably painless.
X */
Xvoid
Xnameval(vp, val, name)
Xregister struct var *vp;
Xchar *val, *name;
X{
X	register char *cp, *xp;
X	char *nv;
X	int fl;
X
X	if (vp->status & RONLY) {
X		for (xp = vp->name; *xp && *xp != '=';)
X			putc(*xp++);
X		err(" is read-only");
X		return;
X	}
X	fl = 0;
X	if (name == NULL) {
X		xp = space(strlen(vp->name)+strlen(val)+2);
X		if (xp == 0)
X			return;
X		/* make string:  name=value */
X		setarea((char *)xp, 0);
X		name = xp;
X		for (cp = vp->name; (*xp = *cp++) && *xp!='='; xp++)
X			;
X		if (*xp++ == 0)
X			xp[-1] = '=';
X		nv = xp;
X		for (cp = val; (*xp++ = *cp++) != '\0';)
X			;
X		val = nv;
X		fl = GETCELL;
X	}
X	if (vp->status & GETCELL)
X		xfree(vp->name);	/* form new string `name=value' */
X	vp->name = name;
X	vp->value = val;
X	vp->status |= fl;
X}
X
Xvoid
Xexport(vp)
Xstruct var *vp;
X{
X	vp->status |= EXPORT;
X}
X
Xvoid
Xronly(vp)
Xstruct var *vp;
X{
X	if (letter(vp->name[0]))	/* not an internal symbol ($# etc) */
X		vp->status |= RONLY;
X}
X
Xint
Xisassign(s)
Xregister char *s;
X{
X	if (!letter(*s))
X		return(0);
X	for (; *s != '='; s++)
X		if (*s == 0 || !letnum(*s))
X			return(0);
X	return(1);
X}
X
Xint
Xassign(s, cf)
Xregister char *s;
Xint cf;
X{
X	register char *cp;
X	struct var *vp;
X
X	if (!letter(*s))
X		return(0);
X	for (cp = s; *cp != '='; cp++)
X		if (*cp == 0 || !letnum(*cp))
X			return(0);
X	vp = lookup(s);
X	nameval(vp, ++cp, cf == COPYV? NULL: s);
X	if (cf != COPYV)
X		vp->status &= ~GETCELL;
X	return(1);
X}
X
Xint
Xcheckname(cp)
Xregister char *cp;
X{
X	if (!letter(*cp++))
X		return(0);
X	while (*cp)
X		if (!letnum(*cp++))
X			return(0);
X	return(1);
X}
X
Xvoid
Xputvlist(f, out)
Xregister int f, out;
X{
X	register struct var *vp;
X
X	for (vp = vlist; vp; vp = vp->next)
X		if (vp->status & f && letter(*vp->name)) {
X			if (vp->status & EXPORT)
X				write(out, "export ", 7);
X			if (vp->status & RONLY)
X				write(out, "readonly ", 9);
X			write(out, vp->name, (int)(findeq(vp->name) - vp->name));
X			write(out, "\n", 1);
X		}
X}
X
Xint
Xeqname(n1, n2)
Xregister char *n1, *n2;
X{
X	for (; *n1 != '=' && *n1 != 0; n1++)
X		if (*n2++ != *n1)
X			return(0);
X	return(*n2 == 0 || *n2 == '=');
X}
X
Xstatic char *
Xfindeq(cp)
Xregister char *cp;
X{
X	while (*cp != '\0' && *cp != '=')
X		cp++;
X	return(cp);
X}
X
X/* -------- gmatch.c -------- */
X/*
X * int gmatch(string, pattern)
X * char *string, *pattern;
X *
X * Match a pattern as in sh(1).
X */
X
X#define	NULL	0
X#define	CMASK	0377
X#define	QUOTE	0200
X#define	QMASK	(CMASK&~QUOTE)
X#define	NOT	'!'	/* might use ^ */
X
Xstatic	char	*cclass();
X
Xint
Xgmatch(s, p)
Xregister char *s, *p;
X{
X	register int sc, pc;
X
X	if (s == NULL || p == NULL)
X		return(0);
X	while ((pc = *p++ & CMASK) != '\0') {
X		sc = *s++ & QMASK;
X		switch (pc) {
X		case '[':
X			if ((p = cclass(p, sc)) == NULL)
X				return(0);
X			break;
X
X		case '?':
X			if (sc == 0)
X				return(0);
X			break;
X
X		case '*':
X			s--;
X			do {
X				if (*p == '\0' || gmatch(s, p))
X					return(1);
X			} while (*s++ != '\0');
X			return(0);
X
X		default:
X			if (sc != (pc&~QUOTE))
X				return(0);
X		}
X	}
X	return(*s == 0);
X}
X
Xstatic char *
Xcclass(p, sub)
Xregister char *p;
Xregister int sub;
X{
X	register int c, d, not, found;
X
X	if ((not = *p == NOT) != 0)
X		p++;
X	found = not;
X	do {
X		if (*p == '\0')
X			return(NULL);
X		c = *p & CMASK;
X		if (p[1] == '-' && p[2] != ']') {
X			d = p[2] & CMASK;
X			p++;
X		} else
X			d = c;
X		if (c == sub || c <= sub && sub <= d)
X			found = !not;
X	} while (*++p != ']');
X	return(found? p+1: NULL);
X}
X
X/* -------- area.c -------- */
X#define	REGSIZE		sizeof(struct region)
X#define GROWBY		256
X#undef	SHRINKBY	64
X#define FREE 32767
X#define BUSY 0
X#define	ALIGN (sizeof(int)-1)
X
X/* #include "area.h" */
X#define	NULL	0
X
Xstruct region {
X	struct	region *next;
X	int	area;
X};
X
X/*
X * All memory between (char *)areabot and (char *)(areatop+1) is
X * exclusively administered by the area management routines.
X * It is assumed that sbrk() and brk() manipulate the high end.
X */
Xstatic	struct region *areabot;		/* bottom of area */
Xstatic	struct region *areatop;		/* top of area */
Xstatic	struct region *areanxt;		/* starting point of scan */
Xchar	*sbrk();
Xchar	*brk();
X
Xinitarea()
X{
X	while ((int)sbrk(0) & ALIGN)
X		sbrk(1);
X	areabot = (struct region *)sbrk(REGSIZE);
X	areabot->next = areabot;
X	areabot->area = BUSY;
X	areatop = areabot;
X	areanxt = areabot;
X}
X
Xchar *
Xgetcell(nbytes)
Xunsigned nbytes;
X{
X	register int nregio;
X	register struct region *p, *q;
X	register i;
X
X	if (nbytes == 0)
X		abort();	/* silly and defeats the algorithm */
X	/*
X	 * round upwards and add administration area
X	 */
X	nregio = (nbytes+(REGSIZE-1))/REGSIZE + 1;
X	for (p = areanxt;;) {
X		if (p->area > areanum) {
X			/*
X			 * merge free cells
X			 */
X			while ((q = p->next)->area > areanum)
X				p->next = q->next;
X			/*
X			 * exit loop if cell big enough
X			 */
X			if (q >= p + nregio)
X				goto found;
X		}
X		p = p->next;
X		if (p == areanxt)
X			break;
X	}
X	i = nregio >= GROWBY ? nregio : GROWBY;
X	p = (struct region *)sbrk(i * REGSIZE);
X	if ((int)p == -1)
X		return(NULL);
X	p--;
X	if (p != areatop)
X		abort();	/* allocated areas are contiguous */
X	q = p + i;
X	p->next = q;
X	p->area = FREE;
X	q->next = areabot;
X	q->area = BUSY;
X	areatop = q;
Xfound:
X	/*
X	 * we found a FREE area big enough, pointed to by 'p', and up to 'q'
X	 */
X	areanxt = p + nregio;
X	if (areanxt < q) {
X		/*
X		 * split into requested area and rest
X		 */
X		if (areanxt+1 > q)
X			abort();	/* insufficient space left for admin */
X		areanxt->next = q;
X		areanxt->area = FREE;
X		p->next = areanxt;
X	}
X	p->area = areanum;
X	return((char *)(p+1));
X}
X
Xvoid
Xfreecell(cp)
Xchar *cp;
X{
X	register struct region *p;
X
X	if ((p = (struct region *)cp) != NULL) {
X		p--;
X		if (p < areanxt)
X			areanxt = p;
X		p->area = FREE;
X	}
X}
X
Xvoid
Xfreearea(a)
Xregister int a;
X{
X	register struct region *p, *top;
X
X	top = areatop;
X	for (p = areabot; p != top; p = p->next)
X		if (p->area >= a)
X			p->area = FREE;
X}
X
Xvoid
Xsetarea(cp,a)
Xchar *cp;
Xint a;
X{
X	register struct region *p;
X
X	if ((p = (struct region *)cp) != NULL)
X		(p-1)->area = a;
X}
X
Xint
Xgetarea(cp)
Xchar *cp;
X{
X	return ((struct region*)cp-1)->area;
X}
X
Xvoid
Xgarbage()
X{
X	register struct region *p, *q, *top;
X
X	top = areatop;
X	for (p = areabot; p != top; p = p->next) {
X		if (p->area > areanum) {
X			while ((q = p->next)->area > areanum)
X				p->next = q->next;
X			areanxt = p;
X		}
X	}
X#ifdef SHRINKBY
X	if (areatop >= q + SHRINKBY && q->area > areanum) {
X		brk((char *)(q+1));
X		q->next = areabot;
X		q->area = BUSY;
X		areatop = q;
X	}
X#endif
X}
+ END-OF-FILE sh1.c
chmod 'u=rw,g=r,o=r' 'sh1.c'
set `wc -c 'sh1.c'`
count=$1
case $count in
14561)	:;;
*)	echo 'Bad character count in ''sh1.c' >&2
		echo 'Count should be 14561' >&2
esac
echo Extracting 'sh2.c'
sed 's/^X//' > 'sh2.c' << '+ END-OF-FILE ''sh2.c'
X#define Extern extern
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include "sh.h"
X
X/* -------- csyn.c -------- */
X/*
X * shell: syntax (C version)
X */
X
Xtypedef union {
X	char	*cp;
X	char	**wp;
X	int	i;
X	struct	op *o;
X} YYSTYPE;
X#define	WORD	256
X#define	LOGAND	257
X#define	LOGOR	258
X#define	BREAK	259
X#define	IF	260
X#define	THEN	261
X#define	ELSE	262
X#define	ELIF	263
X#define	FI	264
X#define	CASE	265
X#define	ESAC	266
X#define	FOR	267
X#define	WHILE	268
X#define	UNTIL	269
X#define	DO	270
X#define	DONE	271
X#define	IN	272
X#define	YYERRCODE 300
X
X/* flags to yylex */
X#define	CONTIN	01	/* skip new lines to complete command */
X
X/* #include "sh.h" */
X#define	SYNTAXERR	zzerr()
Xstatic	int	startl = 1;
Xstatic	int	peeksym = 0;
Xstatic	void	zzerr();
Xstatic	void	word();
Xstatic	char	**copyw();
Xstatic	struct	op *block(), *namelist(), *list(), *newtp();
Xstatic	struct	op *pipeline(), *andor(), *command();
Xstatic	struct	op *nested(), *simple(), *c_list();
Xstatic	struct	op *dogroup(), *thenpart(), *casepart(), *caselist();
Xstatic	struct	op *elsepart();
Xstatic	char	**wordlist(), **pattern();
Xstatic	void	musthave();
Xstatic	int	yylex();
Xstatic	struct ioword *io();
Xstatic	struct ioword **copyio();
Xstatic	char	*tree();
Xstatic	void	diag();
Xstatic	int	nlseen;
Xstatic	int	iounit = IODEFAULT;
Xstatic	struct	op	*tp;
Xstruct	op	*newtp();
X
Xstatic	YYSTYPE	yylval;
X
Xint
Xyyparse()
X{
X	peeksym = 0;
X	yynerrs = 0;
X	outtree = c_list();
X	musthave('\n', 0);
X	return(yynerrs!=0);
X}
X
Xstatic struct op *
Xpipeline(cf)
Xint cf;
X{
X	register struct op *t, *p;
X	register int c;
X
X	t = command(cf);
X	if (t != NULL) {
X		while ((c = yylex(0)) == '|') {
X			if ((p = command(CONTIN)) == NULL)
X				SYNTAXERR;
X			if (t->type != TPAREN && t->type != TCOM) {
X				/* shell statement */
X				t = block(TPAREN, t, NOBLOCK, NOWORDS);
X			}
X			t = block(TPIPE, t, p, NOWORDS);
X		}
X		peeksym = c;
X	}
X	return(t);
X}
X
Xstatic struct op *
Xandor()
X{
X	register struct op *t, *p;
X	register int c;
X
X	t = pipeline(0);
X	if (t != NULL) {
X		while ((c = yylex(0)) == LOGAND || c == LOGOR) {
X			if ((p = pipeline(CONTIN)) == NULL)
X				SYNTAXERR;
X			t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
X		}
X		peeksym = c;
X	}
X	return(t);
X}
X
Xstatic struct op *
Xc_list()
X{
X	register struct op *t, *p;
X	register int c;
X
X	t = andor();
X	if (t != NULL) {
X		if((peeksym = yylex(0)) == '&')
X			t = block(TASYNC, t, NOBLOCK, NOWORDS);
X		while ((c = yylex(0)) == ';' || c == '&' || multiline && c == '\n') {
X			if ((p = andor()) == NULL)
X				return(t);
X			if((peeksym = yylex(0)) == '&')
X				p = block(TASYNC, p, NOBLOCK, NOWORDS);
X			t = list(t, p);
X		}
X		peeksym = c;
X	}
X	return(t);
X}
X
X
Xstatic int
Xsynio(cf)
Xint cf;
X{
X	register struct ioword *iop;
X	register int i;
X	register int c;
X
X	if ((c = yylex(cf)) != '<' && c != '>') {
X		peeksym = c;
X		return(0);
X	}
X	i = yylval.i;
X	musthave(WORD, 0);
X	iop = io(iounit, i, yylval.cp);
X	iounit = IODEFAULT;
X	if (i & IOHERE)
X		markhere(yylval.cp, iop);
X}
X
Xstatic void
Xmusthave(c, cf)
Xint c, cf;
X{
X	if ((peeksym = yylex(cf)) != c)
X		SYNTAXERR;
X	peeksym = 0;
X}
X
Xstatic struct op *
Xsimple()
X{
X	register struct op *t;
X
X	t = NULL;
X	for (;;) {
X		switch (peeksym = yylex(0)) {
X		case '<':
X		case '>':
X			(void) synio(0);
X			break;
X
X		case WORD:
X			if (t == NULL) {
X				t = newtp();
X				t->type = TCOM;
X			}
X			peeksym = 0;
X			word(yylval.cp);
X			break;
X
X		default:
X			return(t);
X		}
X	}
X}
X
Xstatic struct op *
Xnested(type, mark)
Xint type, mark;
X{
X	register struct op *t;
X
X	multiline++;
X	t = c_list();
X	musthave(mark, 0);
X	multiline--;
X	return(block(type, t, NOBLOCK, NOWORDS));
X}
X
Xstatic struct op *
Xcommand(cf)
Xint cf;
X{
X	register struct ioword *io;
X	register struct op *t;
X	struct wdblock *iosave;
X	register int c;
X
X	iosave = iolist;
X	iolist = NULL;
X	if (multiline)
X		cf |= CONTIN;
X	while (synio(cf))
X		cf = 0;
X	switch (c = yylex(cf)) {
X	default:
X		peeksym = c;
X		if ((t = simple()) == NULL) {
X			if (iolist == NULL)
X				return(NULL);
X			t = newtp();
X			t->type = TCOM;
X		}
X		break;
X
X	case '(':
X		t = nested(TPAREN, ')');
X		break;
X
X	case '{':
X		t = nested(TBRACE, '}');
X		break;
X
X	case FOR:
X		t = newtp();
X		t->type = TFOR;
X		musthave(WORD, 0);
X		startl = 1;
X		t->str = yylval.cp;
X		multiline++;
X		t->words = wordlist();
X		if ((c = yylex(0)) != '\n' && c != ';')
X			SYNTAXERR;
X		t->left = dogroup(0);
X		multiline--;
X		break;
X
X	case WHILE:
X	case UNTIL:
X		multiline++;
X		t = newtp();
X		t->type = c == WHILE? TWHILE: TUNTIL;
X		t->left = c_list();
X		t->right = dogroup(1);
X		t->words = NULL;
X		multiline--;
X		break;
X
X	case CASE:
X		t = newtp();
X		t->type = TCASE;
X		musthave(WORD, 0);
X		t->str = yylval.cp;
X		startl++;
X		multiline++;
X		musthave(IN, CONTIN);
X		startl++;
X		t->left = caselist();
X		musthave(ESAC, 0);
X		multiline--;
X		break;
X
X	case IF:
X		multiline++;
X		t = newtp();
X		t->type = TIF;
X		t->left = c_list();
X		t->right = thenpart();
X		musthave(FI, 0);
X		multiline--;
X		break;
X	}
X	while (synio(0))
X		;
X	t = namelist(t);
X	iolist = iosave;
X	return(t);
X}
X
Xstatic struct op *
Xdogroup(onlydone)
Xint onlydone;
X{
X	register int c;
X	register struct op *list;
X
X	c = yylex(CONTIN);
X	if (c == DONE && onlydone)
X		return(NULL);
X	if (c != DO)
X		SYNTAXERR;
X	list = c_list();
X	musthave(DONE, 0);
X	return(list);
X}
X
Xstatic struct op *
Xthenpart()
X{
X	register int c;
X	register struct op *t;
X
X	if ((c = yylex(0)) != THEN) {
X		peeksym = c;
X		return(NULL);
X	}
X	t = newtp();
X	t->type = 0;
X	t->left = c_list();
X	if (t->left == NULL)
X		SYNTAXERR;
X	t->right = elsepart();
X	return(t);
X}
X
Xstatic struct op *
Xelsepart()
X{
X	register int c;
X	register struct op *t;
X
X	switch (c = yylex(0)) {
X	case ELSE:
X		if ((t = c_list()) == NULL)
X			SYNTAXERR;
X		return(t);
X
X	case ELIF:
X		t = newtp();
X		t->type = TELIF;
X		t->left = c_list();
X		t->right = thenpart();
X		return(t);
X
X	default:
X		peeksym = c;
X		return(NULL);
X	}
X}
X
Xstatic struct op *
Xcaselist()
X{
X	register struct op *t;
X	register int c;
X
X	t = NULL;
X	while ((peeksym = yylex(CONTIN)) != ESAC)
X		t = list(t, casepart());
X	return(t);
X}
X
Xstatic struct op *
Xcasepart()
X{
X	register struct op *t;
X	register int c;
X
X	t = newtp();
X	t->type = TPAT;
X	t->words = pattern();
X	musthave(')', 0);
X	t->left = c_list();
X	if ((peeksym = yylex(CONTIN)) != ESAC)
X		musthave(BREAK, CONTIN);
X	return(t);
X}
X
Xstatic char **
Xpattern()
X{
X	register int c, cf;
X
X	cf = CONTIN;
X	do {
X		musthave(WORD, cf);
X		word(yylval.cp);
X		cf = 0;
X	} while ((c = yylex(0)) == '|');
X	peeksym = c;
X	word(NOWORD);
X	return(copyw());
X}
X
Xstatic char **
Xwordlist()
X{
X	register int c;
X
X	if ((c = yylex(0)) != IN) {
X		peeksym = c;
X		return(NULL);
X	}
X	startl = 0;
X	while ((c = yylex(0)) == WORD)
X		word(yylval.cp);
X	word(NOWORD);
X	peeksym = c;
X	return(copyw());
X}
X
X/*
X * supporting functions
X */
Xstatic struct op *
Xlist(t1, t2)
Xregister struct op *t1, *t2;
X{
X	if (t1 == NULL)
X		return(t2);
X	if (t2 == NULL)
X		return(t1);
X	return(block(TLIST, t1, t2, NOWORDS));
X}
X
Xstatic struct op *
Xblock(type, t1, t2, wp)
Xstruct op *t1, *t2;
Xchar **wp;
X{
X	register struct op *t;
X
X	t = newtp();
X	t->type = type;
X	t->left = t1;
X	t->right = t2;
X	t->words = wp;
X	return(t);
X}
X
Xstruct res {
X	char	*r_name;
X	int	r_val;
X} restab[] = {
X	"for",		FOR,
X	"case",		CASE,
X	"esac",		ESAC,
X	"while",	WHILE,
X	"do",		DO,
X	"done",		DONE,
X	"if",		IF,
X	"in",		IN,
X	"then",		THEN,
X	"else",		ELSE,
X	"elif",		ELIF,
X	"until",	UNTIL,
X	"fi",		FI,
X
X	";;",		BREAK,
X	"||",		LOGOR,
X	"&&",		LOGAND,
X	"{",		'{',
X	"}",		'}',
X
X	0,
X};
X
Xrlookup(n)
Xregister char *n;
X{
X	register struct res *rp;
X
X	for (rp = restab; rp->r_name; rp++)
X		if (strcmp(rp->r_name, n) == 0)
X			return(rp->r_val);
X	return(0);
X}
X
Xstatic struct op *
Xnewtp()
X{
X	register struct op *t;
X
X	t = (struct op *)tree(sizeof(*t));
X	t->type = 0;
X	t->words = NULL;
X	t->ioact = NULL;
X	t->left = NULL;
X	t->right = NULL;
X	t->str = NULL;
X	return(t);
X}
X
Xstatic struct op *
Xnamelist(t)
Xregister struct op *t;
X{
X	if (iolist) {
X		iolist = addword((char *)NULL, iolist);
X		t->ioact = copyio();
X	} else
X		t->ioact = NULL;
X	if (t->type != TCOM) {
X		if (t->type != TPAREN && t->ioact != NULL) {
X			t = block(TPAREN, t, NOBLOCK, NOWORDS);
X			t->ioact = t->left->ioact;
X			t->left->ioact = NULL;
X		}
X		return(t);
X	}
X	word(NOWORD);
X	t->words = copyw();
X	return(t);
X}
X
Xstatic char **
Xcopyw()
X{
X	register char **wd;
X
X	wd = getwords(wdlist);
X	wdlist = 0;
X	return(wd);
X}
X
Xstatic void
Xword(cp)
Xchar *cp;
X{
X	wdlist = addword(cp, wdlist);
X}
X
Xstatic struct ioword **
Xcopyio()
X{
X	register struct ioword **iop;
X
X	iop = (struct ioword **) getwords(iolist);
X	iolist = 0;
X	return(iop);
X}
X
Xstatic struct ioword *
Xio(u, f, cp)
Xchar *cp;
X{
X	register struct ioword *iop;
X
X	iop = (struct ioword *) tree(sizeof(*iop));
X	iop->io_unit = u;
X	iop->io_flag = f;
X	iop->io_name = cp;
X	iolist = addword((char *)iop, iolist);
X	return(iop);
X}
X
Xstatic void
Xzzerr()
X{
X	yyerror("syntax error");
X}
X
Xyyerror(s)
Xchar *s;
X{
X	yynerrs++;
X	if (talking) {
X		if (multiline && nlseen)
X			unget('\n');
X		multiline = 0;
X		while (yylex(0) != '\n')
X			;
X	}
X	err(s);
X	fail();
X}
X
Xstatic int
Xyylex(cf)
Xint cf;
X{
X	register int c, c1;
X	int atstart;
X
X	if ((c = peeksym) > 0) {
X		peeksym = 0;
X		if (c == '\n')
X			startl = 1;
X		return(c);
X	}
X	nlseen = 0;
X	e.linep = line;
X	atstart = startl;
X	startl = 0;
X	yylval.i = 0;
X
Xloop:
X	while ((c = getc(0)) == ' ' || c == '\t')
X		;
X	switch (c) {
X	default:
X		if (any(c, "0123456789")) {
X			unget(c1 = getc(0));
X			if (c1 == '<' || c1 == '>') {
X				iounit = c - '0';
X				goto loop;
X			}
X			*e.linep++ = c;
X			c = c1;
X		}
X		break;
X
X	case '#':
X		while ((c = getc(0)) != 0 && c != '\n')
X			;
X		unget(c);
X		goto loop;
X
X	case 0:
X		return(c);
X
X	case '$':
X		*e.linep++ = c;
X		if ((c = getc(0)) == '{') {
X			if ((c = collect(c, '}')) != '\0')
X				return(c);
X			goto pack;
X		}
X		break;
X
X	case '`':
X	case '\'':
X	case '"':
X		if ((c = collect(c, c)) != '\0')
X			return(c);
X		goto pack;
X
X	case '|':
X	case '&':
X	case ';':
X		if ((c1 = dual(c)) != '\0') {
X			startl = 1;
X			return(c1);
X		}
X		startl = 1;
X		return(c);
X	case '^':
X		startl = 1;
X		return('|');
X	case '>':
X	case '<':
X		diag(c);
X		return(c);
X
X	case '\n':
X		nlseen++;
X		gethere();
X		startl = 1;
X		if (multiline || cf & CONTIN) {
X			if (talking && e.iop <= iostack)
X				prs(cprompt->value);
X			if (cf & CONTIN)
X				goto loop;
X		}
X		return(c);
X
X	case '(':
X	case ')':
X		startl = 1;
X		return(c);
X	}
X
X	unget(c);
X
Xpack:
X	while ((c = getc(0)) != 0 && !any(c, "`$ '\"\t;&<>()|^\n"))
X		if (e.linep >= elinep)
X			err("word too long");
X		else
X			*e.linep++ = c;
X	unget(c);
X	if(any(c, "\"'`$"))
X		goto loop;
X	*e.linep++ = '\0';
X	if (atstart && (c = rlookup(line))!=0) {
X		startl = 1;
X		return(c);
X	}
X	yylval.cp = strsave(line, areanum);
X	return(WORD);
X}
X
Xint
Xcollect(c, c1)
Xregister c, c1;
X{
X	char s[2];
X
X	*e.linep++ = c;
X	while ((c = getc(c1)) != c1) {
X		if (c == 0) {
X			unget(c);
X			s[0] = c1;
X			s[1] = 0;
X			prs("no closing "); yyerror(s);
X			return(YYERRCODE);
X		}
X		if (talking && c == '\n' && e.iop <= iostack)
X			prs(cprompt->value);
X		*e.linep++ = c;
X	}
X	*e.linep++ = c;
X	return(0);
X}
X
Xint
Xdual(c)
Xregister c;
X{
X	char s[3];
X	register char *cp = s;
X
X	*cp++ = c;
X	*cp++ = getc(0);
X	*cp = 0;
X	if ((c = rlookup(s)) == 0)
X		unget(*--cp);
X	return(c);
X}
X
Xstatic void
Xdiag(ec)
Xregister int ec;
X{
X	register int c;
X
X	c = getc(0);
X	if (c == '>' || c == '<') {
X		if (c != ec)
X			zzerr();
X		yylval.i = ec == '>'? IOWRITE|IOCAT: IOHERE;
X		c = getc(0);
X	} else
X		yylval.i = ec == '>'? IOWRITE: IOREAD;
X	if (c != '&' || yylval.i == IOHERE)
X		unget(c);
X	else
X		yylval.i |= IODUP;
X}
X
Xstatic char *
Xtree(size)
Xunsigned size;
X{
X	register char *t;
X
X	if ((t = getcell(size)) == NULL) {
X		prs("command line too complicated\n");
X		fail();
X		/* NOTREACHED */
X	}
X	return(t);
X}
X
X/* VARARGS1 */
X/* ARGSUSED */
Xprintf(s)	/* yyparse calls it */
Xchar *s;
X{
X}
X
+ END-OF-FILE sh2.c
chmod 'u=rw,g=r,o=r' 'sh2.c'
set `wc -c 'sh2.c'`
count=$1
case $count in
11571)	:;;
*)	echo 'Bad character count in ''sh2.c' >&2
		echo 'Count should be 11571' >&2
esac
exit 0