/*
   89/03/02: ERS: added the "mode" argument for MS-DOS/Unix
   compatibility. Added prototypes for GNU C.

   fixed spawnve: the original didn't pass the command tail
   correctly, nor did it handle errno right. Also, this version
   passes args in the environment as well as the command line,
   using the ARGV= mechanism supported by crt0.s

   Written 89/01/10 by ERS. Original version Copyright (c) 1988 by
   Memorial University of Newfoundland. This version is based upon
   that original, but is substantially different. (In fact, there
   probably isn't a single line of the original left.)
*/

#include	<stdarg.h>
#include	<process.h>
#include	<param.h>
#include	<errno.h>
#include	<osbind.h>
#include	<stdlib.h>
#include	<time.h>
#include	<string.h>
#include	<fcntl.h>
#include	<unistd.h>
#include	"lib.h"

extern char **environ;

clock_t _child_runtime;
clock_t _sys_runtime;

#define TOS_ARGS 126
static	char	cmd[TOS_ARGS+1];

/*
 * FIXME: currently mode == P_OVERLAY doesn't work as advertised, i.e. it
 * doesn't free whatever memory it can.
 */

int
spawnve(mode, _path, argv, envp)
	int	mode;
	char	*_path;
	char	**argv;
	char	**envp;
{
	void		__exit(long);
	static char	path[MAXPATHLEN];
	size_t		cmlen;
	size_t		enlen = 0;
	char		*p;
	char		*s, *t;
	char		*env;
	clock_t		_child_start;
	long		rval;

	if (mode != P_WAIT && mode != P_OVERLAY && mode != P_NOWAIT) {
		errno = EINVAL;
		return -1;
	}
	(void)unx2dos(_path, path);	/* convert filename, if necessary */
	if (!envp)
		envp = environ;

/* count up space needed for environment */
	for(cmlen = 0; argv[cmlen]; cmlen++)
		enlen += strlen(argv[cmlen]) + 1;
	for(cmlen = 0; envp[cmlen]; cmlen++)
		enlen += strlen(envp[cmlen]) + 1;
	enlen += 32;	/* filler for stuff like ARGV= and zeros */

	if ((env = (char *)Malloc((long)enlen)) == NULL) {
		errno = ENOMEM;
		return -1;
	}
	s = env;
	while ((p = *envp) != 0) {
/*
 * NOTE: in main.c, we converted the PATH environment variable into
 * POSIX form. Here, we convert back into gulam form. Note that the
 * new variable will be shorter than the old, so space is not a problem.
 */
		if (!strncmp(p, "PATH=", 5)) {
			strncpy(s, p, 5); s += 5; p += 5;
			while (*p) {
				if (!strncmp(p, "/dev/", 5) && p[5]) {
					*s++ = p[5];
					*s++ = ':';
					p += 6;
				} else if (*p == ':') {
					*s++ = ','; p++;
				} else if (*p == '/') {
					*s++ = '\\'; p++;
				} else {
					*s++ = *p++;
				}
			}
		} else {
			while(*p)
				*s++ = *p++;
		}
		*s++ = '\0';
		envp++;
	}
	strcpy(s, "ARGV=");
	s += 6; /* s+=sizeof("ARGV=") */

/* copy argv[0] first (because it doesn't go into the command line */
	if (argv && *argv) {
		for (p = *argv; *p; )
			*s++ = *p++;
		*s++ = '\0';
	}

	bzero(t = cmd, sizeof(cmd));

/* s points at the environment's copy of the args */
/* t points at the command line copy to be put in the basepage */

	cmlen = 0;		/* was missing -- mmn, 92/03/10 */
	if (argv && *argv) {
		t++;
		while (*++argv) {
			p = *argv;
			while (*p) {
                              if (cmlen <= TOS_ARGS) {
                                      *t++ = *p; cmlen++;
                              }
				*s++ = *p++;
			}
                        if (cmlen <= TOS_ARGS && *(argv+1)) {
                                *t++ = ' '; cmlen++;
                        }
				*s++ = '\0';
		}
/*                *cmd = (char) cmlen;  NOT ANY MORE */
	}

	/* tie off environment */
	*s++ = '\0';
	*s = '\0';

	/* signal Extended Argument Passing */
	*cmd = 0x7f;

	/* close files marked for close on exec */
	for(cmlen = 0; cmlen < __NHANDLES; cmlen++)
	    if(__open_stat[cmlen].eclose)
		(void)close(cmlen + __SMALLEST_VALID_HANDLE);

	_child_start = clock();

/* MiNT and MicroRTX support background processes with Pexec(100,...) */
	cmlen = (mode == P_NOWAIT) ? 100 : PE_LOADGO;

	if ((rval = Pexec((int)cmlen, path, cmd, env)) < 0)
	{
		errno = -rval;
		cmlen = -1;
	}
	if (mode == P_OVERLAY)
		__exit(rval);

	(void)Mfree(env);
	_child_runtime += (clock() - _child_start);
	return rval;
}

int
spawnv(mode, path, argv)
	int mode;
	char *path;
	char **argv;
{
	return spawnve(mode, path, argv, environ);
}

#ifdef __STDC__
int spawnle(int mode, char *path, ...)
#else
int spawnle(mode, path)
	int	mode;
	char	*path;
#endif
{
	va_list args;
	char	***envp;

	va_start(args, path);

	for (envp = (char ***) args ; *envp ; envp++)
		;
	return spawnve(mode, path, (char **)args, *(envp+1));
}

#ifdef __STDC__
int spawnl(int mode, char *path, ...)
#else
int spawnl(mode, path)
	int	mode;
	char	*path;
#endif
{
	va_list args;

	va_start(args, path);
	return spawnve(mode, path, (char **)args, environ);
}
