/* Copyright (c) 1979 Regents of the University of California */
#include "sh.h"

/*
 * C shell
 */

execute(t, pipein, pipeout)
	register struct command *t;
	int *pipein, *pipeout;
{
	int pid, flags, pv[2];
	register struct command *t1;
	register char *cp;
	bool forked = 0;
	bool shudint;

	if (t == 0)
		return;
	switch (t->t_dtyp) {

	case TCOM:
		cp = t->t_dcom[0];
		if ((cp[0] & (QUOTE|TRIM)) == QUOTE)
			strcpy(cp, cp + 1);
		if ((t->t_dflg & FREDO) == 0)
			Dfix(t);		/* $ " ' \ */
		/* fall into... */

	case TPAR:
		flags = t->t_dflg;
		if (flags & FPOU)
			mypipe(pipeout);
		/*
		 * A child will be interruptible only under very
		 * certain conditions:
		 *	we must be monkeying with interrupts
		 *	the child must not be &'ed
		 *	we must not have had an "onintr -"
		 */
		shudint = setintr && (flags & FINT) == 0 && (!gointr || !eq(gointr, "-"));

		/*
		 * Must do << early so parent will know
		 * where input pointer should be
		 */
		if (flags & FHERE)
			close(0), heredoc(t->t_dlef);

		/*
		 * If not executing commands then
		 * all we must do is read forward in the input to
		 * account for << redirection if present.
		 */
		if (noexec) {
			if (flags & FHERE)
				close(0);
			return;
		}

		set("status", "0");
		pid = 0;

		/*
		 * Built-in functions
		 */
		if (t->t_dtyp == TCOM && isbfunc(t->t_dcom[0])) {
			/*
			 * If output is piped, or running & and we would
			 * eventually fork for non-builtin commands,
			 * then do it now, so we won't block.
			 */
			if ((flags & (FPOU|FAND)) && (flags & FPAR) == 0)
				pid = dofork(shudint), forked++;

			/*
			 * If the builtin is actually executed (some, e.g.
			 * time and nice may refuse to execute here)
			 * then either exit (if we forked) or close i/o
			 * and continue execution (if we didn't).
			 */
			if (pid == 0) {
				doio(t, pipein, pipeout);
				if (flags & FPOU) {
					close(pipeout[0]), close(pipeout[1]);
					pipeout[0] = pipeout[1] = -1;
				}
				if (setintr && forked) {
					if (shudint)
						signal(SIGINT, SIG_DFL), signal(SIGQUIT, SIG_DFL);
					signal(SIGTERM, parterm);
					if (flags & FINT)
						setintr = 0;
				}
				if (func(t, pipein, pipeout)) {
					if (forked)
						exitstat();
					if (didfds && !(t->t_dflg & FREDO))
						donefds();
					return;
				}
			}
		}

		/*
		 * Now, we must make a new process since either the
		 * command is non-builtin, a parenthesized list,
		 * or builtin such as time or nice which really
		 * requires a child.
		 */
		if (!forked && (flags & FPAR) == 0)
			pid = dofork(shudint);
		if (pid != 0) {
			/*
			 * The parent path (or nobody does this if
			 * (flags & FPAR), i.e. date in (set;date))
			 */
			if (didfds == 0 && (flags & FPIN))
				close(pipein[0]), close(pipein[1]);
			if (didfds && !(t->t_dflg & FREDO))
				donefds();
			if (flags & FPRS)
				printf("%d\n", pid), set("child", putn(pid));
			/*
			 * Unless output is piped or command is &
			 * wait for it.
			 */
			if (t->t_dtyp == TCOM)
				cadd(pid, t->t_dcom[0]);
			else
				cadd(pid, "()");
			if ((flags & (FPOU|FAND)) == 0)
				pwait(pid);
			return;
		}

		/*
		 * Insure that this (child) shell doesn't muck on
		 */
		child++;

		/*
		 * If havent yet, finally set up the file descriptors.
		 */
		doio(t, pipein, pipeout);
		if (flags & FPOU)
			close(pipeout[0]), close(pipeout[1]);

		/*
		 * If mucking with interrupts fix interrupt, quit,
		 * and terminate handling ... in any case set setintr
		 * to 0 if we are not interruptible so that no further
		 * interrupt mucking occurs.
		 */
		if (setintr) {
			if (shudint)
				signal(SIGINT, SIG_DFL), signal(SIGQUIT, SIG_DFL);
			signal(SIGTERM, parterm);
			if (flags & FINT)
				setintr = 0;
		}

		/*
		 * For () commands must put new 0,1,2 in FSH* and recurse
		 */
		if (t->t_dtyp == TPAR) {
			t1 = t->t_dspr;
			t1->t_dflg |= flags & FINT;
			OLDSTD = dcopy(0, FOLDSTD);
			SHOUT = dcopy(1, FSHOUT);
			SHDIAG = dcopy(2, FSHDIAG);
			close(SHIN), SHIN = -1;
			didcch = 0, didfds = 0;
			execute(t1);
			exitstat();
		}
		if (eq(t->t_dcom[0], "nice")) {
/* sigh...
			nice(20);
			nice(-10);
*/
			cp = t->t_dcom[1];
			if (any(cp[0], "+-"))
				nice(getn(cp)), lshift(t->t_dcom, 2);
			else
				nice(4), lshift(t->t_dcom, 1);
			t->t_dflg = FPAR | FREDO;
			execute(t);
			exitstat();
		}
		if (eq(t->t_dcom[0], "nohup")) {
			if (setintr == 0)
				signal(SIGHUP, SIG_IGN);
			lshift(t->t_dcom, 1);
			t->t_dflg = FPAR | FREDO;
			execute(t);
			exitstat();
		}
		doexec(t);
		/* no return */

	case TFIL:
		flags = t->t_dflg;
		t1 = t->t_dcar;
		t1->t_dflg |= FPOU | (flags & (FPIN|FINT|FPRS|FDIAG));
		execute(t1, pipein, pv);
		t1 = t->t_dcdr;
		t1->t_dflg |= FPIN | (flags & (FPOU|FINT|FAND|FPRS|FPAR));
		execute(t1, pv, pipeout);
		return;

	case TLST:
		flags = t->t_dflg & FINT;
		if (t1 = t->t_dcar)
			t1->t_dflg |= flags, execute(t1);
		if (t1 = t->t_dcdr)
			t1->t_dflg |= t->t_dflg & (FINT|FPAR), execute(t1);
		return;

	case TOR:
	case TAND:
		flags = t->t_dflg & FINT;
		if (t1 = t->t_dcar) {
			t1->t_dflg |= flags, execute(t1);
			if ((getn(value("status")) == 0) == (t->t_dtyp == TAND))
				return;
		}
		if (t1 = t->t_dcdr)
			t1->t_dflg |= t->t_dflg & (FINT|FPAR), execute(t1);
		return;
	}
}

doio(t, pipein, pipeout)
	register struct command *t;
	int *pipein, *pipeout;
{
	register char *cp;
	register int flags = t->t_dflg;

	if (didfds || (flags & FREDO))
		return;
	if (flags & FHERE)
		goto skipin;
	close(0);
	if (cp = t->t_dlef) {
		cp = globone(Dfix1(cp));
		xfree(cp);
		if (open(cp, 0) < 0)
			Perror(cp);
	} else if (flags & FPIN)
		dup(pipein[0]), close(pipein[0]), close(pipein[1]);
	else if (flags & FINT)
		close(0), open("/dev/null", 0);
	else
		dup(OLDSTD);

skipin:
	close(1);
	if (cp = t->t_drit) {
		cp = globone(Dfix1(cp));
		xfree(cp);
		if ((flags & FCAT) && open(cp, 1) >= 0)
			lseek(1, 0l, 2);
		else {
			if (!(flags & FANY) && adrof("noclobber")) {
				if (flags & FCAT)
					Perror(cp);
				chkclob(cp);
			}
			if (creat(cp, 0644) < 0)
				Perror(cp);
		}
	} else
		dup((flags & FPOU) ? pipeout[1] : SHOUT);

	close(2);
	dup((flags & FDIAG) ? 1 : SHDIAG);
	didfds = 1;
}

dofork(shudint)
	bool shudint;
{
	register int pid, (*savint)();

	savint = signal(SIGINT, SIG_IGN);
	pid = fork();
	if (pid < 0) {
		signal(SIGINT, savint);
		error("No more processes");
	}
	if (pid == 0) {
		child++;
		signal(SIGINT, shudint ? SIG_DFL : savint);
	} else
		signal(SIGINT, savint);
	return (pid);
}

mypipe(pv)
	register int *pv;
{

	if (pipe(pv) < 0)
		goto oops;
	pv[0] = dmove(pv[0], -1);
	pv[1] = dmove(pv[1], -1);
	if (pv[0] >= 0 && pv[1] >= 0)
		return;
oops:
	error("Can't make pipe");
}

chkclob(cp)
	register char *cp;
{
	struct stat stb;

	if (stat(cp, &stb) < 0)
		return;
	if ((stb.st_mode & S_IFMT) == S_IFCHR)
		return;
	error("%s: File exists", cp);
}
