#ident	"@(#)vfork.c	1.3	93/06/08 SMI"	/* From AT&T Toolchest */

/*
 * UNIX shell
 *
 * S. R. Bourne
 * Rewritten by David Korn
 * Bell Telephone Laboratories
 *
 */

#include	"defs.h"
#include	"jobs.h"
#include	"sym.h"
#include	"builtins.h"
#ifdef _vfork_
#   include	<vfork.h>
#endif  /* _vfork_ */

/*
 * This module is provided to allow the Shell to work with vfork instead
 * of fork.  With vfork the data area is shared by parent and child.
 * Save state variables at fork and make Shell variables copy on write.
 * Restore everything to previous state when fork_exit is called and
 * terminate process.
 */


/*
 * Get the interpreter name given a script file 
 * The first line must be of the form #!  <iname>.
 * Returns 1 if <iname> is found, 0 otherwise
 */
int     get_shell(name,iname)
char *name;
char *iname;
{
	register int c;
	register int state = 0;
	register int fd;
	int n;
	char *cp;
	int	rval = 0;
	char buffer[256];
	cp = nam_strval(SHELLNOD);
	/* don't use csh */
	if(strcmp(path_basename(cp),"csh")==0)
		cp = 0;
	strcpy(iname,cp?cp:"/bin/sh");
	if((fd=open(name,0))<0)
		return(-1);
	n = read(fd,buffer,sizeof(buffer));
	cp = buffer;
	while(n-- > 0)
	{
		c = *cp++;
		switch(state)
		{
			case 0:
				if(c!='#')
					goto out;
				break;

			case 1:
				if(c!='!')
					goto out;
				break;

			case 2:
				if(c==' ' || c =='\t')
					continue;
			default:
				if(c=='\n')
				{
					*iname = 0;
					rval = 1;
					goto out;
				}
				*iname++ = c;
		}
		state++;
	}
out:
	close(fd);
	return(rval);
}

#ifdef VFORK
/* The following structure contains the variables that must be saved */
struct f_save
{
	struct	f_save	*f_save_fork;
	struct dolnod	*f_savearg;
	char		*f_staksave;
	int		f_stakoff;
	struct sh_scoped	f_st;
	struct jobs	f_jobstat;
	struct fileblk	*f_iotable[NFILE];
	int		f_inpipe[2];
	int		f_outpipe[2];
 	int		*f_sh_inp;
 	int		*f_sh_outp;
};

/* The following routines are defined by this module */
int	vfork_check();
void	vfork_restore();
int	vfork_save();

static struct f_save *save_fork;	/* most recently saved data */

/*
 * Save state on fork
 */

int	vfork_save()
{
	register struct f_save *fp;
	register int i;
	if((fp = new_of(struct f_save,0))==0)
		return(-1);
	fp->f_save_fork = save_fork;
	save_fork = fp;
	fp->f_stakoff = staktell(0);
	fp->f_staksave = stakfreeze(0);
	fp->f_st = st;
	fp->f_jobstat = job;
	job.pwlist = 0;
	fp->f_savearg = arg_use();
	memcpy(fp->f_iotable,io_ftable,sizeof(fp->f_iotable));
	for(i=0; i < NFILE; i++)
		fp->f_iotable[i] = io_ftable[i];
 	fp->f_sh_inp = sh.inpipe;
 	fp->f_sh_outp = sh.outpipe;
	if(sh.inpipe)
	{
		fp->f_inpipe[0] = sh.inpipe[0];
		fp->f_inpipe[1] = sh.inpipe[1];
	}
	if(sh.outpipe)
	{
		fp->f_outpipe[0] = sh.outpipe[0];
		fp->f_outpipe[1] = sh.outpipe[1];
	}
	st.states |= VFORKED;
	return(0);
}

/*
 * Restore state and exit
 */

void	vfork_restore()
{
	register struct f_save *fp = save_fork;

	if((st.states&VFORKED)==0)
		return;
 	sh.inpipe = fp->f_sh_inp;
 	sh.outpipe = fp->f_sh_outp;
	if(sh.inpipe)
	{
		sh.inpipe[0] = fp->f_inpipe[0];
		sh.inpipe[1] = fp->f_inpipe[1];
	}
	if(sh.outpipe)
	{
		sh.outpipe[0] = fp->f_outpipe[0];
		sh.outpipe[1] = fp->f_outpipe[1];
	}
	memcpy(io_ftable,fp->f_iotable,NFILE*sizeof(char*));
	st = fp->f_st;
	job = fp->f_jobstat;
	arg_free(fp->f_savearg,0);
	save_fork = fp->f_save_fork;
	stakset(fp->f_staksave,fp->f_stakoff);
	free(fp);
}


/*
 * returns non-zero if process should vfork, 0 otherwise
 * we do not vfork for functions and built-ins in the background
 */
int	vfork_check(t)
union anynode *t;
{
	register union anynode	*tf;
	register struct argnod *arg;
	register char *arg0 = NIL;
	struct namnod *np;
	int bltno;
	/* simple command */
	if((t->tre.tretyp&COMMSK)==TCOM)
		return(1);
	tf = t->fork.forktre;
	if((tf->com.comtyp&COMMSK)!=TCOM)
		return(0);
	/* background command */
	arg = tf->com.comarg;
	bltno = tf->com.comtyp>>(COMBITS+1);
	/* can't vfork assignments or most built-ins */
	if(arg==0 || bltno > SYSLOGIN)
		return(0);
	if(tf->com.comtyp&COMSCAN)
	{
		if(arg->argflag&A_RAW)
			arg0 = arg->argval;
	}
	else
		arg0 = *(((struct dolnod*)arg)->dolarg+1);
	/* no vfork if not sure */
	if(arg0==NIL)
		return(0);
	/* eliminate functions */
	if((np=nam_search(arg0,sh.var_tree,0)) &&  np->value.namval.ip)
		return(0);
	/* command substitution with i/o redirection use fork */
	if((t->tre.tretyp&FCOMSUB) && t->tre.treio==(struct ionod*)0)
		return(0);
	return(2);
}
#endif /* VFORK */

