intprops: Avoid bogus "warning: division by zero is undefined" on clang.
[gnulib.git] / lib / findprog-in.c
blob0f76e36ca43ea1ae05d07df0cfdacff69877d571
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/>. */
19 #include <config.h>
21 /* Specification. */
22 #include "findprog.h"
24 #include <errno.h>
25 #include <stdbool.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
31 #include "filename.h"
32 #include "concat-filename.h"
33 #include "xalloc.h"
35 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
36 /* Native Windows, OS/2, DOS */
37 # define NATIVE_SLASH '\\'
38 #else
39 /* Unix */
40 # define NATIVE_SLASH '/'
41 #endif
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 ';'
47 #else
48 /* Unix */
49 # define PATH_SEPARATOR ':'
50 #endif
52 /* The list of suffixes that the execlp/execvp function tries when searching
53 for the program. */
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__
65 "", ".exe", ".com"
66 #elif defined __EMX__
67 "", ".exe"
68 #elif defined __DJGPP__
69 "", ".com", ".exe", ".bat"
70 #else /* Unix */
72 #endif
75 const char *
76 find_in_given_path (const char *progname, const char *path,
77 bool optimize_for_exec)
80 bool has_slash = false;
82 const char *p;
84 for (p = progname; *p != '\0'; p++)
85 if (ISSLASH (*p))
87 has_slash = true;
88 break;
91 if (has_slash)
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. */
98 return progname;
99 else
101 /* Try the various suffixes and see whether one of the files
102 with such a suffix is actually executable. */
103 int failure_errno;
104 size_t i;
105 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
106 const char *progbasename;
109 const char *p;
111 progbasename = progname;
112 for (p = progname; *p != '\0'; p++)
113 if (ISSLASH (*p))
114 progbasename = p + 1;
116 #endif
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))
128 #endif
130 /* Concatenate progname and suffix. */
131 char *progpathname =
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
141 directory. */
142 struct stat statbuf;
144 if (stat (progpathname, &statbuf) >= 0)
146 if (! S_ISDIR (statbuf.st_mode))
148 /* Found! */
149 if (strcmp (progpathname, progname) == 0)
151 free (progpathname);
152 return progname;
154 else
155 return progpathname;
158 errno = EACCES;
162 if (errno != ENOENT)
163 failure_errno = errno;
165 free (progpathname);
169 errno = failure_errno;
170 return NULL;
175 if (path == NULL)
176 /* If PATH is not set, the default search path is implementation dependent.
177 In practice, it is treated like an empty PATH. */
178 path = "";
181 int failure_errno;
182 /* Make a copy, to prepare for destructive modifications. */
183 char *path_copy = xstrdup (path);
184 char *path_rest;
185 char *cp;
187 failure_errno = ENOENT;
188 for (path_rest = path_copy; ; path_rest = cp + 1)
190 const char *dir;
191 bool last;
192 size_t i;
194 /* Extract next directory in PATH. */
195 dir = path_rest;
196 for (cp = path_rest; *cp != '\0' && *cp != PATH_SEPARATOR; cp++)
198 last = (*cp == '\0');
199 *cp = '\0';
201 /* Empty PATH components designate the current directory. */
202 if (dir == cp)
203 dir = ".";
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))
214 #endif
216 /* Concatenate dir, progname, and suffix. */
217 char *progpathname =
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
227 directory. */
228 struct stat statbuf;
230 if (stat (progpathname, &statbuf) >= 0)
232 if (! S_ISDIR (statbuf.st_mode))
234 /* Found! */
235 if (strcmp (progpathname, progname) == 0)
237 free (progpathname);
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. */
243 progpathname =
244 XNMALLOC (2 + strlen (progname) + 1, char);
245 progpathname[0] = '.';
246 progpathname[1] = NATIVE_SLASH;
247 memcpy (progpathname + 2, progname,
248 strlen (progname) + 1);
251 free (path_copy);
252 return progpathname;
255 errno = EACCES;
259 if (errno != ENOENT)
260 failure_errno = errno;
262 free (progpathname);
266 if (last)
267 break;
270 /* Not found in PATH. */
271 free (path_copy);
273 errno = failure_errno;
274 return NULL;