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

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

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'sh3.c'
sed 's/^X//' > 'sh3.c' << '+ END-OF-FILE ''sh3.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) < 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	freehere(areanum);
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 if (cp != NULL)
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 (resetsig) {
X		signal(SIGINT, SIG_DFL);
X		signal(SIGQUIT, SIG_DFL);
X	}
X	if (t->type == TPAREN)
X		exit(execute(t->left, NOPIPE, NOPIPE, FEXEC));
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_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_name, 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' 'sh3.c'
set `wc -c 'sh3.c'`
count=$1
case $count in
16895)	:;;
*)	echo 'Bad character count in ''sh3.c' >&2
		echo 'Count should be 16895' >&2
esac
echo Extracting 'sh4.c'
sed 's/^X//' > 'sh4.c' << '+ END-OF-FILE ''sh4.c'
X#define Extern extern
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.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	areanum = 1;
X	freehere(areanum);
X	freearea(areanum);	/* 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/*
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				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}
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 sh4.c
chmod 'u=rw,g=r,o=r' 'sh4.c'
set `wc -c 'sh4.c'`
count=$1
case $count in
12321)	:;;
*)	echo 'Bad character count in ''sh4.c' >&2
		echo 'Count should be 12321' >&2
esac
echo Extracting 'sh5.c'
sed 's/^X//' > 'sh5.c' << '+ END-OF-FILE ''sh5.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 != '`' && e.iop->task != XGRAVE) {
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 from a here temp file.
X */
Xint
Xherechar(ap)
Xregister struct ioarg *ap;
X{
X	char c;
X
X
X	if (read(ap->afile, &c, sizeof(c)) != sizeof(c)) {
X		close(ap->afile);
X		c = 0;
X	}
X	return (c);
X
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};
X
Xstatic	struct here *inhere;		/* list of hear docs while parsing */
Xstatic	struct here *acthere;		/* list of active here documents */
X
Xstatic	char *readhere();
X
X#define	NCPB	100	/* 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_next = NULL;
X	if (inhere == 0)
X		inhere = h;
X	else
X		for (lh = inhere; 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 = inhere; h != NULL; h = inhere) {
X		h->h_iop->io_name = readhere(h->h_tag, h->h_dosub? 0: '\'');
X		/* relink from inhere to acthere list */
X		inhere = h->h_next;
X		h->h_next = acthere;
X		acthere = h;
X	}
X	inhere = h;
X}
X
Xstatic char *
Xreadhere(s, ec)
Xregister char *s;
X{
X	int tf;
X	char tname[30];
X	register c;
X	jmp_buf ev;
X	char line [LINELIM+1];
X	char *next;
X
X	tempname(tname);
X	tf = creat(tname, 0600);
X	if (tf < 0)
X		return (0);
X	if (newenv(setjmp(errpt = ev)) != 0)
X		return (0);
X	if (e.iop == iostack && e.iop->iofn == filechar) {
X		pushio(e.iop->arg, filechar);
X		e.iobase = e.iop;
X	}
X	for (;;) {
X		if (talking && e.iop <= iostack)
X			prs(cprompt->value);
X		next = line;
X		while ((c = getc(ec)) != '\n' && c) {
X			if (ec == '\'')
X				c &= ~ QUOTE;
X			if (next >= &line[LINELIM]) {
X				c = 0;
X				break;
X			}
X			*next++ = c;
X		}
X		*next = 0;
X		if (strcmp(s, line) == 0 || c == 0)
X			break;
X		*next++ = '\n';
X		write (tf, line, (int)(next-line));
X	}
X	if (c == 0) {
X		prs("here document `"); prs(s); err("' unclosed");
X	}
X	close(tf);
X	quitenv();
X	/* correct area? */
X	return (strsave(tname, areanum));
X}
X
X/*
X * open here temp file.
X * if unquoted here, expand here temp file into second temp file.
X */
Xherein(hname, xdoll)
Xchar *hname;
X{
X	register hf, tf;
X
X	if (hname == 0)
X		return(-1);
X	hf = open(hname, 0);
X	if (hf < 0)
X		return (-1);
X	if (xdoll) {
X		char c;
X		char tname[30];
X		jmp_buf ev;
X
X		tempname(tname);
X		if ((tf = creat(tname, 0600)) < 0)
X			return (-1);
X		if (newenv(setjmp(errpt = ev)) == 0) {
X			PUSHIO(afile, hf, herechar);
X			setbase(e.iop);
X			while ((c = subgetc(0, 0)) != 0) {
X				c &= ~ QUOTE;
X				write(tf, &c, sizeof c);
X			}
X			quitenv();
X		} else
X			unlink(tname);
X		close(tf);
X		tf = open(tname, 0);
X		unlink(tname);
X		return (tf);
X	} else
X		return (hf);
X}
X
Xscraphere()
X{
X	inhere = NULL;
X}
X
X/* unlink here temp files before a freearea(area) */
Xfreehere(area)
Xint area;
X{
X	register struct here *h, *hl;
X
X	hl = NULL;
X	for (h = acthere; h != NULL; hl = h, h = h->h_next)
X		if (getarea(h) >= area) {
X			if (h->h_iop->io_name != NULL)
X				unlink(h->h_iop->io_name);
X			if (hl == NULL)
X				acthere = h->h_next;
X			else
X				hl->h_next = h->h_next;
X		}
X}
X
Xtempname(tname)
Xchar *tname;
X{
X	static int inc;
X	register char *cp, *lp;
X
X	for (cp = tname, lp = "/tmp/shtm"; (*cp = *lp++) != '\0'; cp++)
X		;
X	lp = putn(getpid()*1000 + inc++);
X	for (; (*cp = *lp++) != '\0'; cp++)
X		;
X}
+ END-OF-FILE sh5.c
chmod 'u=rw,g=r,o=r' 'sh5.c'
set `wc -c 'sh5.c'`
count=$1
case $count in
9258)	:;;
*)	echo 'Bad character count in ''sh5.c' >&2
		echo 'Count should be 9258' >&2
esac
echo Extracting 'sh6.c'
sed 's/^X//' > 'sh6.c' << '+ END-OF-FILE ''sh6.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' 'sh6.c'
set `wc -c 'sh6.c'`
count=$1
case $count in
92)	:;;
*)	echo 'Bad character count in ''sh6.c' >&2
		echo 'Count should be 92' >&2
esac
exit 0