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/>. */
31 #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 .bat and .cmd directly without letting the
62 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 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. */
104 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
105 const char *progbasename
;
110 progbasename
= progname
;
111 for (p
= progname
; *p
!= '\0'; p
++)
113 progbasename
= p
+ 1;
117 /* Try all platform-dependent suffixes. */
118 failure_errno
= ENOENT
;
119 for (i
= 0; i
< sizeof (suffixes
) / sizeof (suffixes
[0]); i
++)
121 const char *suffix
= suffixes
[i
];
123 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
124 /* File names without a '.' are not considered executable, and
125 for file names with a '.' no additional suffix is tried. */
126 if ((*suffix
!= '\0') != (strchr (progbasename
, '.') != NULL
))
129 /* Concatenate progname and suffix. */
131 xconcatenated_filename ("", progname
, suffix
);
133 /* On systems which have the eaccess() system call, let's
134 use it. On other systems, let's hope that this program
135 is not installed setuid or setgid, so that it is ok to
136 call access() despite its design flaw. */
137 if (eaccess (progpathname
, X_OK
) == 0)
140 if (strcmp (progpathname
, progname
) == 0)
150 failure_errno
= errno
;
156 errno
= failure_errno
;
163 /* If PATH is not set, the default search path is implementation dependent.
164 In practice, it is treated like an empty PATH. */
169 /* Make a copy, to prepare for destructive modifications. */
170 char *path_copy
= xstrdup (path
);
174 failure_errno
= ENOENT
;
175 for (path_rest
= path_copy
; ; path_rest
= cp
+ 1)
181 /* Extract next directory in PATH. */
183 for (cp
= path_rest
; *cp
!= '\0' && *cp
!= PATH_SEPARATOR
; cp
++)
185 last
= (*cp
== '\0');
188 /* Empty PATH components designate the current directory. */
192 /* Try all platform-dependent suffixes. */
193 for (i
= 0; i
< sizeof (suffixes
) / sizeof (suffixes
[0]); i
++)
195 const char *suffix
= suffixes
[i
];
197 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
198 /* File names without a '.' are not considered executable, and
199 for file names with a '.' no additional suffix is tried. */
200 if ((*suffix
!= '\0') != (strchr (progname
, '.') != NULL
))
203 /* Concatenate dir, progname, and suffix. */
205 xconcatenated_filename (dir
, progname
, suffix
);
207 /* On systems which have the eaccess() system call, let's
208 use it. On other systems, let's hope that this program
209 is not installed setuid or setgid, so that it is ok to
210 call access() despite its design flaw. */
211 if (eaccess (progpathname
, X_OK
) == 0)
214 if (strcmp (progpathname
, progname
) == 0)
218 /* Add the "./" prefix for real, that
219 xconcatenated_filename() optimized away. This
220 avoids a second PATH search when the caller uses
221 execl/execv/execlp/execvp. */
223 XNMALLOC (2 + strlen (progname
) + 1, char);
224 progpathname
[0] = '.';
225 progpathname
[1] = NATIVE_SLASH
;
226 memcpy (progpathname
+ 2, progname
,
227 strlen (progname
) + 1);
235 failure_errno
= errno
;
245 /* Not found in PATH. */
248 errno
= failure_errno
;