1BSD/s1/sh.c

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

#
/*
 * UC berkeley shell
 *
 * Modified by Bill Joy Oct 14, 1976
 *
 *  library "lib"
 *  cd for "chdir"
 *  interruptible waits
 *  new accounting scheme
 *  interactive (login shell) option "-i"
 *  shells with input and output tty's ignore interrupts
 *  verbose option "-v" causes input commands to be echoed
 *  $* for all arguments $1 to end
 *  >* >>* and |* to put unit 2 there also
 *  `cmd1 ; ... ; cmdn &*' means `( cmd1 ; ... ; cmdn ) &'
 *  pascal objects are recognized and px's are forked
 *  gimme and normal
 *  working directory not searched for root
 */

char	shell[]		"/bin/sh";
char	glob[]		"/etc/glob2";
char	px[]		"/bin/px";

#define	PCXN	0404
#define	INTR	2
#define	QUIT	3

#define LINSIZ 	1000
#define ARGSIZ 	50
#define TRESIZ 	100
#define LIBSIZ 	100

#define QUOTE 	0200

#define FAND 	1
#define FCAT 	2
#define FPIN 	4
#define FPOU 	8
#define FPAR 	16
#define FINT 	32
#define FPRS 	64
#define FDIAG 	128

#define	TCOM	1
#define	TPAR	2
#define	TFIL	3
#define	TLST	4

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

#define	ENOEXEC	8
#define	ENOMEM	12

int	niceness;
char	*dolp;
int	*dolnxt;
char	pidp[7];
int	dolc;
char	**dolv;
char	*promp		"% ";
char	*linep;
char	**argp;
int	*treep;
char	peekc;
char	gflg;
char	nofile;
char	error;
char	acctf;
char	*accpt;
char	*accbuf;
int	acct;
char	acnm[]		"/usr/adm/sha\0\0\0";
char	ty;
char	tyed;
char	echo;
int	uid;
char	setintr;
char	*arginp;
char	onelflg;

char	*mesg[] {
	0,
	"Hangup",
	0,
	"Quit",
	"Illegal instruction",
	"Trace/BPT trap",
	"IOT trap",
	"EMT trap",
	"Floating exception",
	"Killed",
	"Bus error",
	"Memory fault",
	"Bad system call",
	0,
	"Alarm clock",
	"Sig 15",
	"Sig 16",
};

char	line[LINSIZ];
char	*args[ARGSIZ];
int	trebuf[TRESIZ];
char	*lib;
char	*numpt		&pidp[sizeof pidp - 1];

main(c, av)
int c;
char **av;
{
	register f;
	register char **v, *cp;

	for(f=3; f<15; f++)
		close(f);
	numb(numb(getpid()));
	if ((uid = getuid()) == 0)
		promp[0] = '#';
	v = av;
	dolnxt = &v[c];
	if (c > 1) {
		if (*(cp = v[1]) == '-') {
			do
				switch(*cp++) {
					case 'v':
						echo++;
						break;
					case 'c':
						if (c > 2)
							arginp = v[2];
						goto l1;
					case 't':
						onelflg = 2;
					case '\0':

l1:
						*promp = '\0';
					case 'i':
						**v = '-';
						nofile++;
						break;
				}
			while (*cp);
			v++;
			c--;
		}
		if (nofile == 0 && c > 1) {
			close(0);
			if (open(cp = v[1], 0) < 0) {
				prs(v[1]);
				err(": cannot open");
			}
			*promp = '\0';
		}
	}
	if ((ty = ttyn(0)) != 'x' && ttyn(1) != 'x')
		**v = '-';
	if (**av == '-') {
		setintr++;
		signal(QUIT, 1);
		signal(INTR, 1);
	}
	dolv = v+1;
	dolc = c-1;
	numpt = &acnm[sizeof acnm - 1];
	numb((uid>>8) & 0377);
	numb(uid & 0377);
	if ((acctf = open(acnm, 1)) >= 0 || echo)
		accbuf = sbrk(256);
	setexit();

	for(;;) {
		prs(promp);
		if ((cp = accbuf) != 0) {
			*cp++ = ty;
			tyed++;
			*cp++ = ' ';
			accpt = cp;
			acct = 254;
		}
		peekc = getc();
		main1();
	}
}

numb(a)
{
	register i, j;

	i = a;
	j = 3;
	do {
		*--numpt = (i % 10) | '0';
		i =/ 10;
	} while(--j);
	return(i);
}

acflush()
{
	register f, c;
	register char *b;

	if ((b = accbuf) != 0) {
		c = 256 - acct;
		if ((f = acctf)>= 0) {
			seek(f, 0, 2);
			write(f, b, c);
		}
		if (echo) {
			if (tyed)
				b[0] = uid ? '%' : '#';
			seek(2, 0, 2);
			write(2, b, c);
		}
		accpt = b;
		acct = 256;
	}
}

main1()
{
	register char c, *cp;
	register *t;

	argp = args;
	linep = line;
	error = 0;
	gflg = 0;
	do {
		cp = linep;
		word();
	} while(*cp != '\n');
	treep = trebuf;
	if (gflg == 0) {
		if (error == 0)
			t = syntax(args, argp);
		if (error != 0)
			err("syntax error");
		else {
			acflush();
			execute(t);
		}
	}
}

word()
{
	register char c, c1;

	*argp++ = linep;

loop:
	switch(c = getc()) {

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

	case '\'':
	case '"':
		c1 = c;
		while((c = readc()) != c1) {
			if (c == '\n') {
				error++;
				peekc = c;
				return;
			}
			*linep++ = c|QUOTE;
		}
		goto pack;

	case '&':
		*linep++ = c;
		if ((c = getc()) == '*')
			*linep++ = c;
		else
			peekc = c;
		*linep++ = '\0';
		return;

	case ';':
	case '<':
	case '>':
	case '(':
	case ')':
	case '|':
	case '^':
	case '\n':
		*linep++ = c;
		*linep++ = '\0';
		return;
	}

	peekc = c;

pack:
	for(;;) {
		c = getc();
		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 > &trebuf[TRESIZ]) {
		prs("Command line overflow\n");
		error++;
		reset();	/* UGH */
	}
	return(t);
}

getc()
{
	register c;

	if (peekc) {
		c = peekc;
		peekc = 0;
		return(c);
	}
	if (argp > &args[ARGSIZ-5]) {
		argp =- 10;
		while((c=getc()) != '\n');
		argp =+ 10;
		err("Too many args");
		gflg++;
		return(c);
	}
	if (linep > &line[LINSIZ-5]) {
		linep =- 10;
		while((c=getc()) != '\n');
		linep =+ 10;
		err("Too many characters");
		gflg++;
		return(c);
	}
	for(;;) {
		if (dolp) {
			if ((c = *dolp++) != '\0')
				return(c);
			dolp = *dolnxt;
			c = dolp;
			if (c != -1) {
				dolnxt++;
				return(' ');
			}
			dolp = 0;
		}
		if ((c = readc()) == '\\')
			if ((c = readc()) == '\n')
				return(' ');
			else
				return(c | QUOTE);
		else if (c == '$') {
			c = readc();
			if (c >= '0' && c <= '9') {
				if (c - '0' < dolc)
					dolp = dolv[c - '0'];
				continue;
			}
			else if (c == '$') {
				dolp = &pidp[1];
				continue;
			}
			else if (c == '*') {
				if (dolc > 1) {
					dolp = dolv[1];
					dolnxt = &dolv[2];
				}
				continue;
			}
		}
		return(c&0177);
	}
}

readc()
{
	char cc;
	register c;
	register char *cp;

	if (arginp) {
		if (arginp == 1)
			exit(0);
		else if ((c = *arginp++) == '\0') {
			arginp = 1;
			c = '\n';
		}
	} else if (onelflg == 1 || read(0, &cc, 1) != 1)
		exit(0);
	else if ((c = cc) == '\n' && onelflg)
		onelflg--;
	if ((cp = accpt) != 0) {
		*cp++ = c;
		if (--acct == 0)
			acflush();
		else
			accpt = cp;
	}
	return(c);
}

/*
 * syntax
 *	empty
 *	syn1
 */

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

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

/*
 * syn1@fl=0
 *	syn2
 *	syn2 ; syntax
 *	syn2 &
 *
 * When fl=1 syn1 handles the vagarities of &*
 */

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

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

	case '(':
		l++;
		continue;

	case ')':
		l--;
		if (l < 0)
			error++;	/* too many ')'s */
		continue;

	case ';':
	case '\n':
		if (fl)
			continue;
	case '&':
		if (l == 0) {
			if (fl) {
				l = (*p)[1];
				if (l == '*')
					**p = ';';
				t1 = syn1(p1, p+1, 0);
				if (l == '*')
					if (t1[DTYP] == TLST) {
						t = tree(5);
						t[DTYP] = TPAR;
						t[DLEF] = 0;
						t[DRIT] = 0;
						t[DFLG] = FAND|FPRS|FINT;
						t[DSPR] = t1;
						t1 = t;
					} else {
						t = t1[DLEF];
						t[DFLG] =| FAND|FPRS|FINT;
					}
			} else {
				l = **p;
				t1 = syn2(p1, p);
				if (l == '&')
					t1[DFLG] =| FAND|FPRS|FINT;
			}
			t = tree(4);
			t[DTYP] = TLST;
			t[DFLG] = 0;
			t[DLEF] = t1;
			t[DRIT] = syntax(p+1, p2);
			return(t);
		}
	}
	if (l == 0)
		if (fl) {
			fl = 0;
			goto top;
		} else
			return(syn2(p1, p2));
	error++;
}

/*
 * 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);
			p++;
			if (p != p2 && **p == '*') {
				t[DFLG] = FDIAG;
				if ((*p)[1] != '\0') {
					(*p)++;
					p--;
				}
			} else {
				t[DFLG] = 0;
				p--;
			}
			t[DRIT] = syn2(p+1, p2);
			return(t);
		}
	}
	return(syn3(p1, p2));
}

/*
 * syn3
 *	( syn1@1 ) [ < 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=='>')
			flg =| FCAT; else
			p--;
		p++;
		if (p != p2 && **p == '*') {
			flg =| FDIAG;
			if ((*p)[1] != '\0') {
				(*p)++;
				p--;
			}
		} else
			p--;

	case '<':
		if (l == 0) {
			p++;
			if (p == p2) {
				error++;
				p--;
			}
			if (any(**p, "<>("))
				error++;
			if (c == '<') {
				if (i != 0)
					error++;
				i = *p;
			} else {
				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, 1);
	} 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, c;
	register *t;

	t = at+DCOM;
	while(p = *t++)
		while(c = *p)
			*p++ = (*f)(c);
}

tglob(c)
int c;
{

	if (any(c, "[?*"))
		gflg = 1;
	return(c);
}

trim(c)
int c;
{

	return(c&0177);
}

endwait()
{
	signal(INTR, 1);
	prs("wait: interrupted\n");
	reset();		/* URK! */
}

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

	if (t != 0)
	switch(t[DTYP]) {

	case TCOM:
		cp1 = t[DCOM];
		if (uid == 0) {
			if (equal(cp1, "gimme")) {
				nice(-20);
				return;
			}
			if (equal(cp1, "normal")) {
				nice(0);
				return;
			}
		}
		if (equal(cp1, "chdir") || equal(cp1, "cd")) {
			if (t[DCOM+1] != 0) {
				scan(t, &trim);
				if (chdir(t[DCOM+1]) < 0) {
					prs(cp1);
					err(": bad directory");
				}
			} else {
				prs(cp1);
				err(": arg count");
			}
			return;
		}
		if (equal(cp1, "shift")) {
			if (dolc < 1) {
				prs("shift: no args\n");
				return;
			}
			dolv[1] = dolv[0];
			dolv++;
			dolc--;
			return;
		}
		if (equal(cp1, "lib")) {
			if (cp2 = *(t1 = &t[DCOM+1])) {
				if (!(cp1 = lib))
					cp1 = lib = sbrk(LIBSIZ+1);
				i = LIBSIZ;
				for(;;) {
					while(*cp1++ = (*cp2++ & 0177))
						if (--i == 0) {
							err("lib: path too long");
							lib[0] = 0;
							return;
						}
					if (!(cp2 = *++t1))
						return;
					*(cp1-1) = ' ';
				}
			} else {
				prs(lib);
				prs("\n");
			}
			return;
		}
		if (equal(cp1, "wait")) {
			if (setintr && *promp) {
				signal(INTR, endwait);
				pwait(-1);
				signal(INTR, 1);
			} else
				pwait(-1);
			return;
		}
		if (equal(cp1, ":"))
			return;

	case TPAR:
		t1 = t;
		f = t1[DFLG];
		i = 0;
		if ((f&FPAR) == 0)
			i = fork();
		if (i == -1) {
			err("try again");
			return;
		}
		if (i != 0) {
			if ((f&FPIN) != 0) {
				close(pf1[0]);
				close(pf1[1]);
			}
			if ((f&FPRS) != 0) {
				prn(i);
				prs("\n");
			}
			if ((f&FAND) != 0)
				return;
			if ((f&FPOU) == 0)
				pwait(i);
			return;
		}
		if ((cp1 = t1[DLEF]) != 0) {
			close(0);
			if (open(cp1, 0) < 0) {
				prs(cp1);
				err(": cannot open");
				exit(1);
			}
		}
		if ((cp2 = t1[DRIT]) != 0) {
			close(1);
			if ((f&FCAT) != 0 && open(cp2, 1) >= 0)
				seek(1, 0, 2);
			else if (creat(cp2, 0644) < 0) {
				prs(t1[DRIT]);
				err(": cannot create");
				exit(1);
			}
		}
		if ((f&FPIN) != 0) {
			close(0);
			dup(pf1[0]);
			close(pf1[0]);
			close(pf1[1]);
		}
		if ((f&FPOU) != 0) {
			close(1);
			dup(pf2[1]);
			close(pf2[0]);
			close(pf2[1]);
		}
		if (f&FDIAG) {
			close(2);
			dup(1);
		}
		if ((f&FINT)!=0 && t1[DLEF]==0 && (f&FPIN)==0) {
			close(0);
			open("/dev/null", 0);
		}
		if ((f&FINT) == 0 && setintr) {
			signal(INTR, 0);
			signal(QUIT, 0);
		}
		if (t1[DTYP] == TPAR) {
			if (t1 = t1[DSPR])
				t1[DFLG] =| f&FINT;
			execute(t1);
			exit(1);
		}
		close(acctf);
		gflg = 0;
		scan(t1+1, &tglob);
		if (gflg) {
			t1 = &t1[DSPR-1];
			*t1++ = glob;
			if ((*t1 = lib) == 0)
				*t1 = &"";
			t1--;
			execv(*t1, t1);
			prs("glob: cannot execute\n");
			exit(1);
		}
		scan(t1, &trim);
		cp1 = t1[DCOM];
		while(*cp1 && *cp1 != '/')
			cp1++;
		if (*cp1 || uid)
			texec(t1[DCOM], t1);
		if (!*cp1) {
			if (cp2 = lib)
				for(;;) {
					while (*cp2 == ' ')
						cp2++;
					if (!*cp2)
						break;
					cp1 = linep;
					while(*cp2)
						if (*cp2 == ' ')
							break;
						else
							*cp1++ = *cp2++;
					scp = cp2;
					*cp1++ = '/';
					cp2 = t[DCOM];
					while(*cp1++ = *cp2++)
						continue;
					texec(linep, t1);
					cp2 = scp;
				}
			cp1 = linep;
			cp2 = "/usr/bin/";
			while(*cp1 = *cp2++)
				cp1++;
			cp2 = t1[DCOM];
			while(*cp1++ = *cp2++);
			texec(linep+4, t1);
			texec(linep, t1);
		}
		prs(t1[DCOM]);
		err(": not found");
		exit(1);

	case TFIL:
		f = t[DFLG];
		pipe(pv);
		t1 = t[DLEF];
		t1[DFLG] =| FPOU | (f&(FPIN|FINT|FPRS|FDIAG));
		execute(t1, pf1, pv);
		t1 = t[DRIT];
		t1[DFLG] =| FPIN | (f&(FPOU|FINT|FAND|FPRS));
		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;

	}
}

texec(f, at)
int *at;
{
	extern errno;
	register int *t, *t1;
	register i;
	int w;

	t = at;
	execv(f, (t1 = &t[DCOM]));
	if (errno == ENOEXEC) {
		*t1 = f;
		*--t1 = shell;
		i = open(f, 0);
		if (i >= 0) {
			if (read(i, &w, 2) == 2 && w == PCXN)
				*t1 = px;
			close(i);
		}
		execv(*t1, t1);
		prs("No ");
		prs(*t1);
		err("!!\n");
		exit(1);
	} else if (errno==ENOMEM) {
		prs(*t1);
		err(": too large");
		exit(1);
	}
}

err(s)
char *s;
{

	prs(s);
	prs("\n");
	if (*promp == '\0') {
		seek(0, 0, 2);
		exit();
	}
}

prs(as)
char *as;
{
	register char *s;

	if (s = as) {
		while(*s)
			s++;
		write(2, as, s-as);
	}
}

prn(n)
int n;
{
	register a;

	if (a = n/10)
		prn(a);
	n = n % 10 + '0';
	write(2, &n, 1);
}

getn(acp)
char *acp;
{
	register i;
	register char c, *cp;

	cp = acp;
	i = 0;
	while ((c = *cp++) != '\0')
		if (c >= '0' && c <= '9')
			i = 10 * i + c - '0';
	return(i);
}

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

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

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

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

pwait(ai)
int ai;
{
	register p, e, i;
	int s;

	if ((i = ai) != 0)
		do {
			p = wait(&s);
			if (p == -1)
				break;
			e = s & 0177;
			if (mesg[e] != 0) {
				if (p != i) {
					prn(p);
					prs(": ");
				}
				prs(mesg[e]);
				if (s & 0200)
					prs(" -- Core dumped");
			}
			if (e != 0)
				err("");
		} while (i != p);
}