crypto/sha256-buffer: Use 'restrict'.
[gnulib.git] / lib / findprog-in.c
blobc254f2f588eaad0db9734724ed98b6daf627a74a
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>
30 #include "filename.h"
31 #include "concat-filename.h"
32 #include "xalloc.h"
34 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
35 /* Native Windows, OS/2, DOS */
36 # define NATIVE_SLASH '\\'
37 #else
38 /* Unix */
39 # define NATIVE_SLASH '/'
40 #endif
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 ';'
46 #else
47 /* Unix */
48 # define PATH_SEPARATOR ':'
49 #endif
51 /* The list of suffixes that the execlp/execvp function tries when searching
52 for the program. */
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__
64 "", ".exe", ".com"
65 #elif defined __EMX__
66 "", ".exe"
67 #elif defined __DJGPP__
68 "", ".com", ".exe", ".bat"
69 #else /* Unix */
71 #endif
74 const char *
75 find_in_given_path (const char *progname, const char *path,
76 bool optimize_for_exec)
79 bool has_slash = false;
81 const char *p;
83 for (p = progname; *p != '\0'; p++)
84 if (ISSLASH (*p))
86 has_slash = true;
87 break;
90 if (has_slash)
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. */
97 return progname;
98 else
100 /* Try the various suffixes and see whether one of the files
101 with such a suffix is actually executable. */
102 int failure_errno;
103 size_t i;
104 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
105 const char *progbasename;
108 const char *p;
110 progbasename = progname;
111 for (p = progname; *p != '\0'; p++)
112 if (ISSLASH (*p))
113 progbasename = p + 1;
115 #endif
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))
127 #endif
129 /* Concatenate progname and suffix. */
130 char *progpathname =
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)
139 /* Found! */
140 if (strcmp (progpathname, progname) == 0)
142 free (progpathname);
143 return progname;
145 else
146 return progpathname;
149 if (errno != ENOENT)
150 failure_errno = errno;
152 free (progpathname);
156 errno = failure_errno;
157 return NULL;
162 if (path == NULL)
163 /* If PATH is not set, the default search path is implementation dependent.
164 In practice, it is treated like an empty PATH. */
165 path = "";
168 int failure_errno;
169 /* Make a copy, to prepare for destructive modifications. */
170 char *path_copy = xstrdup (path);
171 char *path_rest;
172 char *cp;
174 failure_errno = ENOENT;
175 for (path_rest = path_copy; ; path_rest = cp + 1)
177 const char *dir;
178 bool last;
179 size_t i;
181 /* Extract next directory in PATH. */
182 dir = path_rest;
183 for (cp = path_rest; *cp != '\0' && *cp != PATH_SEPARATOR; cp++)
185 last = (*cp == '\0');
186 *cp = '\0';
188 /* Empty PATH components designate the current directory. */
189 if (dir == cp)
190 dir = ".";
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))
201 #endif
203 /* Concatenate dir, progname, and suffix. */
204 char *progpathname =
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)
213 /* Found! */
214 if (strcmp (progpathname, progname) == 0)
216 free (progpathname);
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. */
222 progpathname =
223 XNMALLOC (2 + strlen (progname) + 1, char);
224 progpathname[0] = '.';
225 progpathname[1] = NATIVE_SLASH;
226 memcpy (progpathname + 2, progname,
227 strlen (progname) + 1);
230 free (path_copy);
231 return progpathname;
234 if (errno != ENOENT)
235 failure_errno = errno;
237 free (progpathname);
241 if (last)
242 break;
245 /* Not found in PATH. */
246 free (path_copy);
248 errno = failure_errno;
249 return NULL;