/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: dosys.c 1.11 89/11/01 $@(#)make:dosys.c	1.3.1.2"

# include "defs"
# include "sys/stat.h"
# include "signal.h"
#ifdef SVAE
#define ARG_MAX 12288
#define vfork fork
#else
# include "sys/limits.h"
#endif  /* SVAE */


int intrupt();

struct process aproc;

char derr[ 1024 ];	/* if doexec or doshell fails, error message is placed here */

extern char Makecall;
extern char funny[256];

dosys(comstring, nohalt, noprint, nowait, reclevel)
register CHARSTAR comstring;
int nohalt, noprint, nowait, reclevel;
{
	register CHARSTAR p;
	CHARSTAR ps;
	CHARSTAR pmt = prompt;
	register int i;
	int status, pid;
	register struct process *procp;

	p = comstring;
	while(	*p == BLANK ||
		*p == TAB) p++;
	if(!*p)
		return(-1);

	if(IS_ON(NOEX) && Makecall == NO)
		return(0);

	/* make sure fewer than proclimit processes are running */
	while (proclive >= proclimit) {
		enbint(SIG_IGN);
		waitproc(&status);
		enbint(intrupt);
	}
	waitlevel(reclevel+1);

	if (IS_OFF(SIL) && !noprint)
	{
		ps = p = comstring;
		while(1)
		{
			while(*p && *p != NEWLINE) p++;
			if(*p)
			{
				*p = 0;
				printf("%s%s\n", pmt, ps);
				*p = NEWLINE;
				ps = p + 1;
				p = ps;
			}
			else
			{
				printf("%s%s", pmt, ps);
				break;
			}
		}
	}

	pid = metas(comstring) ? doshell(comstring,nohalt) : doexec(comstring);
	if( pid == -1 )
		fatal("\nToo many processes - Can't fork.\n");
	procp = (struct process *)malloc(sizeof (struct process));
	procp->link = aproc.link; aproc.link = procp;
	procp->pid = pid;
	procp->nohalt = nohalt;
	procp->nowait = nowait;
	procp->reclevel = reclevel;
	++proclive;

	if(IS_OFF(SIL) && !noprint) {
		if(nowait)
			printf(" &%d\n", procp->pid);
		else
			printf("\n");
	}
	fflush(stdout);

	if (*derr != '\0')
		fatal1(derr);

	if(nowait) {
		return 0;
	}

	return waitlevel(reclevel);
}

waitlevel(reclevel) /*  wait till none of the procs above reclevel is live */
int reclevel;
{
	int npending, status, totstatus;
	register struct process *procp;

	totstatus = 0;
	npending = 0;

	for(procp = aproc.link; procp; procp = procp->link)
		if( procp->reclevel >= reclevel )
			++npending;
	enbint(SIG_IGN);
#ifdef MKDEBUG
	if(IS_ON(DBUG))
	{
		printf("waitlevel (%d) %d pending\n", reclevel, npending);
	}
#endif

	while(npending > 0)
		{
		if(waitproc(&status) >= reclevel)
			--npending;
		totstatus |= status;
		}

	enbint(intrupt);
	return totstatus;
}

waitproc(statp)
register int *statp;
{
	register struct process *procp, *procl;
	char junk[50];
	int pid, level, sig;

	pid = wait(statp);
	if(pid == -1) {
		perror("wait ret");
		exit(1);
	}
#ifdef MKDEBUG
	if(IS_ON(DBUG))
	{
		printf("process %d done, status = %d\n", pid, *statp);
	}
#endif
	for(procl = &aproc, procp = aproc.link; procp; procp = (procl=procp)->link)
		if(procp->pid == pid) {
			--proclive;
			level = procp->reclevel;
			procl->link = procp->link;
			free(procp);
			if(*statp == 0)
				return level;	/* reclevel */
			if(procp->nowait)
				printf("\n%d: ", pid);
			if( *statp>>8 )
				printf("*** Error code %d", *statp>>8 );
			else	printf("*** Termination code %d", *statp );

			printf(procp->nohalt ? " (ignored)\n" : "\n");
			fflush(stdout);
			if( IS_OFF(KEEPGO) && !procp->nohalt)
				fatal("");
			return level;	/* reclevel */
		}
	sprintf(junk, "spurious return from process %d", pid);
	fatal(junk);
	/*NOTREACHED*/
}


metas(s)   /* Are there are any  Shell meta-characters? */
register CHARSTAR s;
{
	while(*s)
		if( funny[(unsigned char) *s++] & META)
			return(YES);

	return(NO);
}

doshell(comstring,nohalt)
register CHARSTAR comstring;
register int nohalt;
{
	register CHARSTAR shell;
	int pid;
	char	**envtmp;
	char	*envlst[NENV];
	char	envbuf[ARG_MAX];

	envtmp = environ;
	if((pid = vfork()) == 0)
	{
		enbint(0);
		doclose();

		setenv(envlst, envbuf, &envbuf[ARG_MAX]);
		environ = envlst;
		shell = varptr("SHELL")->varval;
		if(shell == 0 || shell[0] == CNULL)
			shell = SHELLCOM;
#ifdef CCS
		execlp(shell, "sh", (nohalt ? "-c" : "-ce"), comstring, 0);
#else
		execl(shell, "sh", (nohalt ? "-c" : "-ce"), comstring, 0);
#endif
		strcpy(derr, "Couldn't load Shell");
		_exit(1);
	} else if (pid == -1)
		fatal("Couldn't fork");
	environ = envtmp;
	return pid;
}

doclose()	/* Close open directory files before exec'ing */
{
	register OPENDIR od;

	for (od = firstod; od != 0; od = od->nextopendir)
		if (od->dirfc != NULL)
			/*
			 * vfork kludge...
			 * we cannot call closedir since this will modify
			 * the parents data space; just call close directly.
			 */
			close(od->dirfc->dd_fd);
}





#define MAXARGV	200

doexec(str)
register CHARSTAR str;
{
	register CHARSTAR t;
	register CHARSTAR *p;
	CHARSTAR argv[MAXARGV];
	int status, pid;
	char	**envtmp;
	char	*envlst[NENV];
	char	envbuf[ARG_MAX];

	while( *str==BLANK || *str==TAB )
		++str;
	if( *str == CNULL )
		return(-1);	/* no command */

	p = argv;
	for(t = str ; *t ; )
	{
		if ((p - argv) >= MAXARGV)
			fatal1("%s:  Too many arguments.", str);
		*p++ = t;
		while(*t!=BLANK && *t!=TAB && *t!=CNULL)
			++t;
		if(*t)
			for( *t++ = CNULL ; *t==BLANK || *t==TAB  ; ++t);
	}

	*p = NULL;

	envtmp = environ;
	if((pid = vfork()) == 0)
	{
		enbint(0);
		doclose();
		setenv(envlst, envbuf, &envbuf[ARG_MAX]);
		environ = envlst;
		execvp(str, argv);
		sprintf(derr, "Cannot load %s", str);
		_exit(1);
	} else if (pid == -1)
		fatal("Couldn't fork");
	environ = envtmp;
	return pid;
}

touch(force, name)
register int force;
register char *name;
{
        struct stat stbuff;
        char junk[1];
        int fd;

        if( stat(name,&stbuff) < 0)
                if(force)
                        goto create;
                else
                {
                        fprintf(stderr,"touch: file %s does not exist.\n",name);
                        return;
                }
        if(stbuff.st_size == 0)
                goto create;
        if( (fd = open(name, 2)) < 0)
                goto bad;
        if( read(fd, junk, 1) < 1)
        {
                close(fd);
                goto bad;
        }
        lseek(fd, 0L, 0);
        if( write(fd, junk, 1) < 1 )
        {
                close(fd);
                goto bad;
        }
        close(fd);
        return;
bad:
        fprintf(stderr, "Cannot touch %s\n", name);
        return;
create:
        if( (fd = creat(name, 0666)) < 0)
                goto bad;
        close(fd);
}
