/*
 * execvpe.c
 *
 * execvpe() function (from which we build execlp, execlpe, execvp).
 *
 * This version of execvpe() will *not* spawn /bin/sh if the command
 * return ENOEXEC.  That's what #! is for, folks!
 *
 * Since execlpe() and execvpe() aren't in POSIX, nor in glibc,
 * I have followed QNX precedent in the implementation of the PATH:
 * the PATH that is used is the one in the current environment, not
 * in the new environment.  Otherwise it would be impossible to pass
 * a different PATH to the new process than the one one would want to
 * use to search.
 */

#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#define DEFAULT_PATH 	"/bin:/usr/bin:."

int execvpe(const char *file, char * const *argv, char * const *envp)
{
  char path[PATH_MAX];
  const char *searchpath, *esp;
  size_t prefixlen, filelen, totallen;

  if ( strchr(file, '/') )	/* Specific path */
    return execve(file, argv, envp);

  filelen = strlen(file);

  searchpath = getenv("PATH");
  if ( !searchpath )
    searchpath = DEFAULT_PATH;

  errno = ENOENT; /* Default errno, if execve() doesn't change it */

  do {
    esp = strchr(searchpath, ':');
    if ( esp )
      prefixlen = esp-searchpath;
    else
      prefixlen = strlen(searchpath);

    if ( prefixlen == 0 || searchpath[prefixlen-1] == '/' ) {
      totallen = prefixlen+filelen;
      if ( totallen >= PATH_MAX )
	continue;
      memcpy(path, searchpath, prefixlen);
      memcpy(path+prefixlen, file, filelen);
    } else {
      totallen = prefixlen+filelen+1;
      if ( totallen >= PATH_MAX )
	continue;
      memcpy(path, searchpath, prefixlen);
      path[prefixlen] = '/';
      memcpy(path+prefixlen+1, file, filelen);
    }
    path[totallen] = '\0';

    execve(path, argv, envp);
    if ( errno == E2BIG || errno == ENOEXEC ||
	 errno == ENOMEM || errno == ETXTBSY )
      break;			/* Report this as an error, no more search */

    searchpath = esp+1;
  } while ( esp );

  return -1;
}
