AUSAM/source/S/sh.c

Compare this file to the similar file:
Show the results in this format:

/*
 * PWB/UNIX shell
 * copyright 1973 Bell Telephone Laboratries inc.
 * 2.44 of 5/26/77
 *
 * Modified at UNSW by Chris Maltby Oct '78 to
 *	Improve glob
 *	Code for AUSAM
 *	Make accounting optional
 *	Implement $$1 etc.
 *	Allow cd %root
 *	Include code for 'kill'
 *	Timeout terminals
 *	Print out statistics at log-off
 *	Echoall & echonone supplied
 *	Root default path is "/etc/bin" "/etc" "/bin" ""
 *	Execute pascal (px) and spitbol files.
 *
 * Further modified at UNSW by Greg Rose Jan '79.
 *	remembers last person looked up with '%' or '@' (tend to be repetitive)
 *	'$^' is last command line
 *	'$^n' is nth item of previous command line
 *	'$$^n' is nth n+1th ... items of previous command line.
 *
 * Non-AUSAM code corrected by Chris Doney CSU Mar '79.
 *	knows about old style password file.
 *	'wait' command changed to allow to wait
 *		for specific proc.
 *	'wait' is also rub-outable.
 *
 * Above fix fixed so that AUSAM part still works Chris Maltby Apr '79.
 *
 */

#include	<local-system>
#include	<errnos.h>
#include	<stat.h>
#ifdef	AUSAM
#include	<passwd.h>
#endif	AUSAM
#define NOFILE	15	/* max open files per process */

#define	INTR	2
#define	QUIT	3
#define	SIGPIPE	13
#define LINSIZ 512
#define ARGSIZ 100
#define TRESIZ 100

#define QUOTE 0200

#define FAND 0001	/* This tree is '&'ed */
#define FCAT 0002	/* The '>' is really a '>>' */
#define FPIN 0004	/* A pipe is input */
#define FPOU 0010	/* A pipe is output */
#define FPAR 0020	/* The end of a tree in '()' */
#define FINT 0040	/* The tree is interrupt protected */
#define FPRS 0100	/* Print proc ids of '&'s */

#define TCOM 1		/* A command */
#define TPAR 2		/* A parenthesized command tree */
#define TFIL 3		/* DLEF is piped to DRIT */
#define TLST 4		/* A command list */
#define TOR  5		/* Execute DRIT if not DLEF */
#define TAND 6		/* Execute DRIT if DLEF */

#define DTYP 0
#define DFLG 1
#define DLEF 2
#define DRIT 3
#define DSPR 4
#define DCOM 5

#define N 'n'-'a'
#define P 'p'-'a'
#define R 'r'-'a'
#define S 's'-'a'
#define T 't'-'a'
#define W 'w'-'a'
#define Z 'z'-'a'
#define DOLREPL 1
#define DOLREPQ	2

extern errno;
/*	BSIZ = buffer for tty, BSIZFIL = size for file, 1 = size for pipe */
#define BSIZ 64
#define BSIZFIL	512
struct
{
	long start;	/* absolute file addr of char in buf[0] */
	long linloc;	/* loc of begin of line for while processing */
	int gotten;	/* number of bytes read in last actual read */
	int nleft;
	char *nextp;
} b;
#ifndef	AUSAM

#define	uid_pos	2
#define	dir_pos	5
struct
	{
	char	u_logname[8],
		u_logdir[22],
		u_logtty[2];
	}
	user;
	char	*skip_colon();
#define	logname()	user.u_logname
#define	logdir()	user.u_logdir
#define	logtty()	user.u_logtty
char	pwbuff[128];
#endif	AUSAM

char	bstate;	/* primary state var for control of buffering */
		/* 0 ==> BNOW right; real I/O ptr set at BEND (internal I/O) */
		/* 1 ==> real ptr has been set from BNOW (pre-exec) */
		/* 2 ==> post-exec - internals must be reset */
		/* pre-exec: 0->1; post-exec: 1->2; int cmd: 2->0 */
		/* pump begin: 1->0 */
char	b1char;	/* input buffer: 1 at a time */
char	*bbuf	&b1char;	/* addr of actual buffer */
#define	BNOW	(b.start + (b.gotten - b.nleft))
#define	BEND	(b.start + b.gotten)
int bnread BSIZ;	/* must change to 1 if found to be in pipe */

#define ACNAME	"/etc/sha"

char	*dolp;
int	idolp	-1;
int	oldfil0;	/* fildes for original file 0 */
char	pidp[6], argstr[6];
int	wide 5; /* glitch for 5char pid # */
char	devtty[10];
int	ldivr;
char	**dolv;
int	dolc;
char	**wordp;
int	prevc;
int	nwords;
char	*promp, *opromp, *optpromp;
char	*linep;
char	*elinep;
char	**argp;
char	**eargp;
int	*treep;
int	*treeend;
char	peekc;
char	secondpeek;
char	gflg;
char	error;
#ifdef	ACCOUNTING
char	acctf;	/*	<= 0 ==> no acctg at all, >0 ==> acctg */
char	acctfi;	/*	= 0 ==> no acctg for internal cmds */
#endif	ACCOUNTING
char	setintr;
int	uid;
char	*arginp;
char	onelflg;
char	rflg;
char	verbflg;	/* is this a sh -c or -t? */
int	exitcode;
char	exitstr[6] ;
char	*seta[26];
char	*endcore, *endptr;
int	oldintr;	/* save INTR state existing at start */
int	wasintr;
char	gointr[32];
int	catchintr();
int	timeout();
char	proflag;	/* 1 ==> inside .profile */
			/* 2 ==> interrupt in .profile */
char	redirf;	/* 1 ==> I/O redirection of input; controls pump rebuffering */
char	optfv;		/* 0 ==> +v, 1 ==> -v; -v ==> print commands */
char	ttyflag;	/* 1 ==> a login shell */


struct statb sb;

/*	commands performed internally */
char	*comint[]
{
	"chdir",	/* 0 */
	"shift",	/* 1 */
	"cd",		/* 2 */
	"wait",		/* 3 */
	":",		/* 4 */
	"onintr",	/* 5 */
	"=",		/* 6 */
	"next",		/* 7 */
	"if",		/* 8 */
	"else",		/* 9 */
	"endif",	/* 10 */
	"goto",		/* 11 */
	"switch",	/* 12 */
	"break",	/* 13 */
	"endsw",	/* 14 */
	"exit",		/* 15 */
	"while",	/* 16 */
	"end",		/* 17 */
	"continue",	/* 18 */
	"breaksw",	/* 19 */
	"opt",		/* 20 */
	"times",	/* 21 */
	"echoall",	/* 22 */
	"echonone",	/* 23 */
#ifdef	AUSAM
	"kill",		/* 24 */
#else
	"login",	/* 24 */
	"test",		/* 25 */
#endif	AUSAM
	0
};

/* defines for internal commands */
#define	ZCHDIR		0
#define	ZSHIFT		1
#define ZCD		2
#define	ZWAIT		3
#define	ZLABEL		4
#define	ZONINTR		5
#define	ZEQUALS		6
#define	ZNEXT		7
#define	ZIF		8
#define	ZELSE		9
#define	ZENDIF		10
#define	ZGOTO		11
#define	ZSWITCH		12
#define	ZBREAK		13
#define	ZENDSW		14
#define	ZEXIT		15
#define	ZWHILE		16
#define	ZEND		17
#define	ZCONTINUE	18
#define	ZBREAKSW	19
#define	ZOPT		20
#define	ZTIMES		21
#define	ZECHOALL	22
#define	ZECHONONE	23
#ifdef	AUSAM
#define ZKILL		24
#else
#define ZLOGIN		24
#define	ZTEST		25
#endif	AUSAM
#define	ONINTR	comint[ZONINTR]
#define	IF	comint[ZIF]
#define	ENDIF	comint[ZENDIF]
#define	ENDSW	comint[ZENDSW]
#define	END	comint[ZEND]

/*	other keywords */
char	THEN[]	"then";
char	*ARG0, *ARG1;	/* for diagnostics & internal commands */
int	COMTYPE;	/* code for command, -1, or one of Z* */

char	*mesg[]
{
	0,
	"Hangup",
	0,
	"Quit",
	"Illegal instruction",
	"Trace/BPT trap",
	"IOT trap",
	"EMT trap",
	"Floating exception",
	"Killed",
	"Bus error",
	"Segmentation violation",
	"Bad system call",
	"Broken pipe",
	"System termination",
	"Real time limit",
	"CPU time limit",
	"Memory parity",
	"Signal 18",
	"Signal 19",
	0,
};

/*	messages */
char	*SYNTAX	"syntax error: ";
char	*MISS	"missing ";
char	*MISSL	"missing label: ";
char	*NONUM	"non-numeric arg - ";

char	*ARGCNT	"arg count";
char	*ARGLNG "arg list too long";
char	*BADARG	"bad arg: ";
char	*CANTEX	"cannot execute";
char	*CANTOP	"cannot open: ";
char	*NOTPER	"not permitted";
char	*NOTWHI	"used outside loop";
char	*EQERR	"`=' error";
char	*BADDIR	"bad directory";

struct stime
{
	long procu, procs, childu, childs, curtim;
} timeb;
long starttime;
long time();
#define	HZ	50	/* This is AUSTRALIA mate */

/* pascal interpreter 'px' */
char	PX[]		"/bin/px";

#ifdef	ACCOUNTING
struct
{
	char cname[8];
	char lname[6];
	char shtty;
	int tuid;
	long datet;
	long realt;
	long bcput;
	long bsyst;
} tbuf;
#endif	ACCOUNTING

long tell();
char *nxtarg(), *sname();

/*	following items implement while -- end stack of WDEEP levels */
#define	WDEEP	3
#define INWHILE	(wtop < WDEEP)
struct
{
	long sloc;	/* starting loc = addr of while */
	long eloc;	/* ending loc = addr of line AFTER end */
} wstk[WDEEP];
int	wtop	WDEEP;	/* top of stack */

main(c, av)
int c;
char **av;
{
	register f;
	register char *p, **v;
#ifdef	AUSAM
	struct pwent pe[1];
	char pwbuf[SSIZ];
	char tname[2];
#endif

	starttime = time();
	copy(itoa(getpid()), pidp);
	copy("/dev/tty",devtty);
	setxcod(0);

	if (c >= 2 && (eq(av[1], "-v") || eq(av[1], "-x")))
	{
		optfv = 1;
		av[1] = av[0];
		av++;
		--c;
	}
	if (( f=dup(1)) != 2)
		close(f);	/* force open fd 2 if not open already */
	f = c-2;
	f = f<0 ? 0 : f;
#ifdef	AUSAM
	uid = getefvt();
	pe->pw_uid = uid;
	if(getpwlog(pe, pwbuf, sizeof pwbuf) < 0)
	{
		static char *unknown = "unknown";

		pe->pw_strings[LNAME] = unknown;
		pe->pw_strings[DIRPATH] = unknown;
	}
	pwclose();
	tname[0] = ttyn(2); tname[1] = 0;
#define	logname()	pe->pw_strings[LNAME]
#define	logdir()	pe->pw_strings[DIRPATH]
#define	logtty()	tname

#else
	accfix();		/* fix to loginfo */
	uid = getuid();
#endif	AUSAM
	copy(itoa(f), argstr); seta[N] = argstr;
	seta[T] = logtty();
#ifdef	ACCOUNTING
	copyn(logname(), tbuf.lname, sizeof tbuf.lname);
	tbuf.shtty = devtty[8] = *seta[T];
#else
	devtty[8] = *seta[T];
#endif	ACCOUNTING
	v = av;
	p = sname(*v);
	if (eq(p,"-rsh") || eq(p,"rsh"))
				rflg++;
	if (*p == '-')
		proflag++,ttyflag++;
	for (f = 3; f < NOFILE; f++)
		close(f);
	oldfil0 = dup(0);	/* save for pipe into, .profile */
	promp = "% ";
	if(c > 1)
	{
		promp = 0;
#ifdef	ACCOUNTING
		tbuf.shtty = 'x';
#endif	ACCOUNTING
		signal(INTR, oldintr = signal(INTR, 1));
		if (*v[1]=='-')
		{
			*p = '-';
			if (v[1][1]=='c' && c>2)
			{
				arginp = v[2];
				verbflg++;
			}
			else if (v[1][1]=='t')
			{
				onelflg = 2;
				verbflg++;
			}
		}
		else
		{
			close(0);
			f = open(v[1], 0);
			if(f < 0)
			{
				prs(CANTOP);
				err(v[1]);
				exit(1);
			}
			bnread = BSIZFIL;
		}
	}

	if (onelflg || arginp || (seek(0, 0, 1) == -1 && errno == ESPIPE))
		bnread = 1;	/* no lookahead, no buffering */
	else
		bbuf = sbrk(bnread);
	endptr = endcore = sbrk(0);

	seta[S] = logdir();
	setwhere();
	if (promp)
	{
		signal(15, timeout);
		if(uid == 0)
			promp = "# ";
	}
	pexinit();
#ifdef	ACCOUNTING
	initacct();
#endif	ACCOUNTING
	if (proflag)
	{
		if (uid != 0 && (f = open(".profile.",0)) >= 0)
		{
			close(0);
			dup(f);
			close(f);
			promp = 0;
			signal(INTR, catchintr);
		}
		else
			proflag = 0;
	}
	if(*p == '-' && !proflag)
	{
		setintr++;
		signal(QUIT, 1);
		signal(INTR, 1);
	}
	dolv = v+1;
	dolc = c-1;

loop:
	if (promp)
		prints(1, optpromp ? optpromp : promp);
	main1();
	if(wasintr)
	{
		wasintr = 0;
		if(eq(gointr, "-")) goto loop;
		if (proflag > 1)
		{
			b.nleft = 0;
			goto loop;	/* will cause faked `next' */
		}
		wtop = WDEEP;		/* clear out while stack */
		ARG0 = ONINTR;
		ARG1 = gointr;
		search(COMTYPE = ZGOTO, 0);
		setxcod(1);
		signal(INTR, 0);
	}
	goto loop;
}


char line[LINSIZ];
char	lastline[LINSIZ];
char	*lastargs[ARGSIZ];

main1()
{
	char *args[ARGSIZ];
	int trebuf[TRESIZ];
	register char c, *cp;
	register *t;

	argp = args;
	eargp = args+ARGSIZ-5;
	linep = line;
	elinep = line+LINSIZ-5;
	error = 0;
	gflg = 0;
	b.linloc = BNOW;	/* find where we are, in case while */
	COMTYPE = -1;	/* avoid bad diagnostic from readc */
	do
	{
		cp = linep;
		word();
		if (optfv)
		{
			prints(1, " ");
			prints(1, cp);
		}
	} while(*cp != '\n');

	setlast(args);
	treep = trebuf;
	treeend = &trebuf[TRESIZ];
	if(gflg == 0)
	{
		if(error == 0)
		{
			setexit();
			if (error)
				return;
			t = syntax(args, argp);
		}
		if(error != 0) 
		{
			err("syntax error"); 
		}
		else
		{
			redirf = 0;	/* no redirect of fildes 0 yet */
			execute(t);
			bsynch(0);	/* adjust internal to real */
		}
	}
}
setlast(args)
char	**args;
{
	register char	**pp;
	register char	*cp, *cp1;
	char		**pp1;

	if(argp == args+1)
		return;
	pp1 = lastargs;		cp1 = lastline;
	for( pp = args; pp < argp; pp++)
	{
		*pp1++ = cp1;
		for(cp = *pp; *cp1++ = *cp++; );
	}
	prevc = argp - args - 1;	/* because of the newline */
}



word()
{
	register char c, c1;
	register dolflag;

	*argp++ = linep;

loop:
	switch(c = getc(DOLREPL))
	{

	case ' ':
	case '\t':
		goto loop;

	case '\'':	/* '...' : what you see is what you get */
	case '"':	/* "..." : \", \$, $ substitution */
		c1 = c;
		dolflag = (c == '"' && !dolp) ? DOLREPQ : !DOLREPL;
		while((c=getc(dolflag)) != c1)
		{
			if(c == '\n')
			{
				error++;
				peekc = c;
				return;
			}
			if (c1 == '"' && c == '\\' &&
				((peekc = getc(!DOLREPL)) == '$' ||
				peekc == '"'))
				{
					c = peekc;
					peekc = 0;
			}
			*linep++ = c|QUOTE;
		}
		goto pack;

	case '&':
	case '|':
		*linep++ = c;
		if((peekc=getc(DOLREPL)) == c)
			peekc = 0;
		else
			linep--;
	case ';':
	case '<':
	case '>':
	case '(':
	case ')':
	case '^':
	case '\n':
		*linep++ = c;
		*linep++ = '\0';
		return;
	case '\\':
		if ((c=getc(!DOLREPL))=='\n') goto loop;
		else
		{
			c =| QUOTE;
			break;
		}
	}

	peekc = c;

pack:
	for(;;)
	{
		if ((c = getc(DOLREPL))=='\\')
		{
			if ((c=getc(!DOLREPL))=='\n') c = ' ';
			else c =| QUOTE;
		}
		if(any(c, " '\"\t;&<>()|^\n"))
		{
			peekc = c;
			if(any(c, "\"'"))
				goto loop;
			*linep++ = '\0';
			return;
		}
		*linep++ = c;
	}
}


tree(n)
int n;
{
	register *t;

	t = treep;
	treep =+ n;
	if (treep>treeend)
	{
		prs("Command line overflow\n");
		error++;
		reset();
	}
	return(t);
}

char	subchar	'$';	/* variable marker, may be changed by pump */

/*	flag: !DOLREPL ==> no substitution, DOLREPL ==> substitute,
	DOLREPQ ==> quoted substitution: "$1" = value of $1 for sure */
getc(flag)
register flag;
{
	register char c;

	if(peekc)
	{
		c = peekc;
		peekc = 0;
		return(c);
	}
	if(argp > eargp)
	{
		argp =- 10;
		while((c=getc(!DOLREPL)) != '\n');
		argp =+ 10;
		err("Too many args");
		gflg++;
		return(c);
	}
	if(linep > elinep)
	{
		linep =- 10;
		while((c=getc(!DOLREPL)) != '\n');
		linep =+ 10;
		err("Too many characters");
		gflg++;
		return(c);
	}
getd:
	if(dolp)
	{
		if (c = *dolp++)
		{
			if (flag == DOLREPQ)
				c =| QUOTE;
			return c;
		}
		if (idolp != -1 && ++idolp < nwords)
		{
			dolp = wordp[idolp];
			return(' ');
		}
		dolp = 0;
		idolp = -1;
	}
	if(secondpeek)
	{
		c = secondpeek;
		secondpeek = 0;
		return c;
	}
	c = readc();
	if(c == subchar && flag)
	{
		c = readc();
		if(c>='0' && c<='9')
		{
			if(c-'0' < dolc)
				dolp = dolv[c-'0'];
			goto getd;
		}
		else if(c>='a' && c<='z')
		{
			dolp = seta[c-'a'];
			goto getd;
		}
		else if(c == '^')
			/* $^ is whole of previous line,
			 * $^n is nth "word" of previous line,
			 * $$^n is words from nth on of the previous line.
			 * Note the ambiguity of $^ or $^0, which requires
			 * a second peek character
			 */
		{
			c = readc();
			if(c >= '0' && c <= '9')
			{
				if((c =- '0') < prevc)
					dolp = lastargs[c];
				goto getd;
			}
			else
			{
				secondpeek = c;     /* remember for after */
				wordp = lastargs;
				idolp = 0;
				nwords = prevc;
				dolp = *lastargs;
				goto getd;
			}
		}
		else if(c == subchar)
		{
			c = readc();
			if(c >= '0' && c <= '9')	/* $$n = $n $n+1 .... */
			{
				if((c =- '0') < dolc)
				{
					idolp = c;
					dolp = dolv[c];
					nwords = dolc;
					wordp = dolv;
				}
				goto getd;
			}
			else if(c == '^')
			{
				c = readc();
				if(c < '0' || c > '9') /* same as $^ */
				{
					secondpeek = c;
					c = '0';
				}
				if((c =- '0') < prevc)
				{
					idolp = c;
					nwords = prevc;
					dolp = lastargs[c];
					wordp = lastargs;
				}
				goto getd;
			}
			else if(c == subchar)	/* $$$ is pid */
			{
				dolp = pidp;
				goto getd;
			}
			else if(c != '\n')
				c = readc();
		}
		/* $* = $1 $2 .... */
		else if (c == '*')
		{
			if (dolc > 1)
			{
				idolp = 1;
				nwords = dolc;
				wordp = dolv;
				dolp = dolv[1];
			}
			goto getd;
		}
		else if(c != '\n')
			c = readc();
	}
	return(c&0177);
}



/*
 * syntax
 *	empty
 *	syn1
 */

syntax(p1, p2)
register char **p1, **p2;
{

	while(p1 != p2)
	{
		if(any(**p1, ";&\n"))
			p1++;
		else
			return(syn1(p1, p2));
	}
	return(0);
}


/*
 * syn1
 *	syn1a
 *	syn1a & syntax
 *	syn1a ; syntax
 */

syn1(p1, p2)
char **p1, **p2;
{
	register char **p;
	register *t, *t1;
	int l;

	l = 0;
	for(p=p1; p!=p2; p++)
	switch(**p)
	{

	case '(':
		l++;
		continue;

	case ')':
		l--;
		if(l < 0)
			error++;
		continue;

	case '&':
		if((*p)[1] == '&')
			continue;	/* and, not asynch */
	case ';':
	case '\n':
		if(l == 0)
		{
			l = **p;
			t = tree(4);
			t[DTYP] = TLST;
			t[DLEF] = syn1a(p1, p);
			t[DFLG] = 0;
			if(l == '&')
			{
				t1 = t[DLEF];
				t1[DFLG] =| FAND|FPRS|FINT;
			}
			t[DRIT] = syntax(p+1, p2);
			return(t);
		}
	}
	if(l == 0)
		return(syn1a(p1, p2));
	error++;
}


/*
 * syn1a
 *	syn1b
 *	syn1b || syn1a
 */

syn1a(p1,p2)
char **p1, **p2;
{
	register char **p;
	register int l, *t;

	l = 0;
	for(p=p1; p!=p2; p++)
	switch(**p)
	{

	case '(':
		l++;
		continue;

	case ')':
		l--;
		continue;

	case '|':
		if((*p)[1] == '\0')
			continue;	/* a pipe not an or */
		if(l == 0)
		{
			t = tree(4);
			t[DTYP] = TOR;
			t[DLEF] = syn1b(p1, p);
			t[DRIT] = syn1a(p+1, p2);
			t[DFLG] = 0;
			return(t);
		}
	}
	return(syn1b(p1, p2));
}


/*
 * syn1b
 *	syn2
 *	syn2 && syn1b
 */

syn1b(p1,p2)
char **p1, **p2;
{
	register char **p;
	register int l, *t;

	l = 0;
	for(p=p1; p!=p2; p++)
	switch(**p)
	{

	case '(':
		l++;
		continue;

	case ')':
		l--;
		continue;

	case '&':
		if(l == 0)
		{
			t = tree(4);
			t[DTYP] = TAND;
			t[DLEF] = syn2(p1, p);
			t[DRIT] = syn1b(p+1, p2);
			t[DFLG] = 0;
			return(t);
		}
	}
	return(syn2(p1, p2));
}


/*
 * syn2
 *	syn3
 *	syn3 | syn2
 */

syn2(p1, p2)
char **p1, **p2;
{
	register char **p;
	register int l, *t;

	l = 0;
	for(p=p1; p!=p2; p++)
	switch(**p)
	{

	case '(':
		l++;
		continue;

	case ')':
		l--;
		continue;

	case '|':
	case '^':
		if(l == 0)
		{
			t = tree(4);
			t[DTYP] = TFIL;
			t[DLEF] = syn3(p1, p);
			t[DRIT] = syn2(p+1, p2);
			t[DFLG] = 0;
			return(t);
		}
	}
	return(syn3(p1, p2));
}


/*
 * syn3
 *	( syn1 ) [ < in  ] [ > out ]
 *	word word* [ < in ] [ > out ]
 */

syn3(p1, p2)
char **p1, **p2;
{
	register char **p;
	char **lp, **rp;
	register *t;
	int n, l, i, o, c, flg;

	flg = 0;
	if(**p2 == ')')
		flg =| FPAR;
	lp = 0;
	rp = 0;
	i = 0;
	o = 0;
	n = 0;
	l = 0;
	for(p=p1; p!=p2; p++)
	switch(c = **p)
	{

	case '(':
		if(l == 0)
		{
			if(lp != 0)
				error++;
			lp = p+1;
		}
		l++;
		continue;

	case ')':
		l--;
		if(l == 0)
			rp = p;
		continue;

	case '>':
		p++;
		if (p != p2 && **p == '>')
		{
			if (l == 0)
				flg =| FCAT;
		}
		else p--;

	case '<':
		if(l == 0)
		{
			p++;
			if(p == p2)
			{
				error++;
				p--;
				continue;
			}
			if(any(**p, "<>("))
			{
				error++;
				continue;
			}
			if(c == '<')
			{
				if(i != 0)
					error++;
				i = *p;
				continue;
			}
			if(o != 0)
				error++;
			o = *p;
		}
		continue;

	default:
		if(l == 0)
			p1[n++] = *p;
	}
	if(lp != 0)
	{
		if(n != 0)
			error++;
		t = tree(5);
		t[DTYP] = TPAR;
		t[DSPR] = syn1(lp, rp);
	}
	else
	{
		if(n == 0)
			error++;
		p1[n++] = 0;
		t = tree(n+5);
		t[DTYP] = TCOM;
		for(l=0; l<n; l++)
			t[l+DCOM] = p1[l];
	}
	t[DFLG] = flg;
	t[DLEF] = i;
	t[DRIT] = o;
	return(t);
}


scan(at, f)
int *at;
int (*f)();
{
	register char *p;
	register *t;

	t = at+DCOM;
	while(p = *t++)
	{
		if(*p == '%' || *p == '@')
			gflg = 1;
		(*f)(p);
	}
}


tglob(s)
char *s;
{
	register char *p, c;

	for (p=s; c = *p++;)
		if(any(c, "[?*"))
			gflg = 1;
}


trim(s)
char *s;
{
	register char *p;

	for (p=s; *p++ =& 0177; );
	return(s);
}
int	ap, ac;		/* arg pointer & count for if & related cmds */
char	**av;		/* av[0] = t[DCOM] */
int	*savdlef;	/* for cmd piped into, has &t for cmd on other end */

execute(t, pf1, pf2)
int *t, *pf1, *pf2;
{
	int i, f, pv[2], wt;
	register *t1;
	register char *cp1, *cp2;
	int unnext();

tryagain:	/* if expr command may come back here to do command */
	if(t != 0)
	switch(t[DTYP])
	{

	case TCOM:
		ARG0 = cp1 = t[DCOM];
		ARG1 = cp2 = t[DCOM+1];
		if((COMTYPE = lookup(cp1)) < 0)
			goto notinternal;
#ifdef	ACCOUNTING
		if (acctfi)
		{
			times(&timeb);
			timeb.curtim = time();
		}
#endif	ACCOUNTING
		setxcod(0);	/* assume will be good */
		av = &t[DCOM];
		ap = 1;
		for(ac = 1; av[ac]; ac++);
		if (cp2)
			trim(cp2);
		if (COMTYPE != ZEQUALS)	/* = takes care of self,can't do now */
			bsynch(0);	/* make sure internal ptr right */
		switch (COMTYPE)
		{
		case ZCHDIR:
		case ZCD:
			if (rflg)
			{
				die(NOTPER, 0);
				break;
			}
			if(cp2 == 0)
			{
				if( chdir(seta[S]) < 0)
					die("Can't find home directory", 0);
			}
			else
			{
				gflg = 0;
				scan(t, &tglob);
				if(gflg)
				{
					etcglob(t + DCOM, 1);
				}
				else
				{
					if(chdir(cp2) < 0)
						die(BADDIR, 0);
				}
			}
			break;

		case ZSHIFT:
			if(dolc < 1)
			{
				die(ARGCNT, 0);
				break;
			}
			if(cp2==0)
			{
				dolv[1] = dolv[0];
				dolv++;
				dolc--;
				break;
			}
			if((i = atoi(cp2)) < 0 || i > dolc)
			{
				die(BADARG, cp2);
				break;
			}
			for(; i < dolc; i++)
				dolv[i] = dolv[i+1];
			dolc--;
			break;

		case ZWAIT:
			if(cp2)
			{
				i = atoi(cp2);
				if(i == 0)
					return;
			}
			else
			{
				i = -1;
			}
			if(setintr)
				signal(INTR,unnext);
			pwait(i,0);
			if(setintr)
				signal(INTR,1);
			return;
			break;

		case ZLABEL:
		case ZENDIF:
		case ZENDSW:
			break;

		case ZONINTR:
			/* suppress onintr if interactive shell, or if
			 * noninteractive fired up immune to INTR.
			 */
			if (promp || oldintr)
				break;
			if(!cp2)
			{
				signal(INTR, 0);
				break;
			}
			copy(cp2, gointr);
			wasintr = 0;
			signal(INTR, catchintr);
			break;

		case ZEQUALS:
			if(t[DFLG]&FPIN) close(pf1[1]);
			i = *cp2 - 'a';
			if(t[DCOM+3] != 0 && eq(t[DCOM+2], ""))
			{
				t[DCOM+2] = t[DCOM+3];
				setxcod(1);
			}
				/* 3rd exists & null 2nd ==> use 3rd instead */
			if(i>25 || i<0 || (i==P && rflg))
				err(EQERR);
			else
			{
				f = t[DFLG]&FPIN;
				if ((seta[i] = rdval((f ? pf1[0] : 0),
					t[DLEF], t[DCOM+2]))==0)
					err(EQERR);
				if (f)	/* piped, must assure synch */
				{
					pwait(savdlef[DSPR], savdlef);
					bsynch(2);
				}
			}
			break;

		case ZNEXT:
			opromp = promp;
			if(!cp2)
			{
				f = dup(oldfil0);
				promp = uid? "% ": "# ";
				bnread = BSIZ;
				proflag = 0;
			}
			else
			{
				if (rflg)
				{
					die(NOTPER, 0);
					break;
				}
				promp = 0;
				f = open(cp2, 0);
			}
			if (f<0)
			{
				promp = opromp;
				die(CANTOP, cp2);
			}
			else
			{
				close(0);
				dup(f);
				close(f);
				signal(INTR, promp?1:0);
				signal(QUIT, promp?1:0);
				setintr = promp?1:0;
				signal(15, promp?timeout:0);
			}
			b.nleft = b.gotten = 0;
			b.start = 0;
			for (wtop = 0; INWHILE; wtop++)
			{
				wstk[wtop].sloc = 0;
				wstk[wtop].eloc = 0;
			}
			break;

		case ZEXIT:
			bflush();
			setxcod(cp2 ? atoi(cp2) : 0);
			break;

		case ZGOTO:
			/* following takes care of (unlikely) case in which
			 * goto exits from loop(s) whose end(s) are as
			 * yet unlocated. although not efficient, it is
			 * rigged to act in the intuitively proper way.
			 */
			i = wtop;
			while (INWHILE)
			{
				if (wstk[wtop].eloc == 0)
				{
					search(ZBREAK, 0);	/* find end */
					wstk[wtop].eloc = BNOW;
				}
				else bseek(wstk[wtop].eloc);
				wtop++;
			}
			wtop = i;

			search(ZGOTO, 0);

			b.linloc = BNOW;	/* effect of goto on while */
			while (INWHILE &&
				(b.linloc < wstk[wtop].sloc ||
				b.linloc > wstk[wtop].eloc))
				wtop++;	/* pop */
			break;

		case ZELSE:
			search(ZELSE, eq(cp2, IF) && eq(av[ac-1], THEN));
			break;

		case ZBREAK:
			if (INWHILE)
				toend();
			else die(NOTWHI, 0);
			break;

		case ZBREAKSW:
			ARG1 = 0;	/* ignore possible arg */

		case ZSWITCH:
			search(COMTYPE, 0);	/* ZBREAKSW or ZSWITCH */
			break;

#ifndef	AUSAM
		case ZTEST:
			i = exp();
			if(nxtarg() != 0)
				die(SYNTAX, av[ap-1]);
			else
				setxcod(!i);
			break;
#endif

		case ZIF:
			if (exp())
			{
				if (eq(nxtarg(), THEN))
					break;
				--ap;	/*	ap -> new cmd */
				/* shift args to eliminate if */
				for(i = DSPR; i >= 0; i--)
					av[--ap] = t[i];
				t = &av[ap];
#ifdef	ACCOUNTING
				enacct(cp1, 1);	/* acct for if part */
#endif	ACCOUNTING
				goto tryagain;
			}
			else if (eq(nxtarg(), THEN))
				search(ZIF, 0);
			break;

		case ZWHILE:
			if (ac > 2) /* expr like that of if */
			{
				i = exp();
				if (cp1 = nxtarg())
				{
					die(SYNTAX, cp1);
					break;
				}
			}
			else /* 0 args or 1 null arg = false, else true*/
				i = cp2 != 0 && *cp2 != '\0';
			if (wtop >= WDEEP || wstk[wtop].sloc != b.linloc)
			{
				/* not already in loop */
				if (--wtop >= 0)
				{
					if (wstk[wtop].sloc != b.linloc)
					{
						wstk[wtop].sloc = b.linloc;
						wstk[wtop].eloc = 0;
					}
				}
				else
				{
					die(">3 levels", 0);
					break;
				}
			}

			if (i)
				break;
			toend();	/* condition nomatch */
			break;
		case ZCONTINUE:
			if (INWHILE)
				bseek(wstk[wtop].sloc);
			else die(NOTWHI, 0);
			break;

		case ZEND:
			if (INWHILE) /* while active */
			{
				wstk[wtop].eloc = BNOW;
				bseek(wstk[wtop].sloc);
			}
			else die(NOTWHI, 0);
			break;

		case ZOPT:
			for(i = DCOM+1; cp2 = t[i]; i++)
			{
				if (eq(cp2, "-v") || eq(cp2, "-x"))
					optfv = 1;
				else if (eq(cp2, "+v") || eq(cp2, "+x"))
					optfv = 0;
				else if (eq(cp2, "-p"))
				{
					if (cp2 = t[++i])
					{
						optpromp = rdval(0, 0, cp2);
						if(any('#', optpromp) && uid)
							optpromp = 0;
					}
					else
						optpromp = 0;
				}
			}
			break;

		case ZTIMES:
			finish();
			break;

		case ZECHOALL:
			optfv = 1;
			break;

		case ZECHONONE:
			optfv = 0;
			break;

#ifdef	AUSAM
		case ZKILL:
			killer(t + DCOM + 1);
			break;
#else

		case ZLOGIN:
			if(promp != 0) {
				fclean();
				execv("/bin/login", t+DCOM);
			}
			die(CANTEX, 0);
			break;
#endif	AUSAM
		}
#ifdef	ACCOUNTING
		enacct(cp1, 1);		/* internal commands */
#endif	ACCOUNTING
		return;

	notinternal:
	case TPAR:
		bsynch(1);	/* set real ptr = BNOW */
		f = t[DFLG];
		i = 0;
		if((f&FPAR) == 0)
			if ((i = dofork()) == -1)
				return;	/* couldn't fork */
		if(i != 0) /* parent */
		{
			if((f&FPIN) != 0)
			{
				close(pf1[0]);
				close(pf1[1]);
				t[DLEF] = savdlef;	/* link back for pwait*/
			}
			else
				t[DLEF] = 0;	/* 1st or only in pipeline */
			t[DSPR] = i;	/* save proc-num for pwait */
			if ((f & FPRS) && (promp || verbflg))
			{
				prs(itoa(i));
				prs("\n");
			}
			if ((f & (FAND | FPOU)) == 0)
				pwait(i, t);
			bsynch(2);	/* mark possible read by cmd */
			return;
		}
		/*
		 * ignore interrupts for asynchronous cmds [code needed
		 * for sh proc only; ignore also for onintr -
		 */
		if (f & FPRS)
		{
			signal(INTR, 1);
			signal(QUIT, 1);
		}
		if (eq(gointr, "-"))
			signal(INTR, 1);
		if(t[DLEF] != 0)
		{
			close(0);
			/*	following for pipe into sh */
			trim(t[DLEF]);
			if (eq(t[DLEF], "--"))
				dup(oldfil0);
			else
			{
				i = open(t[DLEF], 0);
				if(i < 0)
					xdie(CANTOP, t[DLEF]);
			}
			redirf++;
		}
		if(t[DRIT] != 0)
		{
			if (rflg)
				xdie(">: ",NOTPER);
			if((f&FCAT) != 0)
			{
				i = open(trim(t[DRIT]), 1);
				if(i >= 0)
				{
					seek(i, 0, 2);
					goto f1;
				}
			}
			i = creat(trim(t[DRIT]), 0600);
			if(i < 0)
			{
#ifdef	AUSAM
				if(errno == EDISKLIM)
				{
					xdie("Disk limit reached, cannot create: ",
						t[DRIT]);
				}
				else
#endif	AUSAM
					xdie("cannot create: ",t[DRIT]);
			}
		f1:
			close(1);
			dup(i);
			close(i);
		}
		if((f&FPIN) != 0)
		{
			close(0);
			dup(pf1[0]);
			close(pf1[0]);
			close(pf1[1]);
			redirf++;
		}
		if((f&FPOU) != 0)
		{
			close(1);
			dup(pf2[1]);
			close(pf2[0]);
			close(pf2[1]);
		}
		if((f&FAND)!=0 && t[DLEF]==0 && (f&FPIN)==0)
		{
			close(0);
			open("/dev/null", 0);
		}
		if((f&FINT) == 0 && setintr)
		{
			signal(INTR, 0);
			signal(QUIT, 0);
		}
		if(t[DTYP] == TPAR)
		{
			if(t1 = t[DSPR])
				t1[DFLG] =| f&FINT;
			execute(t1);
			exit(exitcode);
		}
		fclean();

		if (rflg && any('/', trim(t[DCOM])))
			xdie(NOTPER, 0);	/* no / in rsh commands */
		gflg = 0;
		scan(t, &tglob);
		if (gflg)
		{
			/* old glob stuff
			t[DSPR] = seta[P];
			execv("/etc/glob", t+DSPR);
			xdie("glob: ",CANTEX);
			*/
			etcglob(t + DCOM, 0);
		}
		scan(t, &trim);

		if (eq(cp1, "pump"))
			dopump(&t[DCOM+1], redirf);
		texec(t[DCOM], t);

	case TFIL:
		f = t[DFLG];
		pipe(pv);
		t1 = t[DLEF];
		t1[DFLG] =| FPOU | (f&(FPIN|FINT|FPRS));
		execute(t1, pf1, pv);
		t1 = t[DRIT];
		t1[DFLG] =| FPIN | (f&(FPOU|FINT|FAND|FPRS));
		savdlef = t[DLEF];	/* save so can link pipe together */
		execute(t1, pv, pf2);
		return;

	case TLST:
		f = t[DFLG]&FINT;
		if(t1 = t[DLEF])
			t1[DFLG] =| f;
		execute(t1);
		if(t1 = t[DRIT])
			t1[DFLG] =| f;
		execute(t1);
		return;


	case TOR:
	case TAND:
		f = t[DFLG]&FINT;
		t1 = t[DLEF];
		t1[DFLG] =| f;
		execute(t1);
		i = atoi(seta[R]);
		if ((i == 0) == (t[DTYP] == TAND))
		{
			t1 = t[DRIT];
			t1[DFLG] =| f;
			execute(t1);
		}
		return;

	}
}

toend()
{
	if (wstk[wtop].eloc == 0) /* need to find end */
	{
		ARG1 = 0;
		search(ZBREAK, 0);
	}
	else bseek(wstk[wtop].eloc);
	wtop++;		/* pop 1 level */
	return;
}


lookup(p)
register char *p;
{
	register char *q;
	register i;
	for (i = 0; q = comint[i]; i++)
		if (eq(p, q))
			return(i);
	return -1;
}

dofork()
{
	register wt, i;
	for(wt = 10;; wt =+ 10)
	{
		if ((i = fork()) != -1)
			break;
#ifdef	AUSAM
		if(errno == EPROCLIM)
		{
			err("Process limit reached");
			break;
		}
#endif	AUSAM
		if (promp == 0 && wt < 60)
			sleep(wt);
		else
		{
			err("cannot fork;try again");
			break;
		}
	}
	return i;
}

fclean()
{
#ifdef	ACCOUNTING
	if (acctf)
		close(acctf);
#endif	ACCOUNTING
	if (oldfil0)
		close(oldfil0);
	return;
}

texec(f, t)
register *t;
{
	register char *cp;
	char tline[256];
	int txtbsy;	/* kludge cntr for ETXTBSY fix */
	int pcfd;	/* file desc for suspected 'pi' obj */

	txtbsy = 0;
	cp = seta[P];	/* normal case -- search */
	if (any('/', f))
	{
		if (rflg)
			xdie(NOTPER, 0);	/* no / by rsh */
		cp = "";	/* sh: exec only cmd name as given */
	}
	do
	{
		cp = pcat(cp, f, tline, sizeof tline);
	retry:
		execv(tline, t+DCOM);
		switch (errno)
		{
		case ENOEXEC:
			t[DCOM] = tline;
			/*
			 * check for pascal object file
			 * magic number is 0404
			 */
			if((pcfd = open(t[DCOM], 0)) >= 0)
			{
				register int pcread;
				int pcmagic;

				pcread = read(pcfd, &pcmagic, 2);
				close(pcfd);
				if(pcread == 2)
				{
					if(pcmagic == 0404)
					{
						t[DSPR] = PX + 9;
						execv(PX + 4, t + DSPR);
						execv(PX    , t + DSPR);
						xdie("No px!", 0);
					}
				}
			}
			/*
			 * Must be a command file
			 */
			t[DSPR] = seta[Z];
			execv(t[DSPR], t+DSPR);
			xdie(seta[Z], " No shell!");
		case EACCES:
			xdie("not executable", 0); /* missing x (probably) */
		case ENOMEM:
			xdie("too large", 0);
		case EPERM:
			xdie("permission denied", 0);
		case E2BIG:
			xdie(ARGLNG,0);
		case ETXTBSY:
			if ((txtbsy =+ 10) > 60)
				xdie("text busy", 0);
			sleep(txtbsy);
			goto retry;
		}
	} while(cp);
	xdie("not found", 0);
}

char	pipebomb;	/* 1 ==> SIGPIPE caught */
catchpipe()
{
	if (promp != 0)
		exit(1);
	pipebomb++;
	return;
}

/*	dopump: pump command:
 *	pump [+] [-[subchar]] [eofstr]
 *	default is to substitute using $
 *	-	suppresses substitution
 *	-subchar uses subchar instead of $ for substitution
 *	+	causes leading tabs in input to be thrown away
 *	eofstr defaults to !
 *	eofstr may not begin with `+', which is reserved for future flags.
 *	acts like filter, although actually copy of sh
 *	in input, \subchar = subchar, no other escaping performed
 *	if interactive, ignores interrupts & quits, but dies if other end
 *	of pipe dies; if non-interactive, reads to eofstr or real eof
 *	even if other end does die.
 *	NOTE: line is used as workarea, both for eofstr and input line
 *	eofstr is safe because initial arg can't be at beginning
 */

dopump(t)
register char **t;
{
#define	eofstr	line
#define	pumpwk	(line+96)
	register char c, *p;
	char tabeat;
	tabeat = 0;
	if (redirf)
	{
		b.nleft = b.gotten = 0;	/* clear buffer values */
		bnread = 1;	/* be careful with pipes & files */
	}
	else
	{
		if (promp == 0)
			bsynch(0);	/* use existing b., if any */
		else
		{
			signal(INTR, 1);	/* interactive - ignore */
			signal(QUIT, 1);	/* for sake of pump -|ed */
		}
	}
	if (proflag)
	{
		signal(INTR, 0);
		proflag = 0;
	}
	signal(SIGPIPE, catchpipe);
	setxcod(0);		/* in case real eof */
	copy("!", eofstr);
	for(; *t; t++)
		switch (**t)
		{
		case '-':
			subchar = *(*t + 1);	/* \0 turns subst off */
			break;
		case '+':
			tabeat++;
			break;
		default:
			copy(*t, eofstr);
		}

	while(1)	/* real eof will cause exit in readc */
	{
		p = pumpwk;
		if (tabeat)
		{
			while((c = getc(DOLREPL)) == '\t');
			peekc = c;
		}
		do
		{
			c = getc(DOLREPL);
			if (c == '\\' && (peekc = getc(!DOLREPL)) == subchar)
			{
				c = peekc;
				peekc = 0;
			}
			*p++ = c;
		} while (c != '\n');
		*--p = 0;		/* put 0 for \n */
		if (eq(pumpwk, eofstr))
			break;
		*p = '\n';
		if (!pipebomb)
			write(1, pumpwk, p - pumpwk + 1);
	}
	bsynch(1);	/* in case file */
	exit(pipebomb);
}

atoi(s)
char *s;
{

	register char *sp;
	register i, neg;

	sp = s;
	i = neg = 0;

	if(*sp == '-')
	{
		++neg;
		++sp;
	}

	while(*sp)
	{
		if(*sp < '0' || *sp > '9')
		{
			die(NONUM, s);
			return 0;
		}
		i = i * 10 + (*sp - '0');
		++sp;
	}
	return neg? -i: i;
}

xdie(str1, str2)
char *str1, *str2;
{
	die(str1, str2);
	exit(1);
}

die(str1, str2)
char *str1, *str2;
{
	prs(ARG0);
	prs(": ");
	prs(str1);
	err(str2);
	return;
}

/*
 *	err: emit error message, flush input by seeking to EOF (but only
 *	if reading from 0 in unrestricted way), exit.
 */
err(s)
char *s;
{

	prs(s);
	prs("\n");
	if (onelflg == 0 && arginp == 0)
		bflush();
	setxcod(1);
}


prs(s)
register char *s;
{

	if (s == 0)
		return;
	prints(2, s);
}


putc(c)
{

	write(2, &c, 1);
}


any(c, s)
register char c, *s;
{

	while(*s)
		if(*s++ == c)
			return(1);
	return(0);
}


eq(s1, s2)
register char *s1, *s2;
{

	if (s1 == 0 || s2 == 0)
		return 0;
	while(*s1++ == *s2)
		if(*s2++ == '\0')
			return(1);
	return(0);
}


#ifdef	ACCOUNTING
/*	initacct: initialize acctg according to state of ACNAME file:
	if cannot open ACNAME, no acctg done (acctf == 0)
	group permissions of ACNAME are used otherwise:
	0	cmd level, externals only	(acctf > 0, acctfi == 0)
	4	cmd level, add internal cmds	(acctf > 0, acctfi > 0)
	2	shell proc, externals only	(acctf > 0, acctfi == 0)
	1	shell proc, add internal cmds	(acctf > 0, acctfi > 0)
	NOTE: THIS IS KLUDGE INTENDED FOR OBLIVION AFTER EITHER NEW
	MH ACCTG GETS INSTALLED AND/OR INTERRUPT-HANDLING CHANGE
	PERMITS BETTER INTERNAL CMD HANDLING.
 */

initacct()
{
	register f;
	if ((acctf = open(ACNAME, 1)) < 0)
		acctf = 0;	/* no acctg at all */
	else
	{
		fstat(acctf, &sb);
		f = sb.i_mode;
		if (tbuf.shtty == 'x')	/* shell proc */
		{
			if (f & 020)
			{
				if (f & 010)
					acctfi++;	/* ext + int */
			}
			else
			{
				close(acctf);	/* no acct at all */
				acctf = 0;
			}
		}
		else if (f & 040)
			acctfi++;	/* cmd level, ext + int */
	}
	return;
}
#endif	ACCOUNTING

pwait(i, t)
int i, *t;
{
	register p, e, *t1;
	int nprocs;
	int s;

	nprocs = 0;
	t1 = t;
	while(t1)	/* count number of procs in current pipeline */
	{
		if (t1[DSPR])
			nprocs++;
		t1 = t1[DLEF];
	}

	if(i != 0)
	do
	{
#ifdef	ACCOUNTING
		if (acctf)
		{
			times(&timeb);
			timeb.curtim = time();
		}
#endif	ACCOUNTING
		p = waitx(&s);
		if(wasintr) p = waitx(&s);
		if(p == -1)
			break;
		e = s&0177;
		exitcode = (s>>8)&0377;
		if(e==INTR && wasintr)  /* process returned intr */
			e = 0;
		if(mesg[e] != 0)
		{
			if(p != i)
			{
				prs(itoa(p));
				prs(": ");
			}
			prs(mesg[e]);
			if(s&0200)
				prs(" -- Core dumped");
		}
		if (p == i)
			setxcod(exitcode);
		if(e != 0)
			err("");
		for(t1 = t; t1; t1 = t1[DLEF]) /* hunt proc; elims most **gok*/
		{
			if (t1[DSPR] == p)
			{
				nprocs--;
				break;
			}
		}
#ifdef	ACCOUNTING
		acct(t1);
#endif	ACCOUNTING
	} while (nprocs || (t == 0 && p != i));
}


#ifdef	ACCOUNTING
acct(t)
int *t;
{
	if(t == 0)
		enacct("**gok", 0);
	else if(*t == TPAR)
		enacct("()", 0);
	else
		enacct(t[DCOM], 0);
}


enacct(as, acctype)
char *as;
int acctype;	/* 0 ==> child process, 1 ==> internal cmd */
{
	struct stime timbuf;
	register i;
	register char *np;

	if (uid == 0)
		return;
	if (acctf <= 0)	/* if no accounting active, elim sys calls */
		return;
	if (acctype && acctfi <= 0)
		return;	/* internal cmd, but no internal acctg */
	times(&timbuf);
	timbuf.curtim = time();
	tbuf.realt = timbuf.curtim - timeb.curtim;
	if (acctype)
	{
		tbuf.bcput = timbuf.procu - timeb.procu;
		tbuf.bsyst = timbuf.procs - timeb.procs;
	}
	else
	{
		tbuf.bcput = timbuf.childu - timeb.childu;
		tbuf.bsyst = timbuf.childs - timeb.childs;
	}
	np = sname(as);
	for (i=0; i<8; i++)
	{
		tbuf.cname[i] = *np;
		if (*np)
			np++;
	}
	tbuf.datet = timbuf.curtim;
	tbuf.tuid = uid;
	seek(acctf, 0, 2);
	write(acctf, &tbuf, sizeof(tbuf));
}
#endif	ACCOUNTING


rdval(pipef, lef, na)
char *na;
{
	register char *st, *np;
	char c;

	st = endptr;
	np = na;
	if(!pipef && lef)
	{
		pipef = eq(lef, "--") ? dup(oldfil0) : open(lef, 0);
		if(pipef<0)  return 0;
	}
	for(;;)
	{
		if(endptr >= endcore-10)
			if((endcore=sbrk(64))<0) return 0;
		if(!na)
		{
			if(read(pipef, &c, 1) <= 0)
			{
				setxcod(1);	/* EOF indicator */
				break;
			}
		}
		else c = *np++ & 0177;
		*endptr++ = c;
		if(c=='\n' || c=='\0') break;
	}
	if(c=='\n') --endptr;
	*endptr++ = '\0';
	if(pipef || lef) close(pipef);
	return st;
}


catchintr()
{
	if (proflag)
		proflag++;	/* in .profile, make sure come out ok */
	wasintr++;
	signal(INTR, 1);
}


pcat(so1, so2, si, sz)
register char *so1, *so2;
char *si;
int sz;
{
	register char *s;

	s = si;
	while(*so1 != ':' && *so1 != '\0' && --sz) *s++ = *so1++;
	if(si != s && --sz > 0) *s++ = '/';
	while(*so2 && --sz > 0) *s++ = *so2++;
	if (--sz < 0)
	{
			*si = '\0';
			die("cmd line overflow", 0);
	}
	else *s = '\0';
	return *so1 ? ++so1 : 0;
}


setxcod(code)
int code;
{
	copy(itoa(code), exitstr);
	seta[R] = exitstr;
	return;
}

copy(source, sink)
register char *source, *sink;
{
	 while(*sink++ = *source++ & 0177);
}

/*
 *	copyn: copy at most n bytes from source to sink.
 */
copyn(source, sink, n)
register char *source, *sink;
int	n;
{
	register i;
	for (i = 0; i < n; i++)
		if (!(*sink++ = *source++))
			break;
}

itoa(n)
{
	register i, j;
	register char *cp;
	static char *str[12];

	j = n;
	for(cp = &str[4]; ;--cp)
	{
		if(wide) --wide;
		j = ldiv(0, j, 10);
		*cp = ldivr+'0';
		if((j | wide) == 0)
			return cp;
	}
}

char *nxtarg()
{
	register iap;

	if ((iap = ap++) > ac || av[iap] == 0)
		return(0);
	return trim(av[iap]);
}

exp()
{
	int p1;

	p1 = e1();
	if (eq(nxtarg(), "-o")) return(p1 | exp());
	ap--;
	return(p1);
}

e1()
{
	int p1;

	p1 = e2();
	if (eq(nxtarg(), "-a")) return (p1 & e1());
	ap--;
	return(p1);
}

e2()
{
	if (eq(nxtarg(), "!"))
		return(!e3());
	ap--;
	return(e3());
}

e3()
{
	int ccode;
	int nap;
	int int1, int2;
	register char *a, *p1, *p2;

	ccode = 0;
	a = nxtarg();
	if(eq(a, "("))
	{
		ccode = exp();
		if(!eq(nxtarg(), ")")) goto erre3;
		return(ccode);
	}

	if(eq(a, "{")) /* execute a command for exit code */
	{
		nap = ap;	/*	save 1st arg ptr */
		while (ap < ac && !eq(av[ap], "}")) trim(av[ap++]);
		if (ap >= ac || nap == ap)
		{
			die(SYNTAX, av[nap]);
			return 0;
		}
		av[ap++] = 0;	/*	change } to 0 for pexec */
		if ((int1 = dofork()) > 0)
			waitx(&ccode);	/* parent */
		else if (int1 == 0)	/* child */
			texec(av[nap], &av[nap-DCOM]);
		return(ccode? 0 : 1);
	}

	p1 = nxtarg();

	/* file predicates */
	if(eq(a, "-r"))
		return(tio(p1, 0));

	if(eq(a, "-w"))
		return(tio(p1, 1));
	if (eq(a, "-f"))
	{
		if (stat(p1, &sb) == -1)
			return 0;
		return ((sb.i_mode & IFMT) == 0);
	}
	if (eq(a, "-d"))
	{
		if (stat(p1, &sb) == -1)
			return 0;
		return ((sb.i_mode & IFMT) == IFDIR);
	}
	if (eq(a, "-s"))
	{
		if (stat(p1, &sb) == -1)
			return 0;
		return (sb.i_size0 || sb.i_size1);
	}

	/* string predicates */
	if (eq(a, "-n"))
		return(p1 && *p1 != '\0');
	if (eq(a, "-z"))
		return(p1 == 0 || *p1 == '\0');
	if ((p2 = nxtarg()) == 0)
		goto erre3;
	if(eq(p1, "="))
		return(eq(a, p2));

	if(eq(p1, "!="))
		return(!eq(a, p2));

	int1 = atoi(a);
	int2 = atoi(p2);
	if(eq(p1, "-eq"))
		return( int1 == int2 );
	if(eq(p1, "-ne"))
		return( int1 != int2 );
	if(eq(p1, "-gt"))
		return( int1 > int2 );
	if(eq(p1, "-lt"))
		return( int1 < int2 );
	if(eq(p1, "-ge"))
		return( int1 >= int2 );
	if(eq(p1, "-le"))
		return( int1 <= int2 );

erre3:
	die(SYNTAX, p1);
}

tio(a, f)
char *a; int f;
{
	register int fil;

	if ((fil = open(a, f)) >= 0)
	{
		close(fil);
		return(1);
	}
	return(0);
}


/*	search: forward search for required token, type =
 *	ZGOTO: : label
 *	ZSWITCH: : label, : default, or unmatched ENDSW
 *	ZBREAKSW: unmatched endsw
 *	ZIF: unmatched else or endif (or }) (this is failed if-then)
 *	ZELSE: unmatched endif (or })
 *	ZBREAK: unmatched end (break, failed while or end)
 *	levinit = 0, except when called from else if ... then, when it is 1
 */
search(type, levinit)
int type, levinit;
{
	register int level, t;
	register char *aword;
	char fifel, fbr, fswex;
	char wordbuf[128];
	if (type == ZGOTO)
		bseek(0L);
	fifel = type == ZIF || type == ZELSE;
	fbr = type == ZBREAK;
	fswex = type == ZSWITCH || type == ZBREAKSW;
	aword = wordbuf;
	level = levinit;

	do
	{
		*aword = '\0';
		getword(aword);
		if ((t = lookup(aword)) >= 0)
			switch(t)
			{
			case ZELSE:
				if (level == 0 && type == ZIF)
					return;	/* leave rest of else line */
				continue;
			case ZIF:
				while(getword(aword));
				if (fifel && eq(aword, THEN))
					level++;
				break;
			case ZENDIF:
				if (fifel)
					level--;
				break;
			case ZWHILE:
				if (fbr)
					level++;
				break;
			case ZEND:
				if (fbr)
					level--;
				break;
			case ZSWITCH:
				if (fswex)
					level++;
				break;
			case ZENDSW:
				if (fswex)
					level--;
				break;
			case ZLABEL:
				if (getword(aword))
				{
					if (type == ZGOTO)
					{
						if (eq(aword, ARG1))
							level = -1;
					}
					else if (type == ZSWITCH &&
						level == 0 &&
						(eq(aword, "default") ||
						match(ARG1, aword)))
						level = -1;
				}
				break;
			}
		getword(0);	/* gobble rest of line, if any */
	} while (level >= 0 && proflag < 2);
	return;
}

/*	getword: return next word from input (if any):
	aword != 0 : get next word (if any, return 1),
			if none, return 0, do not distturb *aword.
	aword == 0 : consume rest of line thru (nl).
	return 1 if word, 0 if only newline left.
 */

getword(aword)
char *aword;
{
	register int found;	/* 1 ==> found word, 0 ==> not */
	register char c, *wp;
	wp = aword;
	found = 0;
	c = peekc ? peekc : readc();
	peekc = '\0';

	do
	{
		while(c == ' ' || c == '\t')
			c = readc();
		if (c == '\n')
		{
			if (wp)
				break;
			else
				return 0;	/* newline only thing left */
		}
		found++;
		do
		{
			if (wp)
				*wp++ = c;
			c = readc();
			if (c == '\\' && (c = readc()) == '\n')
				c = ' ';
		} while (c != ' ' && c != '\t' && c != '\n');
	} while (!wp);
	peekc = c;
	if (found)
		*wp = '\0';
	return(found);
}

readc()
{
	register c;

	if (arginp)
	{
		if (arginp == 1)
			goto ONLYEXIT;
		if ((c = *arginp++) == 0)
		{
			arginp = 1;
			c = '\n';
		}
		return(c);
	}
	if (onelflg==1)
		goto ONLYEXIT;
	if (b.nleft == 0)
	{
		b.nextp = bbuf;
		b.start = b.start + b.gotten;	/* increm by last gotten */
#ifdef	AGSM|EECF
		if(promp && *seta[T] != '8')
#else
		if(promp)
#endif	AGSM|EECF
		{
			clktim(5*60);
		}
		if ((b.nleft = read(0, b.nextp, bnread)) <= 0 || proflag > 1)
		{
			if(promp)
				clktim(0);	/* cancel timeout */
			if (proflag)	/* still inside .profile */
			{
				b.nleft = 5;	/* fake next */
				b.nextp = "next\n";
			}
			else
			{
				eoferr();
				if( promp && ttyflag )
					finish();
			ONLYEXIT:
				exit(atoi(seta[R]));
			}
		}
		else if(promp)
			clktim(0);
		b.gotten = b.nleft;
	}
	b.nleft--;
	c = *b.nextp++;
	if (c=='\n' && onelflg)
		onelflg--;
	return(c);
}

timeout()
{
	if(promp)
		signal(15,timeout);
}

/*	eoferr: issue error message if cmd was in middle of search */
eoferr()
{
	switch (COMTYPE)
	{
	case ZGOTO:
		die(MISSL, ARG1);
		break;
	case ZIF:
	case ZELSE:
		die(MISS, ENDIF);
		break;
	case ZSWITCH:
	case ZBREAKSW:
		die(MISS, ENDSW);
		break;
	case ZWHILE:
	case ZBREAK:
		die(MISS, END);
		break;
	}
	bsynch(1);
	return;
}

/*	bflush: complete input flush */
bflush()
{
	seek(0, 0, 2);
	b.nleft = b.gotten = 0;
	return;
}

/*	bsynch: synchronize internal buffering & outside world */
/*	btarg gives nominal target value of bstate */
bsynch(btarg)
register btarg;
{
	register obstate;
	long f;
	if (btarg == bstate || promp || bnread == 1 || redirf)
		return;	/* no seeking in any of these cases */
	obstate = bstate;
	bstate = btarg;
	if (btarg == 2)
		return;	/* just remember this; do nothing now */
	switch (obstate)
	{
	case 0:		/* 0 --> 1 only possible */
		if (b.nleft > 0)
			lseek(0, BNOW, 0);	/* set real = internal */
		break;
	case 1:		/* 1 --> 0 (pump begin) */
		lseek(0, BEND, 0);	/* ptr where expected for read */
		break;
	case 2:		/* 2 --> 0; 2 --> 1, leave as 2, since 1 dominates */
		if (btarg == 0)
		{
			f = tell(0);
			if (f >= 0)
				bseek(f);
		}
		else
			bstate = 2;
		break;
	}
}

/*	bseek: seek, staying within current input buffer b, if possible */
bseek(where)
long where;
{
	long bend;
	if (promp || bnread == 1 || redirf)
		return;
	bend = BEND;
	if (where >= b.start && where < bend)
	{
		b.nleft = bend - where;	/* 1 <= b.nleft <= b.gotten */
		b.nextp = bbuf + b.gotten - b.nleft;
		lseek(0, bend, 0);	/* really at end ofbuffer */
	}
	else
	{
		b.nleft = b.gotten = 0;
		b.start = where;
		lseek(0, where, 0);
	}
	return;
}

/*	sname: simple name (last component) of file name */
char *sname(s)
register char *s;
{
	register char *p;
	for(p = s; *p;)
		if (*p++ == '/' && *p != '\0')
			s = p;
	return(s);
}

/*	setwhere: set up wherev for $w (1st component of pathname) */
char	wherev[10];
setwhere()
{
	register char *s, *w;
	register i;
	s = seta[S];
	seta[W] = w = wherev;
	i = 0;
	do
	{
		*w++ = *s++;
	} while (++i < 9 && *s != '/');
}

/*
 *	pexinit: fills in pathstr (seta[P]) and shellnam (seta[Z]).
 *	may be invoked before fork to avoid unnecessary .path opening.
 *	returns 0 if OK, -1 if any error.
 */
pexinit()
{
	char pathbuf[128 + 16];
	register n, f;
	char *newpath, *newshell;
	char *p;

	newshell = "/bin/sh";
	pcat(seta[S], ".path", pathbuf, sizeof pathbuf);
	if ((f = open(pathbuf, 0)) < 0)
		newpath = uid ? ":/bin:" : "/etc/bin:/etc:/bin:";
	else
	{
		n = read(f, pathbuf, sizeof pathbuf);
		close(f);
		if (n <= 0)
		{
			prs("cannot read .path\n");
			return(-1);
		}
		if (pexline(pathbuf, pathbuf + n, 128, &newpath, &p)
		|| pexline(p, pathbuf + n, 16, &newshell, &p))
			return(-1);
	}
	seta[P] = rdval(0, 0, newpath);
	seta[Z] = rdval(0, 0, newshell);
	return(0);
}

/*
 *	pexline: scan for a line (if any) beginning at ptr,
 *	ending at ptrlim -1, for line up to psize bytes long.
 *	convert it to string, return beginning addr in pret
 *	and addr of next line in pnext
 *	return 0 if OK (or not present), -1 if runs off end in middle of line
 *	or if line too long.
 */
pexline(ptr, ptrlim, psize, pret, pnext)
register char *ptr, *ptrlim;
int psize;
char **pret, **pnext;
{
	if (ptr >= ptrlim)
		return(0);
	*pret = ptr;
	if (ptrlim > ptr + psize)
		ptrlim = ptr + psize;
	for (; ptr < ptrlim; ptr++)
		if (*ptr == '\n')
		{
			*ptr++ = '\0';
			*pnext = ptr;
			return(0);
		}
	prs(".path too long\n");
	return(-1);
}
/*
 *	sh internal version of /etc/glob
 *	(from here thru function cat)
 *
 *	"@uname" | "%uname" expands to uname's login directory
 *	"*" in params matches r.e ".*"
 *	"?" in params matches r.e. "."
 *	"[...]" in params matches character class
 *	"[...a-z...]" in params matches a through z.
 *	if param does not contain "*", "[", or "?", use it as is
 *	if it does, find all files in current directory
 *	which match the param, sort them, and use them
 */

#define	STRSIZ	600
char	**avx;
char	*string;
char	*matstr;
char	*ablimit;
int	ncoll;
int 	fin;	/* used by getchar */
int	wflg 0;	/* set if any wild char */

etcglob(argv, cdflag)
char *argv[];
{
	char	ab[STRSIZ];		/* generated characters */
	char	*avxa[200+DCOM];		/* generated arguments */
	char	pscr[512];		/* intermediate results */

	avx = &avxa[DCOM];
	string = ab;
	matstr = pscr;
	ablimit = &ab[STRSIZ];
	*avx = *argv;
	ncoll = 0;
	while (*argv)
		expand(*argv++);
	if (ncoll == 0)
	{
		gerr("No match");
		if(cdflag)
			return;
		else
			exit(1);
	}
	*avx++ = 0;
	if(cdflag)
	{
		if(chdir(avxa[DCOM + 1]) < 0)
		{
			gerr(BADDIR);
		}
		return;
	}
	else
		texec(avxa[DCOM], avxa);
}

#ifdef	AUSAM
expand(as)
char *as;
{
	register char *s, *cs;
	register int c;
	int cf;
	char *proto, **oav, *ocs;

	cs = as;
	oav = avx;
	c = *cs++;
	cf = 0;
	if((c == '@') || (c == '%'))
	{
		static char uname[30];
		static char sbuf[SSIZ];
		static struct pwent pe[1];

		s = uname;
		c = sizeof uname;
		while(--c && (*s++ = *cs) && *cs++ != '/');
		*--s = 0;
		if(*cs == 0)
			cf++;
		/* if the name has been read recently, don't re-read it */
		if( !pe->pw_strings[LNAME] || compar(uname, pe->pw_strings[LNAME]) != 0 )
		{
			pe->pw_strings[LNAME] = uname;
			c = getpwuid(pe, sbuf, SSIZ);
			pwclose();
			if(c <= 0)
			{
				gerr("Who?");
				pe->pw_strings[LNAME] = 0;
				return;
			}
		}
		if(cf)
		{
			*avx++ = cat(pe->pw_strings[DIRPATH], "");
			ncoll++;
			return;
		}
		s = matstr;
		copy(pe->pw_strings[DIRPATH], s);
		while( *s++);
		proto = --s;
		ocs = cs;
		c = cs[-1];
		cf++;
	}
	else
	{
		proto = s = matstr;
		ocs = as;
		if(c == '/')
		{
			*s++ = c;
			ocs = cs;
			c = *cs++;
			proto = s;
		}
	}
	do
	{
		if(c == '/')
		{
			proto = s;
			ocs = cs;
		}
		else if(any(c,"*?["))
		{
			*proto = 0;
			dmatch(proto - matstr, ocs);
			sort(oav);
			return;
		}
		*s++ = c;
	}
	while(c = *cs++);
	*s = 0;
	*avx++ = cat(matstr, "");
	ncoll =+ cf;
}
#else
expand(as)
char *as;
{
	register char *s, *cs;
	register int c;
	int cf, backc, wild, mflg;
	char uname[16], *proto, **oav, *oas, *ocs, *oc, *os;

	cs = as;
	oav = avx;
	cf = 0;
	mflg = 0;
	wild = 1;
	c = *cs++;

	fin = open("/etc/passwd",0);
	if((c == '@') || (c == '%'))
	{
		if(fin == -1)
		{
			prints(2, "No passwd file\n");
			exit();
		}
		while((c = *cs) && c != '/')
		{
			cs++;
			if(c == '*' || c == '?' || c == '[')
			{
				wild = 0;
				wflg = 1;
			}
		}
		if(c)
			cf++;
		*cs++ = 0;
		s = uname;
		proto = as+1;
		seek(fin, 0, 0);
		/* buffered since fin>2 */ 
		while(c = getchar())
			if(c != ':')
				*s++ = c;
			else
			{
				backc = c;
				*s = 0;
				if(amatch(uname, proto))
				{
					mflg = 1;
					s = 0;
					do
						if((c = getchar()) == ':')
							s++;
					while(s < 4 && c);
					if(!c)
						break;	/* bad passwd file */ 
					s = matstr;
					while((c = getchar()) != ':' && c != '\n' && c)
						*s++ = c;
					backc = c;
					if(!c)
						break;
					*s = 0;
					if(cf)
					{
						os = s++;
						for(oc = cs; (c = *cs++) && c != '*' && c != '?' && c != '['; *s++ = c);
						if(!c)
						{
							*os = '/';
							cf = 0;
						}
						cs = oc;
						*s = 0;
						s = os;
					}
					if(cf)
						dmatch(s-matstr, cs);
					else
					{
						ncoll++;
						*avx++ = cat(matstr, "");
					}
					if(wild)
					{
						sort(oav);
						return;
					}
				}
				s = uname;
				if(backc != '\n')
				{
					while((c = getchar()) && c != '\n');
					if(!c)
						break;
				}
			}
		sort(oav);
		if(!mflg)
			wflg = 1;
		return;
	}
	ocs = as;
	proto = 0;
	s = matstr;
	if(c == '/')
	{
		*s++ = c;
		ocs = cs;
		c = *cs++;
		proto = s;
	}
	do
	{
		if(c == '/')
		{
			proto = s;
			ocs = cs;
		}
		else if(c == '*' || c == '?' || c == '[')
		{
			if(proto == 0)
			{
				s = matstr;
				*s++ = '.';
				proto = s;
			}
			*proto = 0;
			dmatch(proto-matstr, ocs);
			sort(oav);
			return;
		}
		*s++ = c;
	}
	while(c = *cs++);
	*avx++ = cat(as, "");
	close(fin);		/* close "/etc/passwd" .. */
}
#endif	AUSAM

dmatch(mof, ps)
int mof;
char *ps;
{
	register char *s;
	int chan, df, loc, ncos, nread;
	char *sp;
	struct statb f;
	struct dent
	{
		int d_ino;
		char d_name[14];
	} etab[32];

	df = 0;
	s = ps;
	if( mof == 0 || ( mof == 1 && matstr[0] == '/'))
		ncos = 0;
	else
		ncos = 1;
	if(stat(matstr, &f) == -1 || (f.i_mode & IFMT) != IFDIR)
		return;
	if((chan = open(matstr, 0)) == -1)
		return;
	while( *s && *s++ != '/');
	if(*s)
	{
		*--s = 0;
		sp = s;
	}
	else
		df++;
	loc = 0;
	while((nread = read(chan, etab, 512)) > 0)
	{
		register struct dent *entry;

		loc =+ nread;
		nread =>> 4;
		for(entry = &etab[0]; entry < &etab[nread]; entry++)
		{
			char ename[16];

			if(entry->d_ino == 0)
				continue;
			copyn(entry->d_name, ename, 14);
			ename[14] = 0;
			if(match(ename, ps))
			{
				register char *ep;

				s = &matstr[mof];
				ep = ename;
				if(ncos)
					*s++ = '/';
				while(*s++ = *ep++);
				if(chan == NOFILE - 1)
				{
					close(chan);
					chan = -1;
				}
				if(df)
				{
					ncoll++;
					*avx++ = cat(matstr, "");
				}
				else
					dmatch(s - matstr - 1, sp + 1);
				if(chan == -1)
				{
					chan = open(matstr, 0);
					seek(chan, loc, 0);
				}
			}
		}
	}
	close(chan);
	if(!df)
		*sp = '/';
}

sort(oavx)
char **oavx;
{
	register char **p1, **p2, **c;

	p1 = oavx;
	while (p1 < avx-1)
	{
		p2 = p1;
		while(++p2 < avx)
		{
			if (compar(*p1, *p2) > 0)
			{
				c = *p1;
				*p1 = *p2;
				*p2 = c;
			}
		}
		p1++;
	}
}


match(s, p)
char *s, *p;
{
	if (*s=='.' && *p!='.')
		return(0);
	return(amatch(s, p));
}

amatch(as, ap)
char *as, *ap;
{
	register char *s, *p;
	register scc;
	int c, cc, ok, lc;

	s = as;
	p = ap;
	if (scc = *s++)
		if ((scc =& 0177) == 0)
			scc = 0200;
	switch (c = *p++)
	{

	case '[':
		ok = 0;
		lc = 077777;
		while (cc = *p++)
		{
			if (cc==']')
			{
				if (ok)
					return(amatch(s, p));
				else
					return(0);
			}
			else if (cc=='-')
			{
				if (lc<=scc && scc<=(c = *p++))
					ok++;
			}
			else
				if (scc == (lc=cc))
					ok++;
		}
		return(0);

	default:
		if (c!=scc)
			return(0);

	case '?':
		if (scc)
			return(amatch(s, p));
		return(0);

	case '*':
		return(umatch(--s, p));

	case '\0':
		return(!scc);
	}
}

umatch(s, p)
char *s, *p;
{
	if(*p==0)
		return(1);
	while(*s)
		if (amatch(s++,p))
			return(1);
	return(0);
}

compar(as1, as2)
char *as1, *as2;
{
	register char *s1, *s2;

	s1 = as1;
	s2 = as2;
	while (*s1++ ==  *s2)
		if (*s2++ == 0)
			return(0);
	return (*--s1 - *s2);
}

cat(as1, as2)
char *as1, *as2;
{
	register char *s1, *s2;
	register int c;

	s2 = string;
	s1 = as1;
	while (c = *s1++)
	{
		if (s2 > ablimit)
			gdie(ARGLNG);
		c =& 0177;
		if (c==0)
		{
			*s2++ = '/';
			break;
		}
		*s2++ = c;
	}
	s1 = as2;
	do
	{
		if (s2 > ablimit)
			gdie(ARGLNG);
		*s2++ = c = *s1++;
	} while (c);
	s1 = string;
	string = s2;
	return(s1);
}

/*
 *	gdie: terminate with error message, but no seek to EOF
 *	purpose is to make internal glob work same as external one.
 *	something should be done about match/nomatch actions.
 */
gdie(str)
char *str;
{
	gerr(str);
	exit(1);
}

gerr(str)
char *str;
{
	prs(ARG0); prs(": ");
	prs(str); prs("\n");
}


int tpflag;

finish()
{
	struct stime now;

	times(&now);
	prs("\nconnect  time ");
	ptime((time() - starttime)*HZ);
	prs("\nuser cpu time ");
	ptime(now.procu + now.childu);
	prs("\nsys  cpu time ");
	ptime(now.procs + now.childs);
	prs("\n\n");
}

ptime(tics)
long tics;
{
	register long *t = &tics;
	struct
	{
		int hiint;
		unsigned loint;
	};

	tpflag = 0;
	tprintf("%d:%d:%d.%d",
	    (*t =/ 60, t)->loint,
	     (*t =/ 60, t)->loint % 60,
	      (*t =/ HZ, t)->loint % 60,
	       (t->loint % HZ) * 2);
}

tprintf(s, a)
register char *s;
int a;
{
	register *ap = &a;
	register char c;

	while(c = *s++)
		if(c == '%')
		{
			tprint1n((*ap)/10);
			if(*++s == '.')
				tpflag++;
			tprint1n((*ap++)%10);
		}
		else
			putc(tpflag ? c : ' ');
}

tprint1n(n)
char n;
{
	putc(((n && ++tpflag) || tpflag) ? (n+'0') : ' ');
}

#ifdef	AUSAM
killer(argv)
register char **argv;
{
	register unsigned pid,upper;
	int sig;

	sig = 9;
	while(*argv)
	{
		if(**argv == '-')
		{
			(*argv)++;
			sig = atoi(*argv);
			if(sig <= 0 || sig >= 20)
			{
				die("Bad signal", 0);
				return;
			}
		}
		else
		{
			pid = nscan(argv);
			if(**argv == '-')
			{
				(*argv)++;
				upper = nscan(argv);
				if(pid > upper)
				{
					die("Lower bound exceeds upper bound", 0);
					break;
				}
			}
			else
				upper = 0;
			if(**argv)
			{
				die(NONUM, *argv);
				break;
			}
			do
			{
				if( kill(pid, sig) < 0 && upper == 0)
				{
					die(itoa(pid), " - Not found");
				}
			}
			while(pid++ < upper);
		}
		argv++;
	}
}

nscan(s)
char **s;
{
	register char *c;
	register i;

	c = *s;
	i = 0;
	while(*c <= '9' && *c >= '0')
		i = i*10 + *c++ - '0';
	*s = c;
	return(i);
}
#endif	AUSAM

#ifndef	AUSAM
accfix()
{
	char	*p;		/* pointer to pwbuff */
	char	c;		/* scratch character */
	char	*cp;		/* pointer to character "c"  */
	char	low_uid;	/* low byte of getuid() call */
	int	uid;		/* user I*/
	int	x_uid;		/* user id from pwbuff */
	int	pwfd;		/* file descriptor for password file */
	int EOF;		/* end of file flag */

	EOF = 0;		/* set end of file flag */
	cp = &c;		/* set the address of "c" */
	low_uid = getuid();
	uid = low_uid;
	if ((pwfd = open("/etc/passwd",0)) < 0 ) 
	{
		/* error */
		prints(2,"no /etc/passwd\n");
		exit(1);	/* kill */
	}
	while(!EOF)
	{
		/* read the parameter file */
		p = &pwbuff[0];	/* set up pointer */
		do
		{
			if(!(read(pwfd, cp, 1)))
				/* end of file */
				EOF++;		/* set flag on */
			*p++ = c;	/* store the character */
		}
		while((c != '\n') && !EOF);
		x_uid = set_uid();
		if(uid == x_uid)
			/* user password entry found */
			 break;
	}
	if(EOF)
	{
		/* error */
		prints(2, "user search failed.\n");
		exit(1);	/* kill */
	}
	set_name();
	set_dir();
	set_tty();
	close(pwfd);	/* close passwd file descriptor */
}

set_uid()
{
	int x_uid;	/* returned user ID */
	char *pw;	/* pointer to pwbuff */

	pw = skip_colon(uid_pos);
	x_uid = fix_atoi(pw);
	return(x_uid);
}

set_name()
{
	char *pw;	/* pointer to pwbuff */
	int n;		/* scratch integer   */

	n = 0;		/* set index */
	pw = &pwbuff[0];
	while(*pw != ':')
	{
		/* read in login_name */
		user.u_logname[ n++ ] = *pw++;
	}
}

set_dir()
{
	int n;	/* scratch integer */
	char *pw;	/* pointer to pwbuff */

	n = 0;
	pw = skip_colon(dir_pos);
	while(*pw != ':')
	{
		/* read login directory name */
		user.u_logdir[ n++ ] = *pw++;
	}
}

char *skip_colon(pos)
int pos;	/* number of colons to be skipped */
{
	char *pw;	/* pointer to pwbuff */
	int n;		/* scratch integer   */

	n = 0;
	pw = &pwbuff[0];
	do
	{
		if(*pw == ':')
			/* add 1 to colon count */
			n++;
		pw++;
	}
	while(n < pos);
	return(pw);	/*indexintopwbuff*/
}

set_tty()
{
	user.u_logtty = ttyn(2);
}

fix_atoi(pw)
char *pw;
{

	char *sp;		/* string pointer */
	int i;		/* holds the integer result */

	sp = pw;	/* pass address */
	i = 0;
	while(1)
	{
		if((*sp < '0') || (*sp > '9'))
			/* end of integer string */
			break;
		i = i*10 + (*sp++ -'0');
	}
	return i;
}
#endif	AUSAM

unnext()
{
	seek(0,0,2);
	setexit(INTR,1);
}