#include <lib.h>
#include <unistd.h>
#define execl	_execl
#define execle	_execle
#define execv	_execv
#define execve	_execve

extern char **environ;		/* environment pointer */

#define	PTRSIZE	(sizeof(char *))
_PROTOTYPE( char *_sbrk, (int _incr)					);

#if _ANSI
#include	<stdarg.h>

PUBLIC int execl(char *name, ...)
{
	int retval;
	va_list ap;

	va_start(ap, name);
	retval = execve(name, (char **)ap, environ);
	va_end(ap);
	return retval;
}
#else
PUBLIC int execl(name, arg0)
char *name;
char *arg0;
{
  return(execve(name, &arg0, environ));
}
#endif

#if _ANSI
PUBLIC int execle(char *name, ...)
{
	int retval;
	va_list ap;
	char *p;

	va_start(ap, name);
	do {
	    p = va_arg(ap, char *);
	} while(p);
	p = (char *)va_arg(ap, char **);
	va_end(ap);
	va_start(ap, name);
	retval = execve(name, (char **)ap, (char **) p);
	va_end(ap);
	return retval;
}
#else
PUBLIC int execle(name, argv)
char *name, *argv;
{
  char **p;
  p = (char **) &argv;
  while (*p++)			/* null statement */
	;
  return(execve(name, &argv, (char **) *p));
}
#endif

PUBLIC int execv(name, argv)
char *name, *argv[];
{

  return(execve(name, argv, environ));
}

PUBLIC int execve(path, argv, envp)
char *path;			/* pointer to name of file to be executed */
char *argv[];			/* pointer to argument array */
char *envp[];			/* pointer to environment */
{
  register char **argtop;
  register char **envtop;

	/* Count the argument pointers and environment pointers. */
  for (argtop = argv; *argtop != (char *) NULL; ) argtop++;
  for (envtop = envp; *envtop != (char *) NULL; ) envtop++;
  return(__execve(path, argv, envp, (int)(argtop - argv), (int)(envtop - envp)));
}

PUBLIC int __execve(path, argv, envp, nargs, nenvps)
char *path;			/* pointer to name of file to be executed */
char *argv[];			/* pointer to argument array */
char *envp[];			/* pointer to environment */
int nargs;			/* number of args */
int nenvps;			/* number of environment strings */
{
/* This is split off from execve to be called from execvp, so execvp does not
 * have to allocate up to ARG_MAX bytes just to prepend "sh" to the arg array.
 */

  char *hp, **ap, *p;
  int i, stackbytes, npointers, overflow, temp;
  char *stack;
  /* Decide how big a stack is needed. Be paranoid about overflow. */
#if ARG_MAX > INT_MAX
#error /* overflow checks and sbrk depend on sizes being ints */
#endif
  overflow = FALSE;
  npointers = 1 + nargs + 1 + nenvps + 1;	/* 1's for argc and NULLs */
  stackbytes = 0;	/* changed because _len is used now */
  if (nargs < 0 || nenvps < 0 || nargs+nenvps < 0 || npointers < 0)
	overflow = TRUE;
  for (i = PTRSIZE; i != 0; i--) {
	temp = stackbytes + npointers;
	if (temp < stackbytes) overflow = TRUE;
	stackbytes = temp;
  }
  for (i = 0, ap = argv; i < nargs; i++) {
	temp = stackbytes + _len(*ap++);
	if (temp < stackbytes) overflow = TRUE;
	stackbytes = temp;
  }
  for (i = 0, ap = envp; i < nenvps; i++) {
	temp = stackbytes + _len(*ap++);
	if (temp < stackbytes) overflow = TRUE;
	stackbytes = temp;
  }
  temp = stackbytes + PTRSIZE - 1;
  if (temp < stackbytes) overflow = TRUE;
  stackbytes = (temp / PTRSIZE) * PTRSIZE;

  /* Check for overflow before committing sbrk. */
  if (overflow || stackbytes > ARG_MAX) {
	errno = E2BIG;
	return(-1);
  }

  /* Allocate the stack. */
  stack = _sbrk(stackbytes);
  if (stack == (char *) -1) {
	errno = E2BIG;
	return(-1);
  }

  /* Prepare the stack vector and argc. */
  ap = (char **) stack;
  hp = &stack[npointers * PTRSIZE];
  *ap++ = (char *) nargs;

  /* Prepare the argument pointers and strings. */
  for (i = 0; i < nargs; i++) {
	*ap++ = (char *) (hp - stack);
	p = *argv++;
	while ((*hp++ = *p++) != 0)
		;
  }
  *ap++ = (char *) NULL;

  /* Prepare the environment pointers and strings. */
  for (i = 0; i < nenvps; i++) {
	*ap++ = (char *) (hp - stack);
	p = *envp++;
	while ((*hp++ = *p++) != 0)
		;
  }
  *ap++ = (char *) NULL;

  /* Do the real work. */
  temp = _callm1(MM, EXEC, _len(path), stackbytes, 0, path, stack, NIL_PTR);
  _sbrk(-stackbytes);
  return(temp);
}
