summary refs log tree commit diff
path: root/posix/execvp.c
blob: 2eda603c399a100749f8dcd01bd965318beb89f7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/* Copyright (C) 1991, 1992, 1995, 1996, 1997 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

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

   The GNU C Library 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
   Library General Public License for more details.

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

#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <paths.h>


static void
internal_function
execute (const char *file, char *const argv[])
{
  execv (file, argv);

  if (errno == ENOEXEC)
    {
      /* The file is accessible but it is not an executable file.
	 Invoke the shell to interpret it as a script.  */

      /* Count the arguments.  */
      int argc = 0;
      while (argv[argc++])
	;

      /* Construct an argument list for the shell.  */
      {
	char *new_argv[argc + 1];
	new_argv[0] = (char *) _PATH_BSHELL;
	new_argv[1] = (char *) file;
	while (argc > 1)
	  {
	    new_argv[argc] = argv[argc - 1];
	    --argc;
	  }

	/* Execute the shell.  */
	execv (new_argv[0], new_argv);
      }
	}
}


/* Execute FILE, searching in the `PATH' environment variable if it contains
   no slashes, with arguments ARGV and environment from `environ'.  */
int
execvp (file, argv)
     const char *file;
     char *const argv[];
{
  int got_eacces = 0;

  if (strchr (file, '/') != NULL)
    /* Don't search when it contains a slash.  */
    execute (file, argv);
  else
    {
      char *path, *p, *name;
      size_t len;

      path = getenv ("PATH");
      if (path == NULL)
	{
	  /* There is no `PATH' in the environment.
	     The default search path is the current directory
	     followed by the path `confstr' returns for `_CS_PATH'.  */
	  len = confstr (_CS_PATH, (char *) NULL, 0);
	  path = (char *) __alloca (1 + len);
	  path[0] = ':';
	  (void) confstr (_CS_PATH, path + 1, len);
	}

      len = strlen (file) + 1;
      name = __alloca (strlen (path) + len);
      p = path;
      do
	{
	  path = p;
	  p = strchr (path, ':');
	  if (p == NULL)
	    p = strchr (path, '\0');

	  if (p == path)
	    /* Two adjacent colons, or a colon at the beginning or the end
	       of `PATH' means to search the current directory.  */
	    (void) memcpy (name, file, len);
	  else
	    {
	      /* Construct the pathname to try.  */
	      char *tmp = __mempcpy (name, path, p - path);
	      *tmp++ = '/';
	      (void) __mempcpy (tmp, file, len);
	    }

	  /* Try to execute this name.  If it works, execv will not return.  */
	  execute (name, argv);

	  switch (errno)
	    {
	    case EACCES:
	      /* Record the we got a `Permission denied' error.  If we end
		 up finding no executable we can use, we want to diagnose
		 that we did find one but were denied access.  */
	      got_eacces = 1;
	    case ENOENT:
	    case ESTALE:
	      /* Those errors indicate the file is missing or not executable
		 by us, in which case we want to just try the next path
		 directory.  */
	      break;

	    default:
	      /* Some other error means we found an executable file, but
		 something went wrong executing it; return the error to our
		 caller.  */
	      return -1;
	    }
	}
      while (*p++ != '\0');
    }

  /* We tried every element and none of them worked.  */

  if (got_eacces)
    /* At least one failure was due to permissions, so report that error.  */
    __set_errno (EACCES);

  /* Return the error from the last attempt (probably ENOENT).  */
  return -1;
}