/*
 * Csh - a shell by PJAC@
 * command tree execution
 */
#include "csh.h"

rtree(tree)
struct	cmd	*tree;
{
	int	result = 0;
	int	xres;

	for(; tree ; tree = tree->cm_next){
		if(result = set_redirect(tree))
			break;
		if(tree->cm_child){
			result = rtree(tree->cm_child);
			if(result){
				unset_redirect(tree->cm_child);
				break;
			}
			if(tree->cm_argc <= 0){	/* no commands here */
				result = unset_redirect(tree);
				if(result)
					break;
				continue;
			}
		}
		result = r_tree(tree);
		xres = unset_redirect(tree);
		if(xres && !result)
			result = xres;
		if(result)
			break;
	}
	return(result);
}

unset_redirect(tree)
struct	cmd	*tree;
{
	if(tree->cm_ifile && *tree->cm_ifile == '|'){
		if(*IPipe){
			close(0);
			unlink(IPipe);
			*IPipe = 0;
		}
	}
	if(tree->cm_ofile){
		dup2(in_ofd, 1);	/* get the fd back */
		if(*tree->cm_ofile == '|'){
			if(tree->cm_ofile[1] == '&') /* get stderr back */
				dup2(in_efd, 2);
			if(!*OPipe){
		puterr("Csh err: wanted to give Opipe away, non there.\n");
				return(ERR);
			}
			if(*IPipe){
			puterr("Csh err: Tried to fill Ipipe - already set.\n");
				return(ERR);
			}
			strcpy(IPipe, OPipe);
			*OPipe = 0;
			return(OK);
		}
		if(*tree->cm_ofile == '&')
			dup2(in_efd, 2);
	}
	return(OK);
}

set_redirect(tree)
struct	cmd	*tree;
{
	char	*file;
	int	stde = FALSE;

	if(tree->cm_ifile != 0){
		/* someone want's redirection */
		if(*tree->cm_ifile == '|'){
			/* want's piped input */
			if(*IPipe == '\0'){
			puterr("Csh err: Wanted pipe input, non available.\n");
				return(ERR);
			}
			file = IPipe;
		}
		else
			file = tree->cm_ifile;
		close(0);
		if(openf(file, 0) != 0)
			return(ERR);
	}
	if(tree->cm_ofile == 0)
		return(OK);
	if(*tree->cm_ofile == '|'){
		if(*OPipe){
			puterr("Can't do multiple pipes yet.\n");
			return(ERR);
		}
		close(1);
		if(mkopipe() != 1)
			return(ERR);
		if(tree->cm_ofile[1] == '&')
			dup2(1, 2);
		return(OK);
	}
	file = tree->cm_ofile;
	if(*file == '&'){
		file++;
		stde = TRUE;
	}
	close(1);
	if(*file == '>'){
		if(openf(++file, 2) != 1)
			return(ERR);
	}
	else if(openf(file, 1) != 1)
		return(ERR);
	if(stde)
		dup2(1, 2);
	return(OK);
}

mkopipe()
{
	static	char	*pstrs[] = {
		"/tmp/",
		"/",
		0,
	};
	static	int	ser;
	char	**xp;
	int	result;

	for(xp = pstrs ; *xp ; xp++){
		strcpy(OPipe, *xp);
		strcat(OPipe, my_Itoa(getpid()));
		strcat(OPipe, "_");
		strcat(OPipe, my_Itoa(++ser));
		strcat(OPipe, ".pip");
		if((result = creat(OPipe, 0644)) >= 0)
			return(result);
	}
	puterr("Can't create pipe tmp file.\n");
	return(-1);
}

openf(file, mode)
char	*file;
{
	int	result = -1;
	long	lseek();

	switch(mode){
	case 0:
		result = open(file, 0);
		break;
	case 2:
		result = open(file, 1);
		/* fall through */
	case 1:
		if(result < 0)
			result = creat(file, 0644);
		if(mode == 2 && result >= 0)
			lseek(result, 0L, 2);
		break;
	}
	if(result < 0){
		if(mode == 0)
			puterr("Can't open '");
		else
			puterr("Can't create '");
		puterr(file);
		puterr("'.\n");
	}
	return(result);
}

r_tree(tree)
struct	cmd	*tree;
{
	struct	inbuilt	*inbp, *find_inb();
	struct	lvars	*lp;
	int	nargs, totargs, i;
	char	**xp;

	if(tree->cm_argc <= 0){
		puterr("Null command.\n");
		return(ERR);
	}
	for(lp = Aliases ; lp ; lp = lp->L_next)
		lp->L_flags &= ~L_ADONE;
retry:;
	for(lp = Aliases ; lp ; lp = lp->L_next)
		if(strcmp(lp->L_name, tree->cm_argv[0]) == 0)
			break;
	if(lp != 0 && (lp->L_flags & L_ADONE) == 0){
		/* got an alias, Blow the arg array */
		/* find out how many arguments there are in the alias */
		for(nargs = 0 ; lp->L_aval[nargs] ; nargs++);
		/* now blow the first arg away and replace by the other
		 * args
		 */
		free(tree->cm_argv[0]);
		if(nargs == 1)	/* simple case */
			tree->cm_argv[0] = strsave(lp->L_aval[0]);
		else {
			totargs = nargs + tree->cm_argc;
			xp = (char **)mmalloc(totargs * sizeof(char *));
			for(i = 0 ; i < nargs ; i++)
				xp[i] = strsave(lp->L_aval[i]);
			for(i = 1; nargs < totargs ; nargs++, i++)
				xp[nargs] = tree->cm_argv[i];
			free( (char *)tree->cm_argv);
			tree->cm_argv = xp;
			tree->cm_argc = totargs - 1;	/* -1 for argv[0] */
		}
		lp->L_flags |= L_ADONE;
		goto retry;
	}
	if(tree->cm_argc <= 0)	/* safety, aliases might be screwed */
		return(ERR);

	if(xflag && (!in_cshrc || XVflag)){
		for(xp = tree->cm_argv ; *xp ; ){
			put_s(*xp++);
			if(*xp)
				put_s(" ");
		}
		put_s("\n");
	}
	if(nflag)
		return(OK);
	if((inbp = find_inb(tree->cm_argv[0])) != NULL)
		return( (*inbp->in_prog)(tree->cm_argc, tree->cm_argv) );
	else
		return(exec_prog(tree));
}

struct	inbuilt *
find_inb(cmd)
char	*cmd;
{
	register struct	inbuilt	*ip;

	for(ip = incmds ; ip->in_cmd != 0 ; ip++)
		if(strcmp(ip->in_cmd, cmd) == 0)
			return(ip);
	return(0);
}

/*
 * execute a program
 */

exec_prog(tree)
struct	cmd	*tree;
{
	char	*p, *q, *s;
	char	lpath[128];
	char	*tpath, **xp;
	int	result = -1;
	int	hash;
	int	dnumb = 0;

	tpath = tree->cm_argv[0];
	to_fslash(tpath);
#ifdef	MSDOS
	/*
	 * Horrible hack time, if got a string with a space or a tab
	 * in it, put quote marks around it so that nasty spawn will work
	 */
	for(xp = tree->cm_argv ; p = *xp ; xp++)
		if(!*p || index(p, ' ') || index(p, '\t')){
			s = q = mmalloc(strlen(p) + 2 + 1);
			*q++ = '"';
			while(*q = *p)
				q++, p++;
			*q++ = '"';
			*q = 0;
			free(*xp);
			*xp = s;
		}
#endif	/* MSDOS */
	if(index(tpath, '/') || *tpath == '.'){
		strcpy(lpath, tpath);
		result = do_exec(lpath, tree);
	}
	else if(Path){
		hash = b_hash(tpath);	/* build hash number */
		for(p = Path; *p;){
#ifdef	MSDOS
			for(q = lpath ; *p && *p != ';' ; *q++ = *p++);
#else
			for(q = lpath ; *p && *p != ':' ; *q++ = *p++);
#endif
			if(*p)
				p++;
			if(q != lpath){
				if(*lpath != '.' && h_look(dnumb++, hash) == 0)
					continue;
				*q++ = '/';
			}
			for(s = tpath ; *q++ = *s++ ; );
			result = do_exec(lpath, tree);
			if(result >= 0)
				break;
			if(errno == E2BIG)
				break;
		}
	}
	else {
		result = -1;
		errno = 0;
	}
	if(result >= 0)
		mustcd = TRUE;
	else {
		puterr(tree->cm_argv[0]);
		if(errno == E2BIG)
			puterr(": Argument list too long.\n");
		else
			puterr(": Command not found.\n");
	}
	return(result);
}

do_exec(path, tree)
char	*path;
struct	cmd	*tree;
{
	char	*lasts;
	int	result;
	char	*dot;

#ifdef	MSDOS
	if((lasts = rindex(path, '/')) == NULL)
		lasts = path;
	if(index(lasts, '.') != NULL)
		return(try_it(path, tree));
	strcat(lasts, ".exe");
	result = try_it(path, tree);
	if(result >= 0)
		return(result);
	if(errno == E2BIG)
		return(-1);
	strcpy(dot = index(lasts, '.'), ".com");
	result = try_it(path, tree);
	if(result >= 0 || errno != ENOENT)
		return(result);
	*dot = '\0';
#else
	result = try_it(path, tree);
	if(result >= 0 || errno != ENOEXEC)
		return(result);
#endif
	if(is_shscript(path))
		result = do_shscript(path, tree);
	return(result);
}

try_it(path, tree)
char	*path;
struct	cmd	*tree;
{
	errno = 0;
#ifdef	MSDOS
	to_bslash(path);
	return(spawnve(P_WAIT, path, tree->cm_argv, Real_Environ));
#else
	return(spawnve(path, tree->cm_argv, Real_Environ));
#endif
}

#ifndef	MSDOS

spawnve(path, args, env)
char	*path, **args, **env;
{
	int	(*sig2)(), (*sig3)(), (*sig15)();
	int	child, status, pid;
#ifdef DEBUG
	write(2, "try_it:", 7);
	write(2, path, strlen(path));
	write(2, "\n", 1);
#endif
	sig2 = signal(SIGINT, SIG_IGN);
	sig3 = signal(SIGQUIT, SIG_IGN);
	sig15 = signal(SIGTERM, SIG_IGN);
	child = fork();
	if(child == -1){
		signal(SIGINT, sig2);
		signal(SIGQUIT, sig3);
		signal(SIGTERM, sig15);
		return(-1);
	}
	if(child == 0){
		/* child time */
#ifdef DEBUG
		write(2, "in_child\n", 9);
#endif
		signal(SIGINT, SIG_DFL);
		signal(SIGQUIT, SIG_DFL);
		signal(SIGTERM, SIG_DFL);
		execve(path, args, env);
		exit(113);
	}
#ifdef DEBUG
	write(2, "waiting\n", 8);
#endif
	while( (pid = wait(&status)) != child && pid != -1);
	signal(SIGINT, sig2);
	signal(SIGQUIT, sig3);
	signal(SIGTERM, sig15);
	if(pid == -1)
		return(-1);
	if(status & 0xFF)
		return(status & 0xFF);
	status = (status >> 8) & 0xFF;
	if(status == 113)
		return(-1);
	return(status);
}
#endif

is_shscript(path)
char	*path;
{
	int	fd;
	char	c;

	if((fd = open(path, 0)) < 0)
		return(0);
	if(read(fd, &c, 1) != 1){
		close(fd);
		return(0);
	}
	close(fd);
	return(c == '#');
}

do_shscript(path, tree)
char	*path;
struct	cmd	*tree;
{
	char	**xp, **oxp;
	int	i;

	xp = (char **)mmalloc(sizeof(char *) * (tree->cm_argc+2));
	for(i = 0, oxp = xp+1 ; i < tree->cm_argc ; i++, oxp++)
		*oxp = tree->cm_argv[i];
	*oxp = 0;
	*xp = strsave(path);
	strcpy(path, SHELL);
	oxp = tree->cm_argv;	/* save this argv */
	tree->cm_argv = xp;
	i = try_it(path, tree);	/* call the shell to exec it */
	free(tree->cm_argv[0]);
	free( (char *)tree->cm_argv);
	tree->cm_argv = oxp;
	return(i);
}
