static char *RcsId = 
    "$Header: /src/bin/make/m/dosys.c,v 4.7 84/04/24 21:12:56 crl Exp $";

/* static char *sccsid = "@(#)dosys.c	4.1 (Berkeley) 81/02/28"; */

/*
 * $Log:	/src/bin/make/m/dosys.c,v $
 * Revision 4.7  84/04/24  21:12:56  crl
 * Replaced touch() with one that uses utime() since it's less work.
 * 
 * Revision 4.6  83/12/11  23:43:53  crl
 * Have closedir() use new directory structures, being careful since
 * 	we have used a vfork()
 * 
 * Revision 4.5  83/12/04  17:29:31  crl
 * In await() enable the interrupt catching before waiting so we will 
 * 	do the $@ deletion.
 * 
 * Revision 4.4  83/10/31  01:17:30  crl
 * Doshell() now looks for a variable SHELL which specifies the shell
 * 	to use in executing a command.
 * 
 * Revision 4.3  83/07/23  14:45:41  crl
 * sys/types.h included in defs.h.
 * 
 * Revision 4.2  83/07/22  14:14:26  crl
 * Made to work transparently with RCS.
 * 
 * Revision 4.1  83/03/01  00:43:12  crl
 * Vanilla 4.1 version
 */

#include "defs.h"
#include <signal.h>
#include <errno.h>
#include <sys/stat.h>

dosys(comstring,nohalt)
register char *comstring;
int nohalt;
{
	register int status;

	if(metas(comstring))
		status = doshell(comstring,nohalt);
	else
		status = doexec(comstring);
	return(status);
}

/* Are there are any  Shell meta-characters? */

metas(s)
register char *s;
{
	register char c;

	while( (funny[c = *s++] & META) == 0 )
		;
	return( c );
}

doshell(comstring,nohalt)
char *comstring;
int nohalt;
{
#ifdef SHELLENV
	char *getenv();
#endif
	static char *shellstr;
	static char *shellcom;
	struct varblock *v;

	if (shellcom == NULL) {
		v = varptr("SHELL");
		if ((shellcom = v->varval) == NULL) {
#ifdef SHELLENV
			if ((shellcom = getenv("SHELL")) == NULL)
				shellcom = SHELLCOM;
			else
				shellcom = copys(shellcom);
#else
			shellcom = SHELLCOM;
#endif
			v->varval = shellcom;
		}
		if ((shellstr = rindex(shellcom, '/')) == NULL)
			shellstr = shellcom;
		else
			shellstr++;
	}
	if((waitpid = vfork()) == 0) {
		enbint(SIG_DFL);
		doclose();
		execl(shellcom, shellstr, (nohalt ? "-c" : "-ce"), comstring, 0);
		fatal("Couldn't load Shell");
	}
	return( await() );
}

await()
{
	int status;
	register int pid;
	int intrupt();

	enbint(intrupt);
	while( (pid = wait(&status)) != waitpid)
		if(pid == -1)
			fatal("bad wait code");
	waitpid = 0;
	return(status);
}

/* Close open directory files before exec'ing */

doclose()
{
	register struct opendir *od;

	/*
	 * We have done a vfork, so we can`t do anything to stomp on our
	 * paren`t address space.  Unfortunately, the code above doesn`t
	 * work since our parent`s files remain open. And we can`t just
	 * do a closedir, since it will free a dynamically-allocated table
	 * entry. So we use this hack, calling close directly. If the
	 * interface ever changes, we are going to be out of luck.
	 */

	for (od = firstod; od; od = od->nxtopendir)
		if (od->dirfc != NULL)
			close(od->dirfc->dd_fd);
}

doexec(str)
register char *str;
{
	register char *t;
	char *argv[200];
	register char **p;

	while( *str==' ' || *str=='\t' )
		++str;
	if( *str == '\0' )
		return(-1);	/* no command */

	p = argv;
	for(t = str ; *t ; ) {
		*p++ = t;
		while(*t!=' ' && *t!='\t' && *t!='\0')
			++t;
		if(*t)
			for( *t++ = '\0' ; *t==' ' || *t=='\t'  ; ++t)
				;
	}

	*p = NULL;

	if((waitpid = vfork()) == 0) {
		enbint(SIG_DFL);
		doclose();
		enbint(intrupt);
		execvp(str, argv);
		fatal1("Cannot load %s",str);
	}

	return( await() );
}

touch(name)
char *name;
{
	struct stat stbuff;
	char buf[OUTMAX];
	time_t tm[2];
	int fd = -1;

	tm[0] = tm[1] = time((time_t *)NULL);
	if ((stat(name,&stbuff) < 0 && (fd = creat(name, 0666)) < 0)
			|| utime(name, tm) < 0) {
		sprintf(buf, "%s: cannot touch %s", progname, name);
		perror(buf);
	}
	if (fd >= 0)
		close(fd);
}
