
/*
 * Shell.
 */
#include <stdio.h>

#define	SU	0
#define SIGQUIT	0
#define SIGINTR	0
#define LSIZE	40
#define	CSIZE	128
#define TSIZE	256

#define T_WORD	0
#define	T_NL	1
#define T_SEMI	2
#define T_LESS	3
#define	T_GREAT	4
#define T_APPND	5
#define T_AMPER	6
#define T_LPAR	7
#define T_RPAR	8

struct	tree {
	int	t_op;
	int	t_flag;
	struct	tree *t_lp;
	struct	tree *t_rp;
	char	*t_ifp;
	char	*t_ofp;
	char	*t_word[];
};

#define	APPND	01
#define ASYNC	02

int	dolc;
int	eflag;
int	iflag;
int	peekc;
int	ecode;

char	**dolv;
char	*dolp;
char	*cmdp;
char	*ttop;

char	*vbls[52];
int	line[LSIZE];
char	chrs[CSIZE];
char	tree[TSIZE];

char	*tmesg[] = {
	"Killed utterly",
	"Killed",
	"Hangup",
	"Quit",
	0,
	0,
	"FPP trap",
	"Sig 7",
	"Sig 8",
	"Sig 9",
	"Sig 10",
	"Sig 11",
	"Sig 12",
	"Sig 13",
	"Sig 14",
	"Sig 15",
	"Odd address trap",
	"Segmentation trap",
	"Illegal instruction",
	"FPP simulation trap",
	"BPT/Trace trap",
	"IOT trap",
	"Bad system call",
	"TRAP trap",
	0
};

char	*emesg[] = {
	"Unterminated string",
	"Command too long",
	"Syntax error",
	"Command too complex"
};

main(argc, argv)
char *argv[];
{
	register struct tree *tp;
	register char *p;
	register c;
	int *lp, uid[2];

	p = ": ";
	getuid(uid);
	if(uid[1] == SU)
		p = "# ";
	vbls['P'-'A'] = saves(p);
	--argc;
	++argv;
	if(argc>0 && argv[0][0]=='-') {
		p = &argv[0][1];
		--argc;
		++argv;
		while(c = *p++)
			switch(c) {

			case 'c':
			case 'C':
				vbls['P'-'A'] = 0;
				if(argc > 0) {
					--argc;
					cmdp = *argv++;
				}
				break;

			case 'e':
			case 'E':
				++eflag;
				break;

			case 'i':
			case 'I':
				++iflag;
			}
	}
	if(argc > 0) {
		fclose(stdin);
		stdin = fopen(argv[0], "r");
		if(stdin == NULL) {
			printf("%s: cannot open.\n", argv[0]);
			exit(1);
		}
		--argc;
		++argv;
		if(!eflag)
			vbls['P'-'A'] = 0;
	}
	if(iflag) {
		catch(SIGQUIT, 1);
		catch(SIGINTR, 1);
	}
	dolc = argc;
	dolv = argv;
	vbls['N'-'A'] = saven(dolc);
	for(;;) {
		if(p = vbls['P'-'A'])
			printf("%s", p);
		ecode = 0;
		setexit();
		if(ecode) {
			printf("%s.\n", emesg[ecode-1]);
			if(ecode < 3)
				while((c=get())!=EOF && c!='\n')
					;
			continue;
		}
		if((lp=readline()) == 0)
			break;
		ttop = tree;
		tp = parse(line, lp);
		execute(tp);
	}
}

readline()
{
	register char *cp;
	register c, *lp;

	lp = line;
	cp = chrs;
	for(;;) {
		c = get();
		if(c==' ' || c=='\t')
			continue;
		if(c == EOF)
			return(0);
		if(lp >= &line[LSIZE-1])
			oops(2);
		if(c == '\n') {
			*lp++ = T_NL;
			break;
		}
		if(c == ';') {
			*lp++ = T_SEMI;
			continue;
		}
		if(c == '(') {
			*lp++ = T_LPAR;
			continue;
		}
		if(c == ')') {
			*lp++ = T_RPAR;
			continue;
		}
		if(c == '<') {
			*lp++ = T_LESS;
			continue;
		}
		if(c == '&') {
			*lp++ = T_AMPER;
			continue;
		}
		if(c == '>') {
			c = get();
			if(c == '>')
				*lp++ = T_APPND;
			else {
				peekc = c;
				*lp++ = T_GREAT;
			}
			continue;
		}
		*lp++ = T_WORD;
		*lp++ = cp;
		if(c == '"')
			while((c=get1(1)) != '"') {
				if(c == '\n') {
					peekc = c;
					oops(1);
				}
				if(cp >= &chrs[CSIZE])
					oops(2);
				*cp++ = c|0200;
			}
		else {
			for(;;) {
				if(cp >= &chrs[CSIZE])
					oops(2);
				*cp++ = c;
				c = get();
				if(any(c, " \t'\"();&<>\n"))
					break;
			}
			peekc = c;
		}
		if(cp >= &chrs[CSIZE])
			oops(2);
		*cp++ = 0;
	}
	return(lp);
}

get()
{
	register c;

	if(c = peekc) {
		peekc = 0;
		return(c);
	}
	for(;;) {
		if(dolp) {
			if(c = *dolp++) {
				echo(c);
				return(c);
			}
			dolp = 0;
		}
		if((c=get1(0)) == '\\') {
			if((c=get1(0)) == '\n') {
				echo(' ');
				return(' ');
			}
			echo(c);
			return(c|0200);
		}
		if(c == '$') {
			c = get1(0);
			if(c>='0' && c<='9') {
				c -= '0';
				if(c < dolc)
					dolp = dolv[c];
				continue;
			}
			if(c>='a' && c<='z' || c>='A' && c<='Z') {
				if(c >= 'a')
					c += 'Z' + 1 - 'a';
				dolp = vbls[c-'A'];
				continue;
			}
		}
		echo(c);
		return(c);
	}
}

get1(ef)
{
	register c;

	if(cmdp) {
		if(cmdp == -1)
			return(EOF);
		c = *cmdp++;
		if(c == 0) {
			cmdp = -1;
			c = '\n';
		}
	} else {
		c = getchar();
		if(c == EOF)
			return(EOF);
	}
	if(ef)
		echo(c);
	return(c);
}

parse(p1, p2)
int *p1, *p2;
{
	register struct tree *tp;
	register o, *p;
	struct tree *lp;
	int l;

	p = p1;
	while(p!=p2 && (*p==T_NL || *p==T_SEMI || *p==T_AMPER))
		++p;
	if(p == p2)
		return(0);
	l = 0;
	for( ; p!=p2; ++p) {
		o = *p;
		if(o == T_LPAR) {
			++l;
			continue;
		}
		if(o == T_RPAR) {
			--l;
			if(l < 0)
				oops(3);
			continue;
		}
		if(o==T_NL || o==T_SEMI || o==T_AMPER) {
			if(l == 0) {
				tp = pcmnd(p1, p);
				if(o == T_AMPER)
					tp->t_flag |= ASYNC;
				if(o!=T_NL && (p[1]!=T_NL && p[1]!=T_RPAR)) {
					lp = treealloc(sizeof(*lp));
					lp->t_op = T_SEMI;
					lp->t_lp = tp;
					lp->t_rp = parse(p+1, p2);
					tp = lp;
				}
				return(tp);
			}
			continue;
		}
		if(o == T_WORD)
			++p;
	}
	if(l == 0)
		return(pcmnd(p1, p2));
	oops(3);
}

pcmnd(p1, p2)
int *p1, *p2;
{
	register struct tree *tp;
	register o, *p;
	int f, l, n;
	int *lp, *rp;
	char *ifp, *ofp;

	l = 0;
	f = 0;
	n = 0;
	lp = 0;
	rp = 0;
	ifp = 0;
	ofp = 0;
	for(p=p1; p!=p2; ++p) {
		o = *p;
		if(o == T_LPAR) {
			if(l == 0) {
				if(lp)
					oops(3);
				lp = p+1;
			}
			++l;
			continue;
		}
		if(o == T_RPAR) {
			--l;
			if(l == 0)
				rp = p;
			continue;
		}
		if(o==T_GREAT || o==T_APPND || o==T_LESS) {
			if(l == 0) {
				if(++p==p2 || (*p++)!=T_WORD)
					oops(3);
				if(o == T_LESS) {
					if(ifp)
						oops(3);
					ifp = *p;
				} else {
					if(ofp)
						oops(3);
					ofp = *p;
					if(o == T_APPND)
						f |= APPND;
				}
			}
			continue;
		}
		if(o == T_WORD) {
			++p;
			if(l == 0)
				p1[n++] = *p;
		}
	}
	if(lp) {
		if(rp==0 || n)
			oops(3);
		tp = treealloc(sizeof(*tp));
		tp->t_op = T_LPAR;
		tp->t_lp = parse(lp, rp);
	} else {
		if(n == 0)
			oops(3);
		p1[n++] = 0;
		tp = treealloc(sizeof(*tp) + n*sizeof(tp->t_word[0]));
		tp->t_op = T_WORD;
		for(l=0; l<n; ++l)
			tp->t_word[l] = p1[l];
	}
	tp->t_flag = f;
	tp->t_ifp = ifp;
	tp->t_ofp = ofp;
	return(tp);
}

treealloc(n)
register n;
{
	register char *p1, *p2;

	if(ttop+n > &tree[TSIZE])
		oops(4);
	p1 = p2 = ttop;
	ttop += n;
	while(n--)
		*p2++ = 0;
	return(p1);
}

execute(tp)
register struct tree *tp;
{
	register char *s;
	register i;
	int c;

	if(tp == 0)
		return;
	switch(tp->t_op) {

	case T_SEMI:
		execute(tp->t_lp);
		execute(tp->t_rp);
		break;

	case T_WORD:
		s = tp->t_word[0];
		if(equal(s, "chdir")) {
			if((s=tp->t_word[1]) == 0)
				printf("Usage: chdir directory\n");
			else if(chwdir(s) < 0)
				printf("%s: bad directory\n", s);
			break;
		}
		if(equal(s, "shift")) {
			if(dolc < 1)
				printf("No args to shift.\n");
			else {
				--dolc;
				++dolv;
				free(vbls['N'-'A']);
				vbls['N'-'A'] = saven(dolc);
			}
			break;
		}
		if(equal(s, "set")) {
			set(tp);
			break;
		}
		if(equal(s, "ask")) {
			ask(tp);
			break;
		}
		if(equal(s, "vars")) {
			for(i=0; i<dolc; ++i)
				printf("$%d %s\n", i, dolv[i]);
			for(i=0; i<52; ++i)
				if(s = vbls[i]) {
					if(i < 26)
						c = 'A'+i;
					else
						c = 'a'+i-26;
					printf("$%c %s\n", c, s);
				}
			break;
		}
		if(equal(s, ":"))
			break;

	case T_LPAR:
		if(tp->t_ifp)
			printf("Execute: ifp=%s\n", tp->t_ifp);
		if(tp->t_ofp)
			printf("Execute: ofp=%s\n", tp->t_ofp);
		if(tp->t_op == T_LPAR) {
			execute(tp->t_lp);
			break;
		}
		printf("Execute:");
		for(i=0; s=tp->t_word[i]; ++i)
			printf(" %s", s);
		if((tp->t_flag&ASYNC) != 0)
			printf(" (Asyncronous)");
		printf("\n");
	}
}

set(tp)
register struct tree *tp;
{
	register char *p, *s;
	char *v;
	int c, n1, n2, r;

	if((r=getr(tp->t_word[1])) < 0)
		return;
	if(tp->t_word[2]==0 || (s=tp->t_word[3])==0)
		goto bad;
	switch(c = tp->t_word[2][0]) {

	case '=':
		v = p = alloc(sizes(s));
		while(c = *s++)
			*p++ = c&0177;
		*p = 0;
		break;

	case '+':
	case '-':
		if(vbls[r] == 0)
			goto bad;
		n1 = grabn(vbls[r]);
		n2 = grabn(s);
		if(c == '+')
			n1 += n2;
		else
			n1 -= n2;
		v = saven(n1);
		break;

	default:
	bad:
		printf("Bad set command.\n");
		return;
	}
	free(vbls[r]);
	vbls[r] = v;
}

ask(tp)
register struct tree *tp;
{
	register char *s;
	register r;
	char b[30];

	if((r=getr(tp->t_word[1])) < 0)
		return;
	if((s=tp->t_word[2]) == 0)
		s = "Ask? ";
	printf("%s", s);
	if((s=gets(b)) == 0)
		s = "(End of file)";
	free(vbls[r]);
	vbls[r] = saves(s);
}

getr(s)
register char *s;
{
	register r;

	if(s == 0) {
		printf("Missing variable\n");
		return(-1);
	}
	r = *s;
	if(!(r>='A' && r<='Z') && !(r>='a' && r<='z')) {
		printf("Bad variable\n");
		return(-1);
	}
	if(r > 'Z')
		r += 'Z' + 1 - 'a';
	return(r-'A');
}

saves(s)
register char *s;
{
	register char *p1, *p2;

	p1 = s;
	while(*p1++)
		;
	p1 = p2 = alloc(p1-s);
	while(*p2++ = *s++)
		;
	return(p1);
}

grabn(s)
register char *s;
{
	register m, n;

	m = 0;
	if(*s == '-') {
		++s;
		++m;
	}
	for(n=0; *s; ++s)
		n = (10*n) + (*s-'0');
	if(m)
		n = -n;
	return(n);
}

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

	p = s;
	while(*p++)
		;
	return(p-s);
}

saven(n)
{
	register char *p;

	p = alloc(7);
	sprintf(p, "%d", n);
	return(p);
}

echo(c)
{
	if(eflag)
		putchar(c);
}

any(c, s)
register c;
register char *s;
{
	while(*s)
		if(*s++ == c)
			return(1);
	return(0);
}

oops(n)
{
	ecode = n;
	reset();
}

/*
 * RSX fakes
 */
catch()
{
}

chwdir()
{
	return(-1);
}


getuid(u)
int u[];
{
	u[0] = u[1] = SU;
}


