1 /* Locating a program in a given path.
2 Copyright (C) 2001-2004, 2006-2021 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001, 2019.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
32 #include "concat-filename.h"
34 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
35 /* Native Windows, OS/2, DOS */
36 # define NATIVE_SLASH '\\'
39 # define NATIVE_SLASH '/'
42 /* Separator in PATH like lists of pathnames. */
43 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
44 /* Native Windows, OS/2, DOS */
45 # define PATH_SEPARATOR ';'
48 # define PATH_SEPARATOR ':'
51 /* The list of suffixes that the execlp/execvp function tries when searching
53 static const char * const suffixes[] =
55 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
56 "", ".com", ".exe", ".bat", ".cmd"
57 /* Note: Files without any suffix are not considered executable. */
58 /* Note: The cmd.exe program does a different lookup: It searches according
59 to the PATHEXT environment variable.
60 See <https://stackoverflow.com/questions/7839150/>.
61 Also, it executes files ending in .bat and .cmd directly without letting
62 the kernel interpret the program file. */
63 #elif defined __CYGWIN__
67 #elif defined __DJGPP__
68 "", ".com", ".exe", ".bat"
75 find_in_given_path (const char *progname, const char *path,
76 const char *directory, bool optimize_for_exec)
79 bool has_slash = false;
83 for (p = progname; *p != '\0'; p++)
92 /* If progname contains a slash, it is either absolute or relative to
93 the current directory. PATH is not used. */
94 if (optimize_for_exec)
95 /* The execl/execv/execlp/execvp functions will try the various
96 suffixes anyway and fail if no executable is found. */
100 /* Try the various suffixes and see whether one of the files
101 with such a suffix is actually executable. */
105 const char *directory_as_prefix =
106 (directory != NULL && IS_RELATIVE_FILE_NAME (progname)
110 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
111 const char *progbasename;
116 progbasename = progname;
117 for (p = progname; *p != '\0'; p++)
119 progbasename = p + 1;
122 bool progbasename_has_dot = (strchr (progbasename, '.') != NULL);
125 /* Try all platform-dependent suffixes. */
126 failure_errno = ENOENT;
127 for (i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); i++)
129 const char *suffix = suffixes[i];
131 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
132 /* File names without a '.' are not considered executable, and
133 for file names with a '.' no additional suffix is tried. */
134 if ((*suffix != '\0') != progbasename_has_dot)
137 /* Concatenate directory_as_prefix, progname, suffix. */
139 concatenated_filename (directory_as_prefix, progname,
142 if (progpathname == NULL)
143 return NULL; /* errno is set here */
145 /* On systems which have the eaccess() system call, let's
146 use it. On other systems, let's hope that this program
147 is not installed setuid or setgid, so that it is ok to
148 call access() despite its design flaw. */
149 if (eaccess (progpathname, X_OK) == 0)
151 /* Check that the progpathname does not point to a
155 if (stat (progpathname, &statbuf) >= 0)
157 if (! S_ISDIR (statbuf.st_mode))
160 if (strcmp (progpathname, progname) == 0)
174 failure_errno = errno;
179 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
180 if (failure_errno == ENOENT && !progbasename_has_dot)
182 /* In the loop above, we skipped suffix = "". Do this loop
183 round now, merely to provide a better errno than ENOENT. */
186 concatenated_filename (directory_as_prefix, progname, "");
188 if (progpathname == NULL)
189 return NULL; /* errno is set here */
191 if (eaccess (progpathname, X_OK) == 0)
195 if (stat (progpathname, &statbuf) >= 0)
197 if (! S_ISDIR (statbuf.st_mode))
204 failure_errno = errno;
210 errno = failure_errno;
217 /* If PATH is not set, the default search path is implementation dependent.
218 In practice, it is treated like an empty PATH. */
222 /* Make a copy, to prepare for destructive modifications. */
223 char *path_copy = strdup (path);
224 if (path_copy == NULL)
225 return NULL; /* errno is set here */
231 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
232 bool progname_has_dot = (strchr (progname, '.') != NULL);
235 failure_errno = ENOENT;
236 for (path_rest = path_copy; ; path_rest = cp + 1)
240 char *dir_as_prefix_to_free;
241 const char *dir_as_prefix;
244 /* Extract next directory in PATH. */
246 for (cp = path_rest; *cp != '\0' && *cp != PATH_SEPARATOR; cp++)
248 last = (*cp == '\0');
251 /* Empty PATH components designate the current directory. */
255 /* Concatenate directory and dir. */
256 if (directory != NULL && IS_RELATIVE_FILE_NAME (dir))
258 dir_as_prefix_to_free =
259 concatenated_filename (directory, dir, NULL);
260 if (dir_as_prefix_to_free == NULL)
262 /* errno is set here. */
263 failure_errno = errno;
266 dir_as_prefix = dir_as_prefix_to_free;
270 dir_as_prefix_to_free = NULL;
274 /* Try all platform-dependent suffixes. */
275 for (i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); i++)
277 const char *suffix = suffixes[i];
279 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
280 /* File names without a '.' are not considered executable, and
281 for file names with a '.' no additional suffix is tried. */
282 if ((*suffix != '\0') != progname_has_dot)
285 /* Concatenate dir_as_prefix, progname, and suffix. */
287 concatenated_filename (dir_as_prefix, progname, suffix);
289 if (progpathname == NULL)
291 /* errno is set here. */
292 failure_errno = errno;
293 free (dir_as_prefix_to_free);
297 /* On systems which have the eaccess() system call, let's
298 use it. On other systems, let's hope that this program
299 is not installed setuid or setgid, so that it is ok to
300 call access() despite its design flaw. */
301 if (eaccess (progpathname, X_OK) == 0)
303 /* Check that the progpathname does not point to a
307 if (stat (progpathname, &statbuf) >= 0)
309 if (! S_ISDIR (statbuf.st_mode))
312 if (strcmp (progpathname, progname) == 0)
316 /* Add the "./" prefix for real, that
317 concatenated_filename() optimized away.
318 This avoids a second PATH search when the
319 caller uses execl/execv/execlp/execvp. */
321 (char *) malloc (2 + strlen (progname) + 1);
322 if (progpathname == NULL)
324 /* errno is set here. */
325 failure_errno = errno;
326 free (dir_as_prefix_to_free);
329 progpathname[0] = '.';
330 progpathname[1] = NATIVE_SLASH;
331 memcpy (progpathname + 2, progname,
332 strlen (progname) + 1);
335 free (dir_as_prefix_to_free);
345 failure_errno = errno;
350 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
351 if (failure_errno == ENOENT && !progname_has_dot)
353 /* In the loop above, we skipped suffix = "". Do this loop
354 round now, merely to provide a better errno than ENOENT. */
357 concatenated_filename (dir_as_prefix, progname, "");
359 if (progpathname == NULL)
361 /* errno is set here. */
362 failure_errno = errno;
363 free (dir_as_prefix_to_free);
367 if (eaccess (progpathname, X_OK) == 0)
371 if (stat (progpathname, &statbuf) >= 0)
373 if (! S_ISDIR (statbuf.st_mode))
380 failure_errno = errno;
386 free (dir_as_prefix_to_free);
393 /* Not found in PATH. */
396 errno = failure_errno;