/* sh - simple shell */

#define MAXLINE 200		/* maximum line length */
#define WORD 256		/* token code for words */
#define EOF -1			/* token code for end of file */
#define ispunct(c)		(c=='|' || c=='&' || c==';' || c=='<' || \
				 c=='>' || c=='(' || c==')' || c=='\n')
#define isspace(c)		(c==' ' || c=='\t')
#define execute(np)		(np ? (*(np)->op)(np) : 0)

struct node {			/* parse tree node */
	int (*op)();		/* operator function */
	struct node *args[2];	/* argument nodes */
	char *argv[100];	/* argument pointers */
	char *io[3];		/* i/o redirection */
} nodes[100];			/* node pool */
struct node *nfree;		/* next available node */
char strspace[10*MAXLINE];	/* string storage */
char *sfree;			/* next free character in strspace */
int t;				/* current token code */
char *token;			/* current token text (in strspace) */
int putback = 0;		/* lookahead */
int status = 0;			/* exit status of most recent command */
char **env;			/* environment of caller */
char *path[] = {".", "/bin", "/usr/bin", "/usr/ucb", 0};
extern int errno;
struct node *alloc(), *list(), *command(), *simple(), *pipeline();
int xsimple(), xpipeline(), xwait(), xnowait(), xsubshell();

main(argc, argv, envp)
int argc;
char *argv[], *envp[];
{
	struct node *np;

	env = envp;
	for (;;) {
		write(2, "$ ", 2);
		nfree = nodes;
		sfree = strspace;
		if ((t = gettoken()) == EOF) break;
		if (t != '\n')
			if (np = list()) execute(np);
			else perror("syntax error", "");
		while (t != EOF && t != '\n')	/* flush syntax errors */
			t = gettoken();
		}
	exit(status);
}

/* command - ( list ) [ ( < | > | >> ) word ]* | simple */
struct node *command()
{
	struct node *np;

	if (t != '(')
		return simple();
	np = alloc(xsubshell);
	t = gettoken();
	if (!(np->args[0] = list()) || t != ')') return 0;
	while ((t = gettoken()) == '<' || t == '>') if (!setio(np)) return 0;
	return np;
}

/* list - pipeline ( ( ; | & ) pipeline )* [ ; | & ]  (not LL(1), but ok) */
struct node *list()
{
	struct node *np, *np1;

	np = alloc((int (*)()) 0);
	if (!(np->args[1] = pipeline())) return 0;
	while (t == ';' || t == '&') {
		np->op = (t == ';') ? xwait : xnowait;
		t = gettoken();
		if (t == ')' || t == '\n')	/* tests ~first(pipeline) */
			break;
		np1 = alloc((int (*)()) 0);
		np1->args[0] = np;
		if (!(np1->args[1] = pipeline())) return 0;
		np = np1;
		}
	if (np->op == 0) np->op = xwait;
	return np;
}

/* pipeline - command ( | command )* */
struct node *pipeline()
{
	struct node *np, *np1;

	if (!(np = command())) return 0;
	while (t == '|') {
		np1 = alloc(xpipeline);
		np1->args[0] = np;
		t = gettoken();
		if (!(np1->args[1] = command())) return 0;
		np = np1;
		}
	return np;
}

/* redirect - redirect i/o according to np->io[] values */
redirect(np)
struct node *np;
{
	if (np->io[0]) {
		close(0);
		if (open(np->io[0], 0) != 0) 
			{ perror(": can't open", np->io[0]); exit(1); }
		}
	if (np->io[1]) {
		close(1);
		if (creat(np->io[1], 0644) != 1) 
			{ perror(": can't create", np->io[1]); exit(1); }
		}
	if (np->io[2]) {
		close(1);
		if (open(np->io[2], 1) != 1 && creat(np->io[2], 0644) != 1) 
			{ perror(": can't write", np->io[2]); exit(1); }
		lseek(1, 0L, 2);
		}
}

/* setio - ( < | > | >> ) word; fill in np->io[] */
int setio(np)
struct node *np;
{
	if (t == '<') { t = gettoken(); np->io[0] = token; }
	else if (t == '>') {
		t = gettoken();
		if (t == '>') { t = gettoken(); np->io[2] = token; }
		else np->io[1] = token;
		}
	else return 0;
	if (t != WORD) return 0;
	return 1;
}
			
/* simple - word ( [ < | > | >> ] word )* */
struct node *simple()
{
	struct node *np;
	int n = 1;

	if (t != WORD) return 0;
	np = alloc(xsimple);
	np->argv[0] = token;
	while ((t = gettoken()) == WORD || t == '<' || t == '>')
		if (t == WORD) np->argv[n++] = token;
		else if (!setio(np)) return 0;
	np->argv[n] = 0;
	return np;
}

/* strcmp - compare s1 and s2 */
int strcmp(s1, s2)
char *s1, *s2;
{
	while (*s1 == *s2++) if (*s1++ == 0) return 0;
	return *s1 - *--s2;
}

/* builtin - check np for builtin command and, if found, execute it */
int builtin(np)
struct node *np;
{
	int pid, n = 0;
	char *s, name[MAXLINE];

	if (s = np->argv[1])
		while (*s >= '0' && *s <= '9')
			n = 10*n + *s++ - '0';
	if (strcmp(np->argv[0], "cd") == 0) {
		if (np->argv[1] && chdir(np->argv[1]) == -1)
			perror(": bad directory", np->argv[0]);
		return 1;
		}
	else if (strcmp(np->argv[0], "exit") == 0)
		exit(np->argv[1] ? n : status);
	else if (strcmp(np->argv[0], "wait") == 0) {
		if (n > 0)
			while ((pid = wait(&status)) > 0 && pid != n)
				;
		else
			wait(0);
		return 1;
		}
	else if (strcmp(np->argv[0], "exec") == 0) {
		redirect(np);
		if (np->argv[1] == (char *) 0)
			return 1;
		for (n = 0; path[n]; n++) {
			sprintf(name, "%s/%s", path[n], np->argv[1]);
			execve(name, &np->argv[1], env);
			}
		perror(": not found", np->argv[1]);
		return 1;
		}
	return 0;
}

/* xpipeline - execute cmd | cmd */
int xpipeline(np)
struct node *np;
{
	int pid, n, fd[2];

	if (pipe(fd) < 0) { perror("can't create pipe", ""); return 0; }
	if ((pid = fork()) == 0) {	/* left side; redirect stdout */
		close(1);
		dup(fd[1]);
		close(fd[0]);
		close(fd[1]);
		execute(np->args[0]);
		exit(status);
		}
	else if (pid == -1) { perror("can't create process", ""); return 0; }
	if ((pid = fork()) == 0) {	/* right side; redirect stdin */
		close(0);
		dup(fd[0]);
		close(fd[0]);
		close(fd[1]);
		if ((pid = execute(np->args[1])) > 0) {
			while ((n = wait(&status)) > 0 && pid != n) ;
			if (n == -1) perror("wait error", "");
			}
		exit(status);
		}
	else if (pid == -1) { perror("can't create process", ""); return 0; }
	close(fd[0]);	/* avoid using up fd's */
	close(fd[1]);
	return pid;
}

/* xsimple - execute a simple command */
int xsimple(np)
struct node *np;
{
	char name[MAXLINE];
	int pid, i;

	if (builtin(np)) return 0;
	if (pid = fork()) {
		if (pid == -1) perror(": can't create process", np->argv[0]);
		return pid;
		}
	redirect(np);	/* child process */
	if (*np->argv[0] == '/' || *np->argv[0] == '.')
		execve(np->argv[0], np->argv, env);
	else
		for (i = 0; path[i]; i++) {
			sprintf(name, "%s/%s", path[i], np->argv[0]);
			execve(name, np->argv, env);
			}
	perror(": not found", np->argv[0]);
	exit(2);
}

/* xsubshell - execute (cmd) */
int xsubshell(np)
struct node *np;
{
	int pid;

	if (pid = fork()) {
		if (pid == -1) perror("can't create process", "");
		return pid;
		}
	redirect(np);	/* child process */
	execute(np->args[0]);
	exit(status);
}

/* xnowait - execute cmd & */
int xnowait(np)
struct node *np;
{
	char *s, buf[MAXLINE];

	execute(np->args[0]);
	sprintf(s = buf, "%5d\n", execute(np->args[1]));
	while (*s == ' ') s++;
	write(2, s, buf + 6 - s);
	return 0;
}

/* xwait - execute cmd ; */
int xwait(np)
struct node *np;
{
	int n, pid;

	execute(np->args[0]);
	if ((pid = execute(np->args[1])) > 0) {
		while ((n = wait(&status)) > 0 && n != pid) ;
		if (n == -1) perror("wait error", "");
		}
	return 0;
}

/* getc - get next, possibly pushed back, input character */
int getc()
{
	char c;

	if (putback) { c = putback; putback = 0; }
	else if (read(0, &c, 1) != 1) return EOF;
	return c;
}

/* gettoken - get next token into string space, return token code */
int gettoken()
{
	int c;

	while ((c = getc()) != EOF) if (!isspace(c)) break;
	if (c == EOF || ispunct(c)) return c;
	token = sfree;
	do {
		if (sfree >= strspace + sizeof(strspace) - 1) 
			{ perror("string storage overflow", ""); exit(2); }
		*sfree++ = c;
		} while ((c = getc()) != EOF && !ispunct(c) && !isspace(c));
	*sfree++ = 0;
	putback = c;
	return WORD;
}

/* alloc - allocate for op and return a node */
struct node *alloc(op)
int (*op)();
{
	if (nfree < nodes + sizeof(nodes)) {
		nfree->op = op;
		nfree->args[0] = nfree->args[1] = 0;
		nfree->argv[0] = nfree->argv[1] = 0;
		nfree->io[0] = nfree->io[1] = nfree->io[2] = 0;
		return nfree++;
		}
	perror("node storage overflow", "");
	exit(2);
}

/* perror - print error message s, prefixed by t */
perror(s, t)
char *s, *t;
{
	char buf[MAXLINE];

	if (errno > 0) sprintf(buf, "%s%s (%d)\n", t, s, errno);
	else sprintf(buf, "%s%s\n", t, s);
	for (s = buf; *s; s++) ;
	write(2, buf, s - buf);
	errno = 0;
}

