1 /* Locating a program in a given path.
2 Copyright (C) 2001-2004, 2006-2020 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"
35 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
36 /* Native Windows, OS/2, DOS */
37 # define NATIVE_SLASH '\\'
40 # define NATIVE_SLASH '/'
43 /* Separator in PATH like lists of pathnames. */
44 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
45 /* Native Windows, OS/2, DOS */
46 # define PATH_SEPARATOR ';'
49 # define PATH_SEPARATOR ':'
52 /* The list of suffixes that the execlp/execvp function tries when searching
54 static const char * const suffixes
[] =
56 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
57 "", ".com", ".exe", ".bat", ".cmd"
58 /* Note: Files without any suffix are not considered executable. */
59 /* Note: The cmd.exe program does a different lookup: It searches according
60 to the PATHEXT environment variable.
61 See <https://stackoverflow.com/questions/7839150/>.
62 Also, it executes files ending in .bat and .cmd directly without letting
63 the kernel interpret the program file. */
64 #elif defined __CYGWIN__
68 #elif defined __DJGPP__
69 "", ".com", ".exe", ".bat"
76 find_in_given_path (const char *progname
, const char *path
,
77 bool optimize_for_exec
)
80 bool has_slash
= false;
84 for (p
= progname
; *p
!= '\0'; p
++)
93 /* If progname contains a slash, it is either absolute or relative to
94 the current directory. PATH is not used. */
95 if (optimize_for_exec
)
96 /* The execl/execv/execlp/execvp functions will try the various
97 suffixes anyway and fail if no executable is found. */
101 /* Try the various suffixes and see whether one of the files
102 with such a suffix is actually executable. */
105 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
106 const char *progbasename
;
111 progbasename
= progname
;
112 for (p
= progname
; *p
!= '\0'; p
++)
114 progbasename
= p
+ 1;
118 /* Try all platform-dependent suffixes. */
119 failure_errno
= ENOENT
;
120 for (i
= 0; i
< sizeof (suffixes
) / sizeof (suffixes
[0]); i
++)
122 const char *suffix
= suffixes
[i
];
124 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
125 /* File names without a '.' are not considered executable, and
126 for file names with a '.' no additional suffix is tried. */
127 if ((*suffix
!= '\0') != (strchr (progbasename
, '.') != NULL
))
130 /* Concatenate progname and suffix. */
132 xconcatenated_filename ("", progname
, suffix
);
134 /* On systems which have the eaccess() system call, let's
135 use it. On other systems, let's hope that this program
136 is not installed setuid or setgid, so that it is ok to
137 call access() despite its design flaw. */
138 if (eaccess (progpathname
, X_OK
) == 0)
140 /* Check that the progpathname does not point to a
144 if (stat (progpathname
, &statbuf
) >= 0)
146 if (! S_ISDIR (statbuf
.st_mode
))
149 if (strcmp (progpathname
, progname
) == 0)
163 failure_errno
= errno
;
169 errno
= failure_errno
;
176 /* If PATH is not set, the default search path is implementation dependent.
177 In practice, it is treated like an empty PATH. */
182 /* Make a copy, to prepare for destructive modifications. */
183 char *path_copy
= xstrdup (path
);
187 failure_errno
= ENOENT
;
188 for (path_rest
= path_copy
; ; path_rest
= cp
+ 1)
194 /* Extract next directory in PATH. */
196 for (cp
= path_rest
; *cp
!= '\0' && *cp
!= PATH_SEPARATOR
; cp
++)
198 last
= (*cp
== '\0');
201 /* Empty PATH components designate the current directory. */
205 /* Try all platform-dependent suffixes. */
206 for (i
= 0; i
< sizeof (suffixes
) / sizeof (suffixes
[0]); i
++)
208 const char *suffix
= suffixes
[i
];
210 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
211 /* File names without a '.' are not considered executable, and
212 for file names with a '.' no additional suffix is tried. */
213 if ((*suffix
!= '\0') != (strchr (progname
, '.') != NULL
))
216 /* Concatenate dir, progname, and suffix. */
218 xconcatenated_filename (dir
, progname
, suffix
);
220 /* On systems which have the eaccess() system call, let's
221 use it. On other systems, let's hope that this program
222 is not installed setuid or setgid, so that it is ok to
223 call access() despite its design flaw. */
224 if (eaccess (progpathname
, X_OK
) == 0)
226 /* Check that the progpathname does not point to a
230 if (stat (progpathname
, &statbuf
) >= 0)
232 if (! S_ISDIR (statbuf
.st_mode
))
235 if (strcmp (progpathname
, progname
) == 0)
239 /* Add the "./" prefix for real, that
240 xconcatenated_filename() optimized away.
241 This avoids a second PATH search when the
242 caller uses execl/execv/execlp/execvp. */
244 XNMALLOC (2 + strlen (progname
) + 1, char);
245 progpathname
[0] = '.';
246 progpathname
[1] = NATIVE_SLASH
;
247 memcpy (progpathname
+ 2, progname
,
248 strlen (progname
) + 1);
260 failure_errno
= errno
;
270 /* Not found in PATH. */
273 errno
= failure_errno
;