[comp.os.minix] MINIX 1.2 shell

ast@cs.vu.nl (Andy Tanenbaum) (08/05/87)

Here is the 1.2 shell in 2 parts.  It has a number of improvements over the
1.1 shell, and should be fairly close to the shell to be used on the Atari
MINIX system.

I have just noticed a bug in it that I can't find.  The bug may well also
have been in the 1.1 shell, but I can't check it out because 1.1 doesn't
run on the hard disk I used to test 1.2.  Anyway, I made a shell script
'doit' consisting of:

ck /usr/ast/minix/kernel
ck /usr/ast/minix/kernel/MINIX
ck /usr/ast/minix/lib
...

In short, ck is run with each directory on the hard disk as an argument.
The shell script 'ck' is:

ls -l * */* >/dev/null 2>&1


Finally, from the console I started the command

while true
do sh -v doit
done >log 2>&1


This command generates quite a few shells floating around.  It runs fine
for a few minutes, but when doit hits the line containing ck /usr/ast/minix/lib
it hangs.  You can't kill it with DEL or CTRL-\.  F1 works, and from looking
at several samples, the shell seems to be looping in getcell or sometimes in
execve.  I suspect that it ran out of memory somehow.  When I chmem'd from the
standard 8000 bytes to 16000, the problem remained.

After trying this test several times and having to reboot after each test, I
made a small change to tty.c and mm/signal.c.  Now the F9 key does the
equivalent of the super-user typing:  kill -1 9.  In other words, it sends a
signal 9 to every process in the system.  While this is definitely a sledge
hammer approach, it de-hangs the system instantly.  It also kills your shell
and logs you out.  So be it.  I would prefer to have found the bug in the
shell, but not having done so, this will have to do for the time being.
The F9 key will be in 1.2, which I am almost done with.  I will post the
definitive diff listing between 1.1 and 1.2 in perhaps a week.  If anyone
can reproduce, find, and fix the shell bug, please post it.

Andy Tanenbaum

------------------------------MINIX 1.2 shell (part 1 of 2) -------------
: 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
echo Extracting \m\a\k\e\f\i\l\e
sed 's/^X//' > \m\a\k\e\f\i\l\e << '+ END-OF-FILE '\m\a\k\e\f\i\l\e
XCFLAGS=-F
Xl=../../lib
X
Xshobj = sh1.s sh2.s sh3.s sh4.s sh5.s sh6.s
Xsh:	$(shobj) sh.h
X	@asld -i -o sh -T. $l/crtso.s $(shobj) $l/libc.a $l/end.s
X	@chmem =8000 sh
X
+ END-OF-FILE makefile
chmod 'u=rw,g=r,o=r' \m\a\k\e\f\i\l\e
set `sum \m\a\k\e\f\i\l\e`
sum=$1
case $sum in
45952)	:;;
*)	echo 'Bad sum in '\m\a\k\e\f\i\l\e >&2
esac
echo Extracting \s\h\.\h
sed 's/^X//' > \s\h\.\h << '+ END-OF-FILE '\s\h\.\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	union {
X		char	*io_name;	/* file name */
X		struct block *io_here;	/* here structure pointer */
X	} io_un;
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	4	/* file IO */
X#define XHERE	0x80	/* Any of the above inside a here document */
X#define XMASK	0x7f	/* Get the actual task */
X
X/* in substitution */
X#define	INSUB()	((e.iop->task&XMASK)==XGRAVE||(e.iop->task&XMASK)==XDOLL)
X
X/*
X * input generators for IO structure
X */
Xint	nlchar();
Xint	strchar();
Xint	filechar();
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 */);
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' \s\h\.\h
set `sum \s\h\.\h`
sum=$1
case $sum in
29038)	:;;
*)	echo 'Bad sum in '\s\h\.\h >&2
esac
echo Extracting \s\h\1\.\c
sed 's/^X//' > \s\h\1\.\c << '+ END-OF-FILE '\s\h\1\.\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[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-1));
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	freearea(areanum = 1);
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	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
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' \s\h\1\.\c
set `sum \s\h\1\.\c`
sum=$1
case $sum in
04453)	:;;
*)	echo 'Bad sum in '\s\h\1\.\c >&2
esac
echo Extracting \s\h\2\.\c
sed 's/^X//' > \s\h\2\.\c << '+ END-OF-FILE '\s\h\2\.\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		while ((c = yylex(0)) == ';' || c == '&' || multiline && c == '\n') {
X			if (c == '&')
X				t = block(TASYNC, t, NOBLOCK, NOWORDS);
X			if ((p = andor()) == NULL)
X				return(t);
X			t = list(t, p);
X		}
X		peeksym = c;
X	}
X	return(t);
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_un.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' \s\h\2\.\c
set `sum \s\h\2\.\c`
sum=$1
case $sum in
34787)	:;;
*)	echo 'Bad sum in '\s\h\2\.\c >&2
esac
exit 0

mmdf@udel.UUCP (08/08/87)

Andy Tanenbaum's recent posting of the 1.2 Minix shell was in two parts.
I never got the second of the two.  Can anybody out there help?

Many thanks!
					-Frank
					mstan!frank@uunet.UU.NET

egisin@orchid.waterloo.edu (Eric Gisin) (08/08/87)

I've haven't received part two either, but this version contains
patches that Jim Paradis posted several weeks ago.

He tries to implement "<<" redirection with temp files
but does it incorrectly. Consider the following shell script.
$ for x in a b; do
> cat <<.
x=$x
.
> done

The shell has to create a temp file with 'x=$x' during parsing,
then read the first temp file and do dollar expansion into a second
temp file before "cat" is executed.
Jim's fixes try to do this with one pass, which is impossible
so his version doesn't work for the above example.

I reported this bug to ast about a week ago, but I don't think
he received my mail.  I do have a correct fix for << redirection
that uses temp files.

ast@cs.vu.nl (Andy Tanenbaum) (08/09/87)

In article <10150@orchid.waterloo.edu> egisin@orchid.waterloo.edu (Eric Gisin) writes:
>I've haven't received part two either, but this version contains
>patches that Jim Paradis posted several weeks ago.
>
The 1.2 shell incorporates fixes from multiple sources, so it is not
identical to Jim's.

If Part 2 got lost somewhere, I can repost it.  Will a couple of people
please let me know (but not all of you-- 12,000 people on USENET 
according to the July Arbitron ratings, plus ? on Bitnet, Arpanet, ...)

Andy Tanenbaum (ast@cs.vu.nl)

randy@umn-cs.UUCP (Randy Orrison) (08/10/87)

Part 2 did not make it to umn-cs either.
Please send a copy to ...!umn-cs!local-minix if you don't re-post.
	-randy
-- 
Randy Orrison, University of Minnesota School of Mathematics
UUCP:	{ihnp4, seismo!rutgers!umnd-cs, sun}!umn-cs!randy
ARPA:	randy@ux.acss.umn.edu		 (Yes, these are three
BITNET:	randy@umnacvx			 different machines)

ast@cs.vu.nl (Andy Tanenbaum) (08/10/87)

: 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
echo Extracting \m\a\k\e\f\i\l\e
sed 's/^X//' > \m\a\k\e\f\i\l\e << '+ END-OF-FILE '\m\a\k\e\f\i\l\e
XCFLAGS=-F
Xl=../../lib
X
Xshobj = sh1.s sh2.s sh3.s sh4.s sh5.s sh6.s
Xsh:	$(shobj) sh.h
X	@asld -i -o sh -T. $l/crtso.s $(shobj) $l/libc.a $l/end.s
X	@chmem =8000 sh
X
+ END-OF-FILE makefile
chmod 'u=rw,g=r,o=r' \m\a\k\e\f\i\l\e
set `sum \m\a\k\e\f\i\l\e`
sum=$1
case $sum in
45952)	:;;
*)	echo 'Bad sum in '\m\a\k\e\f\i\l\e >&2
esac
echo Extracting \s\h\.\h
sed 's/^X//' > \s\h\.\h << '+ END-OF-FILE '\s\h\.\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	union {
X		char	*io_name;	/* file name */
X		struct block *io_here;	/* here structure pointer */
X	} io_un;
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	4	/* file IO */
X#define XHERE	0x80	/* Any of the above inside a here document */
X#define XMASK	0x7f	/* Get the actual task */
X
X/* in substitution */
X#define	INSUB()	((e.iop->task&XMASK)==XGRAVE||(e.iop->task&XMASK)==XDOLL)
X
X/*
X * input generators for IO structure
X */
Xint	nlchar();
Xint	strchar();
Xint	filechar();
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 */);
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' \s\h\.\h
set `sum \s\h\.\h`
sum=$1
case $sum in
29038)	:;;
*)	echo 'Bad sum in '\s\h\.\h >&2
esac
echo Extracting \s\h\1\.\c
sed 's/^X//' > \s\h\1\.\c << '+ END-OF-FILE '\s\h\1\.\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[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-1));
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	freearea(areanum = 1);
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	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
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' \s\h\1\.\c
set `sum \s\h\1\.\c`
sum=$1
case $sum in
04453)	:;;
*)	echo 'Bad sum in '\s\h\1\.\c >&2
esac
echo Extracting \s\h\2\.\c
sed 's/^X//' > \s\h\2\.\c << '+ END-OF-FILE '\s\h\2\.\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		while ((c = yylex(0)) == ';' || c == '&' || multiline && c == '\n') {
X			if (c == '&')
X				t = block(TASYNC, t, NOBLOCK, NOWORDS);
X			if ((p = andor()) == NULL)
X				return(t);
X			t = list(t, p);
X		}
X		peeksym = c;
X	}
X	return(t);
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_un.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' \s\h\2\.\c
set `sum \s\h\2\.\c`
sum=$1
case $sum in
34787)	:;;
*)	echo 'Bad sum in '\s\h\2\.\c >&2
esac
exit 0

ast@cs.vu.nl (Andy Tanenbaum) (08/10/87)

: 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
echo Extracting \s\h\3\.\c
sed 's/^X//' > \s\h\3\.\c << '+ END-OF-FILE '\s\h\3\.\c
X#define Extern extern
X#include "signal.h"
X#include "errno.h"
X#include "setjmp.h"
X#include "sh.h"
X
X/* -------- exec.c -------- */
X/* #include "sh.h" */
X
X/*
X * execute tree
X */
X
Xstatic	char	*signame[] = {
X	"Signal 0",
X	"Hangup",
X	NULL,	/* interrupt */
X	"Quit",
X	"Illegal instruction",
X	"Trace/BPT trap",
X	"abort",
X	"EMT trap",
X	"Floating exception",
X	"Killed",
X	"Bus error",
X	"Memory fault",
X	"Bad system call",
X	NULL,	/* broken pipe */
X	"Alarm clock",
X	"Terminated",
X};
X#define	NSIGNAL (sizeof(signame)/sizeof(signame[0]))
X
Xstatic	struct	op *findcase();
Xstatic	void	brkset();
Xstatic	void	echo();
Xstatic	int	forkexec();
Xstatic	int	parent();
X
Xint
Xexecute(t, pin, pout, act)
Xregister struct op *t;
Xint *pin, *pout;
Xint act;
X{
X	register struct op *t1;
X	int i, pv[2], rv, child, a;
X	char *cp, **wp, **wp2;
X	struct var *vp;
X	struct brkcon bc;
X
X	if (t == NULL)
X		return(0);
X	rv = 0;
X	a = areanum++;
X	wp = (wp2 = t->words) != NULL? eval(wp2, DOALL): NULL;
X
X	switch(t->type) {
X	case TPAREN:
X	case TCOM:
X		rv = forkexec(t, pin, pout, act, wp, &child);
X		if (child) {
X			exstat = rv;
X			leave();
X		}
X		break;
X
X	case TPIPE:
X		if ((rv = openpipe(pv)) < 0)
X			break;
X		pv[0] = remap(pv[0]);
X		pv[1] = remap(pv[1]);
X		(void) execute(t->left, pin, pv, 0);
X		rv = execute(t->right, pv, pout, 0);
X		break;
X
X	case TLIST:
X		(void) execute(t->left, pin, pout, 0);
X		rv = execute(t->right, pin, pout, 0);
X		break;
X
X	case TASYNC:
X		i = parent();
X		if (i != 0) {
X			if (i != -1) {
X				if (pin != NULL)
X					closepipe(pin);
X				if (talking) {
X					prs(putn(i));
X					prs("\n");
X				}
X			} else
X				rv = -1;
X			setstatus(rv);
X		} else {
X			signal(SIGINT, SIG_IGN);
X			signal(SIGQUIT, SIG_IGN);
X			if (talking)
X				signal(SIGTERM, SIG_DFL);
X			talking = 0;
X			if (pin == NULL) {
X				close(0);
X				open("/dev/null", 0);
X			}
X			exit(execute(t->left, pin, pout, FEXEC));
X		}
X		break;
X
X	case TOR:
X	case TAND:
X		rv = execute(t->left, pin, pout, 0);
X		if ((t1 = t->right)!=NULL && (rv == 0) == (t->type == TAND))
X			rv = execute(t1, pin, pout, 0);
X		break;
X
X	case TFOR:
X		if (wp == NULL) {
X			wp = dolv+1;
X			if ((i = dolc-1) < 0)
X				i = 0;
X		} else
X			i = -1;
X		vp = lookup(t->str);
X		while (setjmp(bc.brkpt))
X			if (isbreak)
X				goto broken;
X		brkset(&bc);
X		for (t1 = t->left; i-- && *wp != NULL;) {
X			setval(vp, *wp++);
X			rv = execute(t1, pin, pout, 0);
X		}
X		brklist = brklist->nextlev;
X		break;
X
X	case TWHILE:
X	case TUNTIL:
X		while (setjmp(bc.brkpt))
X			if (isbreak)
X				goto broken;
X		brkset(&bc);
X		t1 = t->left;
X		while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
X			rv = execute(t->right, pin, pout, 0);
X		brklist = brklist->nextlev;
X		break;
X
X	case TIF:
X	case TELIF:
X		rv = !execute(t->left, pin, pout, 0)?
X			execute(t->right->left, pin, pout, 0):
X			execute(t->right->right, pin, pout, 0);
X		break;
X
X	case TCASE:
X		if ((cp = evalstr(t->str, DOSUB|DOTRIM)) == 0)
X			cp = "";
X		if ((t1 = findcase(t->left, cp)) != NULL)
X			rv = execute(t1, pin, pout, 0);
X		break;
X
X	case TBRACE:
X/*
X		if (iopp = t->ioact)
X			while (*iopp)
X				if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) {
X					rv = -1;
X					break;
X				}
X*/
X		if (rv >= 0 && (t1 = t->left))
X			rv = execute(t1, pin, pout, 0);
X		break;
X	}
X
Xbroken:
X	t->words = wp2;
X	isbreak = 0;
X	freearea(areanum);
X	areanum = a;
X	if (intr) {
X		closeall();
X		fail();
X	}
X	return(rv);
X}
X
Xstatic int
Xforkexec(t, pin, pout, act, wp, pforked)
Xregister struct op *t;
Xint *pin, *pout;
Xint act;
Xchar **wp;
Xint *pforked;
X{
X	int i, rv, (*shcom)();
X	int doexec();
X	register int f;
X	char *cp;
X	struct ioword **iopp;
X	int resetsig;
X
X	resetsig = 0;
X	*pforked = 0;
X	shcom = NULL;
X	rv = -1;	/* system-detected error */
X	if (t->type == TCOM) {
X		/* strip all initial assignments */
X		/* not correct wrt PATH=yyy command  etc */
X		if (flag['x'])
X			echo(wp);
X		while ((cp = *wp++) != NULL && assign(cp, COPYV))
X			;
X		wp--;
X		if (cp == NULL && t->ioact == NULL)
X			return(setstatus(0));
X		else
X			shcom = inbuilt(cp);
X	}
X	t->words = wp;
X	f = act;
X	if (shcom == NULL && (f & FEXEC) == 0) {
X		i = parent();
X		if (i != 0) {
X			if (i == -1)
X				return(rv);
X			if (pin != NULL)
X				closepipe(pin);
X			return(pout==NULL? setstatus(waitfor(i,0)): 0);
X		}
X		if (talking) {
X			signal(SIGINT, SIG_IGN);
X			signal(SIGQUIT, SIG_IGN);
X			resetsig = 1;
X		}
X		talking = 0;
X		intr = 0;
X		(*pforked)++;
X		brklist = 0;
X		execflg = 0;
X	}
X#ifdef COMPIPE
X	if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) {
X		err("piping to/from shell builtins not yet done");
X		return(-1);
X	}
X#endif
X	if (pin != NULL) {
X		dup2(pin[0], 0);
X		closepipe(pin);
X	}
X	if (pout != NULL) {
X		dup2(pout[1], 1);
X		closepipe(pout);
X	}
X	if ((iopp = t->ioact) != NULL) {
X		if (shcom != NULL && shcom != doexec) {
X			prs(cp);
X			err(": cannot redirect shell command");
X			return(-1);
X		}
X		while (*iopp)
X			if (iosetup(*iopp++, pin!=NULL, pout!=NULL))
X				return(rv);
X	}
X	if (shcom)
X		return(setstatus((*shcom)(t)));
X	/* should use FIOCEXCL */
X	for (i=FDBASE; i<NOFILE; i++)
X		close(i);
X	if (t->type == TPAREN)
X		exit(execute(t->left, NOPIPE, NOPIPE, FEXEC));
X	if (resetsig) {
X		signal(SIGINT, SIG_DFL);
X		signal(SIGQUIT, SIG_DFL);
X	}
X	if (wp[0] == NULL)
X		exit(0);
X	cp = rexecve(wp[0], wp, makenv(wp));
X	prs(wp[0]); prs(": "); warn(cp);
X	if (!execflg)
X		trap[0] = NULL;
X	leave();
X	/* NOTREACHED */
X}
X
X/*
X * common actions when creating a new child
X */
Xstatic int
Xparent()
X{
X	register int i;
X
X	i = fork();
X	if (i != 0) {
X		if (i == -1)
X			warn("try again");
X		setval(lookup("!"), putn(i));
X	}
X	return(i);
X}
X
X/*
X * 0< 1> are ignored as required
X * within pipelines.
X */
Xiosetup(iop, pipein, pipeout)
Xregister struct ioword *iop;
Xint pipein, pipeout;
X{
X	register u;
X	char *cp, *msg;
X
X	if (iop->io_unit == IODEFAULT)	/* take default */
X		iop->io_unit = iop->io_flag&(IOREAD|IOHERE)? 0: 1;
X	if (pipein && iop->io_unit == 0)
X		return(0);
X	if (pipeout && iop->io_unit == 1)
X		return(0);
X	msg = iop->io_flag&(IOREAD|IOHERE)? "open": "create";
X	if ((iop->io_flag & IOHERE) == 0) {
X		cp = iop->io_un.io_name;
X		if ((cp = evalstr(cp, DOSUB|DOTRIM)) == NULL)
X			return(1);
X	}
X	if (iop->io_flag & IODUP) {
X		if (cp[1] || !digit(*cp) && *cp != '-') {
X			prs(cp);
X			err(": illegal >& argument");
X			return(1);
X		}
X		if (*cp == '-')
X			iop->io_flag = IOCLOSE;
X		iop->io_flag &= ~(IOREAD|IOWRITE);
X	}
X	switch (iop->io_flag) {
X	case IOREAD:
X		u = open(cp, 0);
X		break;
X
X	case IOHERE:
X	case IOHERE|IOXHERE:
X		u = herein(iop->io_un.io_here, iop->io_flag&IOXHERE);
X		cp = "here file";
X		break;
X
X	case IOWRITE|IOCAT:
X		if ((u = open(cp, 1)) >= 0) {
X			lseek(u, (long)0, 2);
X			break;
X		}
X	case IOWRITE:
X		u = creat(cp, 0666);
X		break;
X
X	case IODUP:
X		u = dup2(*cp-'0', iop->io_unit);
X		break;
X
X	case IOCLOSE:
X		close(iop->io_unit);
X		return(0);
X	}
X	if (u < 0) {
X		prs(cp);
X		prs(": cannot ");
X		warn(msg);
X		return(1);
X	} else {
X		if (u != iop->io_unit) {
X			dup2(u, iop->io_unit);
X			close(u);
X		}
X	}
X	return(0);
X}
X
Xstatic void
Xecho(wp)
Xregister char **wp;
X{
X	register i;
X
X	prs("+");
X	for (i=0; wp[i]; i++) {
X		if (i)
X			prs(" ");
X		prs(wp[i]);
X	}
X	prs("\n");
X}
X
Xstatic struct op **
Xfind1case(t, w)
Xstruct op *t;
Xchar *w;
X{
X	register struct op *t1;
X	struct op **tp;
X	register char **wp, *cp;
X
X	if (t == NULL)
X		return(NULL);
X	if (t->type == TLIST) {
X		if ((tp = find1case(t->left, w)) != NULL)
X			return(tp);
X		t1 = t->right;	/* TPAT */
X	} else
X		t1 = t;
X	for (wp = t1->words; *wp;)
X		if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp))
X			return(&t1->left);
X	return(NULL);
X}
X
Xstatic struct op *
Xfindcase(t, w)
Xstruct op *t;
Xchar *w;
X{
X	register struct op **tp;
X
X	return((tp = find1case(t, w)) != NULL? *tp: NULL);
X}
X
X/*
X * Enter a new loop level (marked for break/continue).
X */
Xstatic void
Xbrkset(bc)
Xstruct brkcon *bc;
X{
X	bc->nextlev = brklist;
X	brklist = bc;
X}
X
X/*
X * Wait for the last process created.
X * Print a message for each process found
X * that was killed by a signal.
X * Ignore interrupt signals while waiting
X * unless `canintr' is true.
X */
Xint
Xwaitfor(lastpid, canintr)
Xregister int lastpid;
Xint canintr;
X{
X	register int pid, rv;
X	int s;
X
X	rv = 0;
X	do {
X		pid = wait(&s);
X		if (pid == -1) {
X			if (errno != EINTR || canintr)
X				break;
X		} else {
X			if ((rv = WAITSIG(s)) != 0) {
X				if (rv < NSIGNAL) {
X					if (signame[rv] != NULL) {
X						if (pid != lastpid) {
X							prn(pid);
X							prs(": ");
X						}
X						prs(signame[rv]);
X					}
X				} else {
X					if (pid != lastpid) {
X						prn(pid);
X						prs(": ");
X					}
X					prs("Signal "); prn(rv); prs(" ");
X				}
X				if (WAITCORE(s))
X					prs(" - core dumped");
X				prs("\n");
X				rv = -1;
X			} else
X				rv = WAITVAL(s);
X		}
X/* Special patch for MINIX: sync before each command */
X		sync();
X	} while (pid != lastpid);
X	return(rv);
X}
X
Xint
Xsetstatus(s)
Xregister int s;
X{
X	exstat = s;
X	setval(lookup("?"), putn(s));
X	return(s);
X}
X
X/*
X * PATH-searching interface to execve.
X * If getenv("PATH") were kept up-to-date,
X * execvp might be used.
X */
Xchar *
Xrexecve(c, v, envp)
Xchar *c, **v, **envp;
X{
X	register int i;
X	register char *sp, *tp;
X	int eacces = 0, asis = 0;
X	extern int errno;
X
X	sp = any('/', c)? "": path->value;
X	asis = *sp == '\0';
X	while (asis || *sp != '\0') {
X		asis = 0;
X		tp = e.linep;
X		for (; *sp != '\0'; tp++)
X			if ((*tp = *sp++) == ':') {
X				asis = *sp == '\0';
X				break;
X			}
X		if (tp != e.linep)
X			*tp++ = '/';
X		for (i = 0; (*tp++ = c[i++]) != '\0';)
X			;
X		execve(e.linep, v, envp);
X		switch (errno) {
X		case ENOEXEC:
X			*v = e.linep;
X			tp = *--v;
X			*v = e.linep;
X			execve("/bin/sh", v, envp);
X			*v = tp;
X			return("no Shell");
X
X		case ENOMEM:
X			return("program too big");
X
X		case E2BIG:
X			return("argument list too long");
X
X		case EACCES:
X			eacces++;
X			break;
X		}
X	}
X	return(errno==ENOENT ? "not found" : "cannot execute");
X}
X
X/*
X * Run the command produced by generator `f'
X * applied to stream `arg'.
X */
Xrun(arg, f)
Xstruct ioarg arg;
Xint (*f)();
X{
X	struct op *otree;
X	struct wdblock *swdlist;
X	struct wdblock *siolist;
X	jmp_buf ev, rt;
X	xint *ofail;
X	int rv;
X
X	areanum++;
X	swdlist = wdlist;
X	siolist = iolist;
X	otree = outtree;
X	ofail = failpt;
X	rv = -1;
X	if (newenv(setjmp(errpt = ev)) == 0) {
X		wdlist = 0;
X		iolist = 0;
X		pushio(arg, f);
X		e.iobase = e.iop;
X		yynerrs = 0;
X		if (setjmp(failpt = rt) == 0 && yyparse() == 0)
X			rv = execute(outtree, NOPIPE, NOPIPE, 0);
X		quitenv();
X	}
X	wdlist = swdlist;
X	iolist = siolist;
X	failpt = ofail;
X	outtree = otree;
X	freearea(areanum--);
X	return(rv);
X}
X
X/* -------- do.c -------- */
X/* #include "sh.h" */
X
X/*
X * built-in commands: doX
X */
X
Xstatic	void	rdexp();
Xstatic	void	badid();
Xstatic	int	brkcontin();
X
Xdolabel()
X{
X	return(0);
X}
X
Xdochdir(t)
Xregister struct op *t;
X{
X	register char *cp, *er;
X
X	if ((cp = t->words[1]) == NULL && (cp = homedir->value) == NULL)
X		er = ": no home directory";
X	else if(chdir(cp) < 0)
X		er = ": bad directory";
X	else
X		return(0);
X	prs(cp != NULL? cp: "cd");
X	err(er);
X	return(1);
X}
X
Xdoshift(t)
Xregister struct op *t;
X{
X	register n;
X
X	n = t->words[1]? getn(t->words[1]): 1;
X	if(dolc < n) {
X		err("nothing to shift");
X		return(1);
X	}
X	dolv[n] = dolv[0];
X	dolv += n;
X	dolc -= n;
X	setval(lookup("#"), putn(dolc));
X	return(0);
X}
X
X/*
X * execute login and newgrp directly
X */
Xdologin(t)
Xstruct op *t;
X{
X	register char *cp;
X
X	if (talking) {
X		signal(SIGINT, SIG_DFL);
X		signal(SIGQUIT, SIG_DFL);
X	}
X	cp = rexecve(t->words[0], t->words, makenv(t->words));
X	prs(t->words[0]); prs(": "); err(cp);
X	return(1);
X}
X
Xdoumask(t)
Xregister struct op *t;
X{
X	register int i, n;
X	register char *cp;
X
X	if ((cp = t->words[1]) == NULL) {
X		i = umask(0);
X		umask(i);
X		for (n=3*4; (n-=3) >= 0;)
X			putc('0'+((i>>n)&07));
X		putc('\n');
X	} else {
X		for (n=0; *cp>='0' && *cp<='9'; cp++)
X			n = n*8 + (*cp-'0');
X		umask(n);
X	}
X	return(0);
X}
X
Xdoexec(t)
Xregister struct op *t;
X{
X	register i;
X	jmp_buf ex;
X	xint *ofail;
X
X	t->ioact = NULL;
X	for(i = 0; (t->words[i]=t->words[i+1]) != NULL; i++)
X		;
X	if (i == 0)
X		return(1);
X	execflg = 1;
X	ofail = failpt;
X	if (setjmp(failpt = ex) == 0)
X		execute(t, NOPIPE, NOPIPE, FEXEC);
X	failpt = ofail;
X	execflg = 0;
X	return(1);
X}
X
Xdodot(t)
Xstruct op *t;
X{
X	register i;
X	register char *sp, *tp;
X	char *cp;
X
X	if ((cp = t->words[1]) == NULL)
X		return(0);
X	sp = any('/', cp)? ":": path->value;
X	while (*sp) {
X		tp = e.linep;
X		while (*sp && (*tp = *sp++) != ':')
X			tp++;
X		if (tp != e.linep)
X			*tp++ = '/';
X		for (i = 0; (*tp++ = cp[i++]) != '\0';)
X			;
X		if ((i = open(e.linep, 0)) >= 0) {
X			exstat = 0;
X			next(remap(i));
X			return(exstat);
X		}
X	}
X	prs(cp);
X	err(": not found");
X	return(-1);
X}
X
Xdowait(t)
Xstruct op *t;
X{
X	register i;
X	register char *cp;
X
X	if ((cp = t->words[1]) != NULL) {
X		i = getn(cp);
X		if (i == 0)
X			return(0);
X	} else
X		i = -1;
X	if (talking)
X		signal(SIGINT, onintr);
X	setstatus(waitfor(i, 1));
X	if (talking)
X		signal(SIGINT, SIG_IGN);
X	return(0);
X}
X
Xdoread(t)
Xstruct op *t;
X{
X	register char *cp, **wp;
X	register nb;
X
X	if (t->words[1] == NULL) {
X		err("Usage: read name ...");
X		return(1);
X	}
X	for (wp = t->words+1; *wp; wp++) {
X		for (cp = e.linep; cp < elinep-1; cp++)
X			if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) ||
X			    *cp == '\n' ||
X			    wp[1] && any(*cp, ifs->value))
X				break;
X		*cp = 0;
X		if (nb <= 0)
X			break;
X		setval(lookup(*wp), e.linep);
X	}
X	return(nb <= 0);
X}
X
Xdoeval(t)
Xregister struct op *t;
X{
X	int wdchar();
X
X	return(RUN(awordlist, t->words+1, wdchar));
X}
X
Xdotrap(t)
Xregister struct op *t;
X{
X	register char *s;
X	register n, i;
X
X	if (t->words[1] == NULL) {
X		for (i=0; i<NSIG; i++)
X			if (trap[i]) {
X				prn(i);
X				prs(": ");
X				prs(trap[i]);
X				prs("\n");
X			}
X		return(0);
X	}
X	n = getsig((s = t->words[2])!=NULL? s: t->words[1]);
X	xfree(trap[n]);
X	trap[n] = 0;
X	if (s != NULL) {
X		if ((i = strlen(s = t->words[1])) != 0) {
X			trap[n] = strsave(s, 0);
X			setsig(n, sig);
X		} else
X			setsig(n, SIG_IGN);
X	} else
X		setsig(n, (n == SIGINT || n == SIGQUIT) && talking? SIG_IGN: SIG_DFL);
X	return(0);
X}
X
Xgetsig(s)
Xchar *s;
X{
X	register int n;
X
X	if ((n = getn(s)) < 0 || n >= NSIG) {
X		err("trap: bad signal number");
X		n = 0;
X	}
X	return(n);
X}
X
Xsetsig(n, f)
Xregister n;
Xint (*f)();
X{
X	if (n == 0)
X		return;
X	if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) {
X		ourtrap[n] = 1;
X		signal(n, f);
X	}
X}
X
Xgetn(as)
Xchar *as;
X{
X	register char *s;
X	register n, m;
X
X	s = as;
X	m = 1;
X	if (*s == '-') {
X		m = -1;
X		s++;
X	}
X	for (n = 0; digit(*s); s++)
X		n = (n*10) + (*s-'0');
X	if (*s) {
X		prs(as);
X		err(": bad number");
X	}
X	return(n*m);
X}
X
Xdobreak(t)
Xstruct op *t;
X{
X	return(brkcontin(t->words[1], 1));
X}
X
Xdocontinue(t)
Xstruct op *t;
X{
X	return(brkcontin(t->words[1], 0));
X}
X
Xstatic int
Xbrkcontin(cp, val)
Xregister char *cp;
X{
X	register struct brkcon *bc;
X	register nl;
X
X	nl = cp == NULL? 1: getn(cp);
X	if (nl <= 0)
X		nl = 999;
X	do {
X		if ((bc = brklist) == NULL)
X			break;
X		brklist = bc->nextlev;
X	} while (--nl);
X	if (nl) {
X		err("bad break/continue level");
X		return(1);
X	}
X	isbreak = val;
X	longjmp(bc->brkpt, 1);
X	/* NOTREACHED */
X}
X
Xdoexit(t)
Xstruct op *t;
X{
X	register char *cp;
X
X	execflg = 0;
X	if ((cp = t->words[1]) != NULL)
X		exstat = getn(cp);
X	leave();
X}
X
Xdoexport(t)
Xstruct op *t;
X{
X	rdexp(t->words+1, export, EXPORT);
X	return(0);
X}
X
Xdoreadonly(t)
Xstruct op *t;
X{
X	rdexp(t->words+1, ronly, RONLY);
X	return(0);
X}
X
Xstatic void
Xrdexp(wp, f, key)
Xregister char **wp;
Xvoid (*f)();
Xint key;
X{
X	if (*wp != NULL) {
X		for (; *wp != NULL; wp++)
X			if (checkname(*wp))
X				(*f)(lookup(*wp));
X			else
X				badid(*wp);
X	} else
X		putvlist(key, 1);
X}
X
Xstatic void
Xbadid(s)
Xregister char *s;
X{
X	prs(s);
X	err(": bad identifier");
X}
X
Xdoset(t)
Xregister struct op *t;
X{
X	register struct var *vp;
X	register char *cp;
X	register n;
X
X	if ((cp = t->words[1]) == NULL) {
X		for (vp = vlist; vp; vp = vp->next)
X			varput(vp->name, 1);
X		return(0);
X	}
X	if (*cp == '-') {
X		t->words++;
X		if (*++cp == 0)
X			flag['x'] = flag['v'] = 0;
X		else
X			for (; *cp; cp++)
X				switch (*cp) {
X				case 'e':
X					if (!talking)
X						flag['e']++;
X					break;
X
X				default:
X					if (*cp>='a' && *cp<='z')
X						flag[*cp]++;
X					break;
X				}
X		setdash();
X	}
X	if (t->words[1]) {
X		t->words[0] = dolv[0];
X		for (n=1; t->words[n]; n++)
X			setarea((char *)t->words[n], 0);
X		dolc = n-1;
X		dolv = t->words;
X		setval(lookup("#"), putn(dolc));
X		setarea((char *)(dolv-1), 0);
X	}
X	return(0);
X}
X
Xvarput(s, out)
Xregister char *s;
X{
X	if (letnum(*s)) {
X		write(out, s, strlen(s));
X		write(out, "\n", 1);
X	}
X}
X
X
Xstruct	builtin {
X	char	*command;
X	int	(*fn)();
X};
Xstatic struct	builtin	builtin[] = {
X	":",		dolabel,
X	"cd",		dochdir,
X	"shift",	doshift,
X	"exec",		doexec,
X	"wait",		dowait,
X	"read",		doread,
X	"eval",		doeval,
X	"trap",		dotrap,
X	"break",	dobreak,
X	"continue",	docontinue,
X	"exit",		doexit,
X	"export",	doexport,
X	"readonly",	doreadonly,
X	"set",		doset,
X	".",		dodot,
X	"umask",	doumask,
X	"login",	dologin,
X	"newgrp",	dologin,
X	0,
X};
X
Xint (*inbuilt(s))()
Xregister char *s;
X{
X	register struct builtin *bp;
X
X	for (bp = builtin; bp->command != NULL; bp++)
X		if (strcmp(bp->command, s) == 0)
X			return(bp->fn);
X	return(NULL);
X}
X
+ END-OF-FILE sh3.c
chmod 'u=rw,g=r,o=r' \s\h\3\.\c
set `sum \s\h\3\.\c`
sum=$1
case $sum in
63046)	:;;
*)	echo 'Bad sum in '\s\h\3\.\c >&2
esac
echo Extracting \s\h\4\.\c
sed 's/^X//' > \s\h\4\.\c << '+ END-OF-FILE '\s\h\4\.\c
X#define Extern extern
X#include "signal.h"
X#include "errno.h"
X#include "setjmp.h"
X#include "stat.h"
X#include "sh.h"
X
X/* -------- eval.c -------- */
X/* #include "sh.h" */
X/* #include "word.h" */
X
X/*
X * ${}
X * `command`
X * blank interpretation
X * quoting
X * glob
X */
X
Xstatic	char	*blank();
Xstatic	int	grave();
Xstatic	int	expand();
Xstatic	int	dollar();
X
Xchar **
Xeval(ap, f)
Xregister char **ap;
X{
X	struct wdblock *wb;
X	char **wp;
X	jmp_buf ev;
X
X	inword++;
X	wp = NULL;
X	wb = NULL;
X	if (newenv(setjmp(errpt = ev)) == 0) {
X		wb = addword((char *)0, wb); /* space for shell name, if command file */
X		while (expand(*ap++, &wb, f))
X			;
X		wb = addword((char *)0, wb);
X		wp = getwords(wb) + 1;
X		quitenv();
X	} else
X		gflg = 1;
X	inword--;
X	return(gflg? NULL: wp);
X}
X
X/*
X * Make the exported environment from the exported
X * names in the dictionary.  Keyword assignments
X * ought to be taken from wp (the list of words on the command line)
X * but aren't, yet. Until then: ARGSUSED
X */
Xchar **
Xmakenv(wp)
Xchar **wp;
X{
X	register struct wdblock *wb;
X	register struct var *vp;
X
X	wb = NULL;
X	for (vp = vlist; vp; vp = vp->next)
X		if (vp->status & EXPORT)
X			wb = addword(vp->name, wb);
X	wb = addword((char *)0, wb);
X	return(getwords(wb));
X}
X
Xchar *
Xevalstr(cp, f)
Xregister char *cp;
Xint f;
X{
X	struct wdblock *wb;
X
X	inword++;
X	wb = NULL;
X	if (expand(cp, &wb, f)) {
X		if (wb == NULL || wb->w_nword == 0 || (cp = wb->w_words[0]) == NULL)
X			cp = "";
X		DELETE(wb);
X	} else
X		cp = NULL;
X	inword--;
X	return(cp);
X}
X
Xstatic int
Xexpand(cp, wbp, f)
Xregister char *cp;
Xregister struct wdblock **wbp;
X{
X	jmp_buf ev;
X
X	gflg = 0;
X	if (cp == NULL)
X		return(0);
X	if (!anys("$`'\"", cp) &&
X	    !anys(ifs->value, cp) &&
X	    ((f&DOGLOB)==0 || !anys("[*?", cp))) {
X		cp = strsave(cp, areanum);
X		if (f & DOTRIM)
X			unquote(cp);
X		*wbp = addword(cp, *wbp);
X		return(1);
X	}
X	if (newenv(setjmp(errpt = ev)) == 0) {
X		PUSHIO(aword, cp, strchar);
X		e.iobase = e.iop;
X		while ((cp = blank(f)) && gflg == 0) {
X			e.linep = cp;
X			cp = strsave(cp, areanum);
X			if ((f&DOGLOB) == 0) {
X				if (f & DOTRIM)
X					unquote(cp);
X				*wbp = addword(cp, *wbp);
X			} else
X				*wbp = glob(cp, *wbp);
X		}
X		quitenv();
X	} else
X		gflg = 1;
X	return(gflg == 0);
X}
X
X/*
X * Blank interpretation and quoting
X */
Xstatic char *
Xblank(f)
X{
X	register c, c1;
X	register char *sp;
X
X	sp = e.linep;
X
Xloop:
X	switch (c = subgetc('"', 0)) {
X	case 0:
X		if (sp == e.linep)
X			return(0);
X		*e.linep++ = 0;
X		return(sp);
X
X	default:
X		if (f & DOBLANK && any(c, ifs->value))
X			goto loop;
X		break;
X
X	case '"':
X	case '\'':
X		if (INSUB())
X			break;
X		for (c1 = c; (c = subgetc(c1, 1)) != c1;) {
X			if (c == 0)
X				break;
X			if (c == '\'' || !any(c, "$`\""))
X				c |= QUOTE;
X			*e.linep++ = c;
X		}
X		c = 0;
X	}
X	unget(c);
X	for (;;) {
X		c = subgetc('"', 0);
X		if (c == 0 ||
X		    f & DOBLANK && any(c, ifs->value) ||
X		    !INSUB() && any(c, "\"'`")) {
X			unget(c);
X			if (any(c, "\"'`"))
X				goto loop;
X			break;
X		}
X		*e.linep++ = c;
X	}
X	*e.linep++ = 0;
X	return(sp);
X}
X
X/*
X * Get characters, substituting for ` and $
X */
Xint
Xsubgetc(ec, quoted)
Xregister char ec;
Xint quoted;
X{
X	register char c;
X
Xagain:
X	c = getc(ec);
X	if (!INSUB() && ec != '\'') {
X		if (c == '`') {
X			if (grave(quoted) == 0)
X				return(0);
X			e.iop->task = XGRAVE;
X			goto again;
X		}
X		if (c == '$' && (c = dollar(quoted)) == 0) {
X			e.iop->task = XDOLL;
X			goto again;
X		}
X	}
X	return(c);
X}
X
X/*
X * Prepare to generate the string returned by ${} substitution.
X */
Xstatic int
Xdollar(quoted)
Xint quoted;
X{
X	int otask;
X	struct io *oiop;
X	char *dolp;
X	register char *s, c, *cp;
X	struct var *vp;
X
X	c = readc();
X	s = e.linep;
X	if (c != '{') {
X		*e.linep++ = c;
X		if (letter(c)) {
X			while ((c = readc())!=0 && letnum(c))
X				if (e.linep < elinep)
X					*e.linep++ = c;
X			unget(c);
X		}
X		c = 0;
X	} else {
X		oiop = e.iop;
X		otask = e.iop->task;
X		e.iop->task = XOTHER;
X		while ((c = subgetc('"', 0))!=0 && c!='}' && c!='\n')
X			if (e.linep < elinep)
X				*e.linep++ = c;
X		if (oiop == e.iop)
X			e.iop->task = otask;
X		if (c != '}') {
X			err("unclosed ${");
X			gflg++;
X			return(c);
X		}
X	}
X	if (e.linep >= elinep) {
X		err("string in ${} too long");
X		gflg++;
X		e.linep -= 10;
X	}
X	*e.linep = 0;
X	if (*s)
X		for (cp = s+1; *cp; cp++)
X			if (any(*cp, "=-+?")) {
X				c = *cp;
X				*cp++ = 0;
X				break;
X			}
X	if (s[1] == 0 && (*s == '*' || *s == '@')) {
X		if (dolc > 1) {
X			/* currently this does not distinguish $* and $@ */
X			/* should check dollar */
X			e.linep = s;
X			PUSHIO(awordlist, dolv+1, dolchar);
X			return(0);
X		} else {	/* trap the nasty ${=} */
X			s[0] = '1';
X			s[1] = 0;
X		}
X	}
X	vp = lookup(s);
X	if ((dolp = vp->value) == null) {
X		switch (c) {
X		case '=':
X			if (digit(*s)) {
X				err("cannot use ${...=...} with $n");
X				gflg++;
X				break;
X			}
X			setval(vp, cp);
X			dolp = vp->value;
X			break;
X
X		case '-':
X			dolp = strsave(cp, areanum);
X			break;
X
X		case '?':
X			if (*cp == 0) {
X				prs("missing value for ");
X				err(s);
X			} else
X				err(cp);
X			gflg++;
X			break;
X		}
X	} else if (c == '+')
X		dolp = strsave(cp, areanum);
X	if (flag['u'] && dolp == null) {
X		prs("unset variable: ");
X		err(s);
X		gflg++;
X	}
X	e.linep = s;
X	PUSHIO(aword, dolp, strchar);
X	return(0);
X}
X
X/*
X * Run the command in `...` and read its output.
X */
Xstatic int
Xgrave(quoted)
Xint quoted;
X{
X	register char *cp;
X	register int i;
X	int pf[2];
X
X	for (cp = e.iop->arg.aword; *cp != '`'; cp++)
X		if (*cp == 0) {
X			err("no closing `");
X			return(0);
X		}
X	if (openpipe(pf) < 0)
X		return(0);
X	if ((i = fork()) == -1) {
X		closepipe(pf);
X		err("try again");
X		return(0);
X	}
X	if (i != 0) {
X		e.iop->arg.aword = ++cp;
X		close(pf[1]);
X		PUSHIO(afile, remap(pf[0]), quoted? qgravechar: gravechar);
X		return(1);
X	}
X	*cp = 0;
X	/* allow trapped signals */
X	for (i=0; i<NSIG; i++)
X		if (ourtrap[i] && signal(i, SIG_IGN) != SIG_IGN)
X			signal(i, SIG_DFL);
X	dup2(pf[1], 1);
X	closepipe(pf);
X	flag['e'] = 0;
X	flag['v'] = 0;
X	flag['n'] = 0;
X	cp = strsave(e.iop->arg.aword, 0);
X	
X	/* jrp debug */
X	scraphere();
X
X	freearea(areanum = 1);	/* free old space */
X	e.oenv = NULL;
X	e.iop = (e.iobase = iostack) - 1;
X	unquote(cp);
X	talking = 0;
X	PUSHIO(aword, cp, nlchar);
X	onecommand();
X	exit(1);
X}
X
Xchar *
Xunquote(as)
Xregister char *as;
X{
X	register char *s;
X
X	if ((s = as) != NULL)
X		while (*s)
X			*s++ &= ~QUOTE;
X	return(as);
X}
X
X/* -------- glob.c -------- */
X/* #include "sh.h" */
X
X#define	DIRSIZ	14
Xstruct	direct
X{
X	unsigned short	d_ino;
X	char	d_name[DIRSIZ];
X};
X/*
X * glob
X */
X
X#define	scopy(x) strsave((x), areanum)
X#define	BLKSIZ	512
X#define	NDENT	((BLKSIZ+sizeof(struct direct)-1)/sizeof(struct direct))
X
Xstatic	struct wdblock	*cl, *nl;
Xstatic	char	spcl[] = "[?*";
Xstatic	int	xstrcmp();
Xstatic	char	*generate();
Xstatic	int	anyspcl();
X
Xstruct wdblock *
Xglob(cp, wb)
Xchar *cp;
Xstruct wdblock *wb;
X{
X	register i;
X	register char *pp;
X
X	if (cp == 0)
X		return(wb);
X	i = 0;
X	for (pp = cp; *pp; pp++)
X		if (any(*pp, spcl))
X			i++;
X		else if (!any(*pp & ~QUOTE, spcl))
X			*pp &= ~QUOTE;
X	if (i != 0) {
X		for (cl = addword(scopy(cp), (struct wdblock *)0); anyspcl(cl); cl = nl) {
X			nl = newword(cl->w_nword*2);
X			for(i=0; i<cl->w_nword; i++) { /* for each argument */
X				for (pp = cl->w_words[i]; *pp; pp++)
X					if (any(*pp, spcl)) {
X						globname(cl->w_words[i], pp);
X						break;
X					}
X				if (*pp == '\0')
X					nl = addword(scopy(cl->w_words[i]), nl);
X			}
X			for(i=0; i<cl->w_nword; i++)
X				DELETE(cl->w_words[i]);
X			DELETE(cl);
X		}
X		for(i=0; i<cl->w_nword; i++)
X			unquote(cl->w_words[i]);
X		glob0((char *)cl->w_words, cl->w_nword, sizeof(char *), xstrcmp);
X		if (cl->w_nword) {
X			for (i=0; i<cl->w_nword; i++)
X				wb = addword(cl->w_words[i], wb);
X			DELETE(cl);
X			return(wb);
X		}
X	}
X	wb = addword(unquote(cp), wb);
X	return(wb);
X}
X
Xglobname(we, pp)
Xchar *we;
Xregister char *pp;
X{
X	register char *np, *cp;
X	char *name, *gp, *dp;
X	int dn, j, n, k;
X	struct direct ent[NDENT];
X	char dname[DIRSIZ+1];
X	struct stat dbuf;
X
X	for (np = we; np != pp; pp--)
X		if (pp[-1] == '/')
X			break;
X	for (dp = cp = space((int)(pp-np)+3); np < pp;)
X		*cp++ = *np++;
X	*cp++ = '.';
X	*cp = '\0';
X	for (gp = cp = space(strlen(pp)+1); *np && *np != '/';)
X		*cp++ = *np++;
X	*cp = '\0';
X	dn = open(dp, 0);
X	if (dn < 0) {
X		DELETE(dp);
X		DELETE(gp);
X		return;
X	}
X	dname[DIRSIZ] = '\0';
X	while ((n = read(dn, (char *)ent, sizeof(ent))) >= sizeof(*ent)) {
X		n /= sizeof(*ent);
X		for (j=0; j<n; j++) {
X			if (ent[j].d_ino == 0)
X				continue;
X			strncpy(dname, ent[j].d_name, DIRSIZ);
X			if (dname[0] == '.' &&
X			    (dname[1] == '\0' || dname[1] == '.' && dname[2] == '\0'))
X				if (*gp != '.')
X					continue;
X			for(k=0; k<DIRSIZ; k++)
X				if (any(dname[k], spcl))
X					dname[k] |= QUOTE;
X			if (gmatch(dname, gp)) {
X				name = generate(we, pp, dname, np);
X				if (*np && !anys(np, spcl)) {
X					if (stat(name,&dbuf)) {
X						DELETE(name);
X						continue;
X					}
X				}
X				nl = addword(name, nl);
X			}
X		}
X	}
X	close(dn);
X	DELETE(dp);
X	DELETE(gp);
X}
X
X/*
X * generate a pathname as below.
X * start..end1 / middle end
X * the slashes come for free
X */
Xstatic char *
Xgenerate(start1, end1, middle, end)
Xchar *start1;
Xregister char *end1;
Xchar *middle, *end;
X{
X	char *p;
X	register char *op, *xp;
X
X	p = op = space((int)(end1-start1)+strlen(middle)+strlen(end)+2);
X	for (xp = start1; xp != end1;)
X		*op++ = *xp++;
X	for (xp = middle; (*op++ = *xp++) != '\0';)
X		;
X	op--;
X	for (xp = end; (*op++ = *xp++) != '\0';)
X		;
X	return(p);
X}
X
Xstatic int
Xanyspcl(wb)
Xregister struct wdblock *wb;
X{
X	register i;
X	register char **wd;
X
X	wd = wb->w_words;
X	for (i=0; i<wb->w_nword; i++)
X		if (anys(spcl, *wd++))
X			return(1);
X	return(0);
X}
X
Xstatic int
Xxstrcmp(p1, p2)
Xchar *p1, *p2;
X{
X	return(strcmp(*(char **)p1, *(char **)p2));
X}
X
X/* -------- word.c -------- */
X/* #include "sh.h" */
X/* #include "word.h" */
Xchar *memcpy();
X
X#define	NSTART	16	/* default number of words to allow for initially */
X
Xstruct wdblock *
Xnewword(nw)
Xregister nw;
X{
X	register struct wdblock *wb;
X
X	wb = (struct wdblock *) space(sizeof(*wb) + nw*sizeof(char *));
X	wb->w_bsize = nw;
X	wb->w_nword = 0;
X	return(wb);
X}
X
Xstruct wdblock *
Xaddword(wd, wb)
Xchar *wd;
Xregister struct wdblock *wb;
X{
X	register struct wdblock *wb2;
X	register nw;
X
X	if (wb == NULL)
X		wb = newword(NSTART);
X	if ((nw = wb->w_nword) >= wb->w_bsize) {
X		wb2 = newword(nw * 2);
X		memcpy((char *)wb2->w_words, (char *)wb->w_words, nw*sizeof(char *));
X		wb2->w_nword = nw;
X		DELETE(wb);
X		wb = wb2;
X	}
X	wb->w_words[wb->w_nword++] = wd;
X	return(wb);
X}
X
Xchar **
Xgetwords(wb)
Xregister struct wdblock *wb;
X{
X	register char **wd;
X	register nb;
X
X	if (wb == NULL)
X		return(NULL);
X	if (wb->w_nword == 0) {
X		DELETE(wb);
X		return(NULL);
X	}
X	wd = (char **) space(nb = sizeof(*wd) * wb->w_nword);
X	memcpy((char *)wd, (char *)wb->w_words, nb);
X	DELETE(wb);	/* perhaps should done by caller */
X	return(wd);
X}
X
Xint	(*func)();
Xint	globv;
X
Xglob0(a0, a1, a2, a3)
Xchar *a0;
Xunsigned a1;
Xint a2;
Xint (*a3)();
X{
X	func = a3;
X	globv = a2;
X	glob1(a0, a0 + a1 * a2);
X}
X
Xglob1(base, lim)
Xchar *base, *lim;
X{
X	register char *i, *j;
X	int v2;
X	char **k;
X	char *lptr, *hptr;
X	int c;
X	unsigned n;
X
X
X	v2 = globv;
X
Xtop:
X	if ((n=(int)(lim-base)) <= v2)
X		return;
X	n = v2 * (n / (2*v2));
X	hptr = lptr = base+n;
X	i = base;
X	j = lim-v2;
X	for(;;) {
X		if (i < lptr) {
X			if ((c = (*func)(i, lptr)) == 0) {
X				glob2(i, lptr -= v2);
X				continue;
X			}
X			if (c < 0) {
X				i += v2;
X				continue;
X			}
X		}
X
Xbegin:
X		if (j > hptr) {
X			if ((c = (*func)(hptr, j)) == 0) {
X				glob2(hptr += v2, j);
X				goto begin;
X			}
X			if (c > 0) {
X				if (i == lptr) {
X					glob3(i, hptr += v2, j);
X					i = lptr += v2;
X					goto begin;
X				}
X				glob2(i, j);
X				j -= v2;
X				i += v2;
X				continue;
X			}
X			j -= v2;
X			goto begin;
X		}
X
X
X		if (i == lptr) {
X			if (lptr-base >= lim-hptr) {
X				glob1(hptr+v2, lim);
X				lim = lptr;
X			} else {
X				glob1(base, lptr);
X				base = hptr+v2;
X			}
X			goto top;
X		}
X
X
X		glob3(j, lptr -= v2, i);
X		j = hptr -= v2;
X	}
X}
X
Xglob2(i, j)
Xchar *i, *j;
X{
X	register char *index1, *index2, c;
X	int m;
X
X	m = globv;
X	index1 = i;
X	index2 = j;
X	do {
X		c = *index1;
X		*index1++ = *index2;
X		*index2++ = c;
X	} while(--m);
X}
X
Xglob3(i, j, k)
Xchar *i, *j, *k;
X{
X	register char *index1, *index2, *index3;
X	int c;
X	int m;
X
X	m = globv;
X	index1 = i;
X	index2 = j;
X	index3 = k;
X	do {
X		c = *index1;
X		*index1++ = *index3;
X		*index3++ = *index2;
X		*index2++ = c;
X	} while(--m);
X}
+ END-OF-FILE sh4.c
chmod 'u=rw,g=r,o=r' \s\h\4\.\c
set `sum \s\h\4\.\c`
sum=$1
case $sum in
59682)	:;;
*)	echo 'Bad sum in '\s\h\4\.\c >&2
esac
echo Extracting \s\h\5\.\c
sed 's/^X//' > \s\h\5\.\c << '+ END-OF-FILE '\s\h\5\.\c
X#define Extern extern
X#include "signal.h"
X#include "errno.h"
X#include "setjmp.h"
X#include "sh.h"
X
X/* -------- io.c -------- */
X/* #include "sh.h" */
X
X/*
X * shell IO
X */
X
X
Xint
Xgetc(ec)
Xregister int ec;
X{
X	register int c;
X
X	if(e.linep > elinep) {
X		while((c=readc()) != '\n' && c)
X			;
X		err("input line too long");
X		gflg++;
X		return(c);
X	}
X	c = readc();
X	if ((ec != '"') && (ec != '\'')) {
X		if(c == '\\') {
X			c = readc();
X			if (c == '\n' && ec != '\"')
X				return(getc(ec));
X			c |= QUOTE;
X		}
X	}
X	return(c);
X}
X
Xvoid
Xunget(c)
X{
X	if (e.iop >= e.iobase)
X		e.iop->peekc = c;
X}
X
Xint
Xreadc()
X{
X	register c;
X	static int eofc;
X
X	for (; e.iop >= e.iobase; e.iop--)
X		if ((c = e.iop->peekc) != '\0') {
X			e.iop->peekc = 0;
X			return(c);
X		} else if ((c = (*e.iop->iofn)(&e.iop->arg, e.iop)) != '\0') {
X			if (c == -1) {
X				e.iop++;
X				continue;
X			}
X			if (e.iop == iostack)
X				ioecho(c);
X			return(c);
X		}
X	if (e.iop >= iostack ||
X	    multiline && eofc++ < 3)
X		return(0);
X	leave();
X	/* NOTREACHED */
X}
X
Xvoid
Xioecho(c)
Xchar c;
X{
X	if (flag['v'])
X		write(2, &c, sizeof c);
X}
X
Xvoid
Xpushio(arg, fn)
Xstruct ioarg arg;
Xint (*fn)();
X{
X	if (++e.iop >= &iostack[NPUSH]) {
X		e.iop--;
X		err("Shell input nested too deeply");
X		gflg++;
X		return;
X	}
X	e.iop->iofn = fn;
X	e.iop->arg = arg;
X	e.iop->peekc = 0;
X	e.iop->xchar = 0;
X	e.iop->nlcount = 0;
X	if (fn == filechar || fn == linechar || fn == nextchar)
X		e.iop->task = XIO;
X	else if (fn == gravechar || fn == qgravechar)
X		e.iop->task = XGRAVE;
X	else
X		e.iop->task = XOTHER;
X}
X
Xstruct io *
Xsetbase(ip)
Xstruct io *ip;
X{
X	register struct io *xp;
X
X	xp = e.iobase;
X	e.iobase = ip;
X	return(xp);
X}
X
X/*
X * Input generating functions
X */
X
X/*
X * Produce the characters of a string, then a newline, then EOF.
X */
Xint
Xnlchar(ap)
Xregister struct ioarg *ap;
X{
X	register int c;
X
X	if (ap->aword == NULL)
X		return(0);
X	if ((c = *ap->aword++) == 0) {
X		ap->aword = NULL;
X		return('\n');
X	}
X	return(c);
X}
X
X/*
X * Given a list of words, produce the characters
X * in them, with a space after each word.
X */
Xint
Xwdchar(ap)
Xregister struct ioarg *ap;
X{
X	register char c;
X	register char **wl;
X
X	if ((wl = ap->awordlist) == NULL)
X		return(0);
X	if (*wl != NULL) {
X		if ((c = *(*wl)++) != 0)
X			return(c & 0177);
X		ap->awordlist++;
X		return(' ');
X	}
X	ap->awordlist = NULL;
X	return('\n');
X}
X
X/*
X * Return the characters of a list of words,
X * producing a space between them.
X */
Xstatic	int	xxchar(), qqchar();
X
Xint
Xdolchar(ap)
Xregister struct ioarg *ap;
X{
X	register char *wp;
X
X	if ((wp = *ap->awordlist++) != NULL) {
X		PUSHIO(aword, wp, *ap->awordlist == NULL? qqchar: xxchar);
X		return(-1);
X	}
X	return(0);
X}
X
Xstatic int
Xxxchar(ap)
Xregister struct ioarg *ap;
X{
X	register int c;
X
X	if (ap->aword == NULL)
X		return(0);
X	if ((c = *ap->aword++) == '\0') {
X		ap->aword = NULL;
X		return(' ');
X	}
X	return(c);
X}
X
Xstatic int
Xqqchar(ap)
Xregister struct ioarg *ap;
X{
X	register int c;
X
X	if (ap->aword == NULL || (c = *ap->aword++) == '\0')
X		return(0);
X	return(c);
X}
X
X/*
X * Produce the characters from a single word (string).
X */
Xint
Xstrchar(ap)
Xregister struct ioarg *ap;
X{
X	register int c;
X
X	if (ap->aword == 0 || (c = *ap->aword++) == 0)
X		return(0);
X	return(c);
X}
X
X/*
X * Return the characters from a file.
X */
Xint
Xfilechar(ap)
Xregister struct ioarg *ap;
X{
X	register int i;
X	char c;
X	extern int errno;
X
X	do {
X		i = read(ap->afile, &c, sizeof(c));
X	} while (i < 0 && errno == EINTR);
X	return(i == sizeof(c)? c&0177: (closef(ap->afile), 0));
X}
X
X/*
X * Return the characters produced by a process (`...`).
X * Quote them if required, and remove any trailing newline characters.
X */
Xint
Xgravechar(ap, iop)
Xstruct ioarg *ap;
Xstruct io *iop;
X{
X	register int c;
X
X	if ((c = qgravechar(ap, iop)&~QUOTE) == '\n')
X		c = ' ';
X	return(c);
X}
X
Xint
Xqgravechar(ap, iop)
Xregister struct ioarg *ap;
Xstruct io *iop;
X{
X	register int c;
X
X	if (iop->xchar) {
X		if (iop->nlcount) {
X			iop->nlcount--;
X			return('\n'|QUOTE);
X		}
X		c = iop->xchar;
X		iop->xchar = 0;
X	} else if ((c = filechar(ap)) == '\n') {
X		iop->nlcount = 1;
X		while ((c = filechar(ap)) == '\n')
X			iop->nlcount++;
X		iop->xchar = c;
X		if (c == 0)
X			return(c);
X		iop->nlcount--;
X		c = '\n';
X	}
X	return(c!=0? c|QUOTE: 0);
X}
X
X/*
X * Return a single command (usually the first line) from a file.
X */
Xint
Xlinechar(ap)
Xregister struct ioarg *ap;
X{
X	register int c;
X
X	if ((c = filechar(ap)) == '\n') {
X		if (!multiline) {
X			closef(ap->afile);
X			ap->afile = -1;	/* illegal value */
X		}
X	}
X	return(c);
X}
X
X/*
X * Return the next character from the command source,
X * prompting when required.
X */
Xint
Xnextchar(ap)
Xregister struct ioarg *ap;
X{
X	register int c;
X
X	if ((c = filechar(ap)) != 0)
X		return(c);
X	if (talking && e.iop <= iostack+1)
X		prs(prompt->value);
X	return(0);
X}
X
Xvoid
Xprs(s)
Xregister char *s;
X{
X	if (*s)
X		write(2, s, strlen(s));
X}
X
Xvoid
Xputc(c)
Xchar c;
X{
X	write(2, &c, sizeof c);
X}
X
Xvoid
Xprn(u)
Xunsigned u;
X{
X	prs(itoa(u, 0));
X}
X
Xvoid
Xclosef(i)
Xregister i;
X{
X	if (i > 2)
X		close(i);
X}
X
Xvoid
Xcloseall()
X{
X	register u;
X
X	for (u=NUFILE; u<NOFILE;)
X		close(u++);
X}
X
X/*
X * remap fd into Shell's fd space
X */
Xint
Xremap(fd)
Xregister int fd;
X{
X	register int i;
X	int map[NOFILE];
X
X	if (fd < e.iofd) {
X		for (i=0; i<NOFILE; i++)
X			map[i] = 0;
X		do {
X			map[fd] = 1;
X			fd = dup(fd);
X		} while (fd >= 0 && fd < e.iofd);
X		for (i=0; i<NOFILE; i++)
X			if (map[i])
X				close(i);
X		if (fd < 0)
X			err("too many files open in shell");
X	}
X	return(fd);
X}
X
Xint
Xopenpipe(pv)
Xregister int *pv;
X{
X	register int i;
X
X	if ((i = pipe(pv)) < 0)
X		err("can't create pipe - try again");
X	return(i);
X}
X
Xvoid
Xclosepipe(pv)
Xregister int *pv;
X{
X	if (pv != NULL) {
X		close(*pv++);
X		close(*pv);
X	}
X}
X
X/* -------- here.c -------- */
X/* #include "sh.h" */
X
X/*
X * here documents
X */
X
Xstruct	here {
X	char	*h_tag;
X	int	h_dosub;
X	struct	ioword *h_iop;
X	struct	here	*h_next;
X} *herelist;
X
Xstruct	block {
X	char	*b_linebuf;
X	char	*b_next;
X	char	b_tmpfile[50];
X	int	b_fd;
X};
X
Xstatic	struct block *readhere();
X
X#define	NCPB	2048		/* here text block allocation unit */
X
Xmarkhere(s, iop)
Xregister char *s;
Xstruct ioword *iop;
X{
X	register struct here *h, *lh;
X
X	h = (struct here *) space(sizeof(struct here));
X	if (h == 0)
X		return;
X	h->h_tag = evalstr(s, DOSUB);
X	if (h->h_tag == 0)
X		return;
X	h->h_iop = iop;
X	h->h_iop->io_un.io_here = NULL;
X	h->h_next = NULL;
X	if (herelist == 0)
X		herelist = h;
X	else
X		for (lh = herelist; lh!=NULL; lh = lh->h_next)
X			if (lh->h_next == 0) {
X				lh->h_next = h;
X				break;
X			}
X	iop->io_flag |= IOHERE|IOXHERE;
X	for (s = h->h_tag; *s; s++)
X		if (*s & QUOTE) {
X			iop->io_flag &= ~ IOXHERE;
X			*s &= ~ QUOTE;
X		}
X	h->h_dosub = iop->io_flag & IOXHERE;
X}
X
Xgethere()
X{
X	register struct here *h;
X
X	for (h = herelist; h != NULL; h = h->h_next) {
X		h->h_iop->io_un.io_here = 
X			readhere(h->h_tag, h->h_dosub? 0: '\'',
X				h->h_iop->io_flag & IOXHERE);
X	}
X	herelist = NULL;
X}
X
Xstatic struct block *
Xreadhere(s, ec, nolit)
Xregister char *s;
X{
X	register struct block *bp;
X	register c;
X	jmp_buf ev;
X
X	bp = (struct block *) space(sizeof(*bp));
X	if (bp == 0)
X		return(0);
X	bp->b_linebuf = (char *)space(NCPB);
X	if (bp->b_linebuf == 0) {
X		/* jrp - should release bp here... */
X		return(0);
X	}
X	if (newenv(setjmp(errpt = ev)) == 0) {
X		if (e.iop == iostack && e.iop->iofn == filechar) {
X			pushio(e.iop->arg, filechar);
X			e.iobase = e.iop;
X		}
X
X		/* jrp changes */
X		bp->b_linebuf[0] = 0;
X		bp->b_next = bp->b_linebuf;
X		bp->b_tmpfile[0] = 0;
X		bp->b_fd = -1;
X		for (;;) {
X			while ((c = getc(ec)) != '\n' && c) {
X				if (ec == '\'')
X					c &= ~ QUOTE;
X				if (savec(c, bp, nolit) == 0) {
X					c = 0;
X					break;
X				}
X			}
X			savec(0, bp, nolit);
X			if (strcmp(s, bp->b_linebuf) == 0 || c == 0)
X				break;
X			savec('\n', bp, nolit);
X		}
X		*bp->b_linebuf = 0;
X		if (c == 0) {
X			prs("here document `"); prs(s); err("' unclosed");
X		}
X		quitenv();
X	}
X	return(bp);
X}
X
Xstatic
Xsavec(c, bp, nolit)
Xregister struct block *bp;
X{
X	/* jrp - gutted routine completely, modified to use temp file. */
X	
X	/* If the file is not open, see if a filename needs to be
X	 * created.  If so, create one.  Then create the file.
X	 */
X	char *	lp;
X	char *	cp;
X	static int inc;
X	int	len;
X
X	if(bp->b_fd < 0) {
X	    if(bp->b_tmpfile[0] == 0) {
X		/* Key this by the PID plus a tag... */
X		for (cp = bp->b_tmpfile, lp = "/tmp/shtm"; 
X		     (*cp = *lp++) != '\0'; cp++)
X			;
X
X		inc = (inc + 1) % 100;
X		lp = putn(getpid()*100 + inc);
X		for (; (*cp = *lp++) != '\0'; cp++)
X			;
X	    }
X
X	    /* Create the file, then open it for
X	     * read/write access.  After opening the
X	     * file, unlink it to it'll go away when
X	     * we're through using it.
X	     */
X	    bp->b_fd = creat(bp->b_tmpfile, 0600);
X	    close(bp->b_fd);
X	    bp->b_fd = open(bp->b_tmpfile, 2);
X	    unlink(bp->b_tmpfile);
X	    if(bp->b_fd < 0) {
X	        return(0);
X	    }
X	}
X
X	/* Stuff the character into the line buffer.  If it's a
X	 * newline, then insert it before the trailing null, write
X	 * out the line, and reset the line buffer.
X	 */
X	if(c == '\n') {
X	    bp->b_next[-1] = '\n';
X	    bp->b_next[0] = '\0';
X	    len = strlen(bp->b_linebuf);
X
X	    /* Write this out, unless the line ended
X	     * with a backslash...
X	     */
X	    if((len > 1) && (bp->b_next[-2] != '\\')) {
X		write_linebuf(bp, nolit);
X	    }
X
X	    return(1);
X	}
X	else {
X	    if(bp->b_next == &(bp->b_linebuf[NCPB - 1])) {
X		prs("here: line buffer full\n");
X		return(0);
X	    }
X	    *(bp->b_next++) = c;
X	    return(1);
X	}
X}
X
Xwrite_linebuf(bp, nolit)
Xstruct block * bp;
X{
X
X	char c;
X	jmp_buf ev;
X
X	if(nolit) {
X		if (newenv(setjmp(errpt = ev)) == 0) {
X			PUSHIO(aword, bp->b_linebuf, strchar);
X			setbase(e.iop);
X			e.iop->task |= XHERE;
X			while ((c = subgetc(0, 0)) != 0) {
X				c &= ~ QUOTE;
X				write(bp->b_fd, &c, sizeof c);
X			}
X			quitenv();
X		
X		}
X	}
X	else {
X		write(bp->b_fd, bp->b_linebuf, strlen(bp->b_linebuf));
X	}
X
X	/* Zap the line buffer for next time... */
X	bp->b_next = bp->b_linebuf;
X	bp->b_linebuf[0] = 0;
X}
X
Xherein(bp, xdoll)
Xstruct block *bp;
X{
X	int	ret_fd;
X
X	if (bp == 0)
X		return(-1);
X
X	/* If we have a temp file, then rewind it to the beginning */
X	if(bp->b_fd < 0) {
X		return(-1);
X	}
X
X	lseek(bp->b_fd, 0L, 0);
X
X	/* Free up this block pointer, as we're
X	 * not going to need it anymore.
X	 */
X	xfree(bp->b_linebuf);
X	xfree(bp);
X
X	return(bp->b_fd);
X}
X
Xscraphere()
X{
X	struct here * h;
X	struct here * nexth;
X	struct block * bp;
X
X
X	/* Close and unlink any files associated with
X	 * heres in progress, and free up all the
X	 * associated structures. 
X	 */
X	h = herelist;
X	while(h != NULL) {
X		nexth = h->h_next;
X		bp = (struct block *)h->h_iop->io_un.io_here;
X		if(bp != NULL) {
X			if(bp->b_fd >= 0) { close(bp->b_fd); }
X			if(*bp->b_tmpfile) { unlink(bp->b_tmpfile); }
X			xfree(bp->b_linebuf);
X			xfree(bp);
X		}
X		xfree(h);
X		h = nexth;
X	}
X
X	herelist = NULL;
X}
X
Xchar *
Xmemcpy(ato, from, nb)
Xregister char *ato, *from;
Xregister int nb;
X{
X	register char *to;
X
X	to = ato;
X	while (--nb >= 0)
X		*to++ = *from++;
X	return(ato);
X}
+ END-OF-FILE sh5.c
chmod 'u=rw,g=r,o=r' \s\h\5\.\c
set `sum \s\h\5\.\c`
sum=$1
case $sum in
38678)	:;;
*)	echo 'Bad sum in '\s\h\5\.\c >&2
esac
echo Extracting \s\h\6\.\c
sed 's/^X//' > \s\h\6\.\c << '+ END-OF-FILE '\s\h\6\.\c
X#define Extern
X
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include "sh.h"
X
+ END-OF-FILE sh6.c
chmod 'u=rw,g=r,o=r' \s\h\6\.\c
set `sum \s\h\6\.\c`
sum=$1
case $sum in
30298)	:;;
*)	echo 'Bad sum in '\s\h\6\.\c >&2
esac
exit 0