This file is exec.def, from which is created exec.c.
It implements the builtin "exec" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.

Bash is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES exec.c

$BUILTIN exec
$FUNCTION exec_builtin
$SHORT_DOC exec [-cl] [-a name] file [redirection ...]
Exec FILE, replacing this shell with the specified program.
If FILE is not specified, the redirections take effect in this
shell.  If the first argument is `-l', then place a dash in the
zeroth arg passed to FILE, as login does.  If the `-c' option
is supplied, FILE is executed with a null environment.  The `-a'
option means to make set argv[0] of the executed process to NAME.
If the file cannot be executed and the shell is not interactive,
then the shell exits, unless the shell option `execfail' is set.
$END

#include <config.h>

#include "../bashtypes.h"
#include "posixstat.h"
#include <signal.h>
#include <errno.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "../execute_cmd.h"
#include "../findcmd.h"
#if defined (JOB_CONTROL)
#  include "../jobs.h"
#endif
#include "../flags.h"
#include "../trap.h"
#if defined (HISTORY)
#  include "../bashhist.h"
#endif
#include "common.h"
#include "bashgetopt.h"

/* Not all systems declare ERRNO in errno.h... and some systems #define it! */
#if !defined (errno)
extern int errno;
#endif /* !errno */

extern int subshell_environment;
extern REDIRECT *redirection_undo_list;

#if !defined (HAVE_WORKING_FORK)
extern int subshell_exit_builtin;
#endif


int no_exit_on_failed_exec;

/* If the user wants this to look like a login shell, then
   prepend a `-' onto NAME and return the new name. */
static char *
mkdashname (name)
     char *name;
{
  char *ret;

  ret = (char *)xmalloc (2 + strlen (name));
  ret[0] = '-';
  strcpy (ret + 1, name);
  return ret;
}

int
exec_builtin (list)
     WORD_LIST *list;
{
  int exit_value = EXECUTION_FAILURE;
  int cleanenv, login, opt;
  char *argv0, *command, **args, **env, *newname, *com2;

  cleanenv = login = 0;
  argv0 = (char *)NULL;

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "cla:")) != -1)
    {
      switch (opt)
	{
	case 'c':
	  cleanenv = 1;
	  break;
	case 'l':
	  login = 1;
	  break;
	case 'a':
	  argv0 = list_optarg;
	  break;
	default:
	  builtin_usage ();
	  return (EX_USAGE);
	}
    }
  list = loptend;

  /* First, let the redirections remain. */
  dispose_redirects (redirection_undo_list);
  redirection_undo_list = (REDIRECT *)NULL;

  if (list == 0)
    return (EXECUTION_SUCCESS);

#if defined (RESTRICTED_SHELL)
  if (restricted)
    {
      sh_restricted ((char *)NULL);
      return (EXECUTION_FAILURE);
    }
#endif /* RESTRICTED_SHELL */

  args = strvec_from_word_list (list, 1, 0, (int *)NULL);

#ifndef __DJGPP__
  /* A command with a slash anywhere in its name is not looked up in $PATH. */
  command = absolute_program (args[0]) ? args[0] : search_for_command (args[0]);
#else
  command = search_for_command (args[0]);
  if (command)
    {
      free (args[0]);
      args[0] = strdup (command);
    }
#endif

  if (command == 0)
    {
      sh_notfound (args[0]);
      exit_value = EX_NOTFOUND;	/* As per Posix.2, 3.14.6 */
      goto failed_exec;
    }

  com2 = full_pathname (command);
  if (com2)
    {
      if (command != args[0])
	free (command);
      command = com2;
    }

  if (argv0)
    {
      free (args[0]);
      args[0] = login ? mkdashname (argv0) : savestring (argv0);
    }
  else if (login)
    {
      newname = mkdashname (args[0]);
      free (args[0]);
      args[0] = newname;
    }

  /* Decrement SHLVL by 1 so a new shell started here has the same value,
     preserving the appearance.  After we do that, we need to change the
     exported environment to include the new value. */
  if (cleanenv == 0)
    adjust_shell_level (-1);

  if (cleanenv)
    env = (char **)NULL;
  else
    {	
      maybe_make_export_env ();
      env = export_env;
    }

#if defined (HISTORY)
  if (interactive_shell && subshell_environment == 0)
    maybe_save_shell_history ();
#endif /* HISTORY */

  restore_original_signals ();

#if defined (JOB_CONTROL)
  if (subshell_environment == 0)
    end_job_control ();
#endif /* JOB_CONTROL */

#ifdef HAVE_WORKING_FORK
  shell_execve (command, args, env);
#else
  if (!subshell_exit_builtin)
    {
      exit_value = shell_execve (command, args, env);
      if (exit_value >= 0)
	  exit (exit_value);
      exit_value = EXECUTION_FAILURE;
    }
  else
    {
      exit_value = shell_execve (command, args, env);
      if (cleanenv == 0)
	  adjust_shell_level (1);
      goto failed_exec;
    }
#endif

  /* We have to set this to NULL because shell_execve has called realloc()
     to stuff more items at the front of the array, which may have caused
     the memory to be freed by realloc().  We don't want to free it twice. */
  args = (char **)NULL;
  if (cleanenv == 0)
    adjust_shell_level (1);

  if (executable_file (command) == 0)
    {
      builtin_error ("%s: cannot execute: %s", command, strerror (errno));
      exit_value = EX_NOEXEC;	/* As per Posix.2, 3.14.6 */
    }
  else
    file_error (command);

failed_exec:
  if (command)
    free (command);

#ifdef HAVE_WORKING_FORK
  if (subshell_environment || (interactive == 0 && no_exit_on_failed_exec == 0))
    exit_shell (exit_value);
#endif

  if (args)
    strvec_dispose (args);

#ifndef HAVE_WORKING_FORK
  if (!subshell_environment && (interactive == 0 && no_exit_on_failed_exec == 0))
    exit_shell (exit_value);
#endif

  initialize_traps ();
  initialize_signals (1);

#if defined (JOB_CONTROL)
  restart_job_control ();
#endif /* JOB_CONTROL */

  return (exit_value);
}
