Bump to m4 1.4.19
[platform/upstream/m4.git] / lib / findprog-in.c
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.
4
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.
9
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.
14
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/>.  */
17
18
19 #include <config.h>
20
21 /* Specification.  */
22 #include "findprog.h"
23
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>
30
31 #include "filename.h"
32 #include "concat-filename.h"
33
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
41
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
50
51 /* The list of suffixes that the execlp/execvp function tries when searching
52    for the program.  */
53 static const char * const suffixes[] =
54   {
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__
64     "", ".exe", ".com"
65     #elif defined __EMX__
66     "", ".exe"
67     #elif defined __DJGPP__
68     "", ".com", ".exe", ".bat"
69     #else /* Unix */
70     ""
71     #endif
72   };
73
74 const char *
75 find_in_given_path (const char *progname, const char *path,
76                     const char *directory, bool optimize_for_exec)
77 {
78   {
79     bool has_slash = false;
80     {
81       const char *p;
82
83       for (p = progname; *p != '\0'; p++)
84         if (ISSLASH (*p))
85           {
86             has_slash = true;
87             break;
88           }
89     }
90     if (has_slash)
91       {
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
99           {
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
105             const char *directory_as_prefix =
106               (directory != NULL && IS_RELATIVE_FILE_NAME (progname)
107                ? directory
108                : "");
109
110             #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
111             const char *progbasename;
112
113             {
114               const char *p;
115
116               progbasename = progname;
117               for (p = progname; *p != '\0'; p++)
118                 if (ISSLASH (*p))
119                   progbasename = p + 1;
120             }
121
122             bool progbasename_has_dot = (strchr (progbasename, '.') != NULL);
123             #endif
124
125             /* Try all platform-dependent suffixes.  */
126             failure_errno = ENOENT;
127             for (i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); i++)
128               {
129                 const char *suffix = suffixes[i];
130
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)
135                 #endif
136                   {
137                     /* Concatenate directory_as_prefix, progname, suffix.  */
138                     char *progpathname =
139                       concatenated_filename (directory_as_prefix, progname,
140                                              suffix);
141
142                     if (progpathname == NULL)
143                       return NULL; /* errno is set here */
144
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)
150                       {
151                         /* Check that the progpathname does not point to a
152                            directory.  */
153                         struct stat statbuf;
154
155                         if (stat (progpathname, &statbuf) >= 0)
156                           {
157                             if (! S_ISDIR (statbuf.st_mode))
158                               {
159                                 /* Found!  */
160                                 if (strcmp (progpathname, progname) == 0)
161                                   {
162                                     free (progpathname);
163                                     return progname;
164                                   }
165                                 else
166                                   return progpathname;
167                               }
168
169                             errno = EACCES;
170                           }
171                       }
172
173                     if (errno != ENOENT)
174                       failure_errno = errno;
175
176                     free (progpathname);
177                   }
178               }
179             #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
180             if (failure_errno == ENOENT && !progbasename_has_dot)
181               {
182                 /* In the loop above, we skipped suffix = "".  Do this loop
183                    round now, merely to provide a better errno than ENOENT.  */
184
185                 char *progpathname =
186                   concatenated_filename (directory_as_prefix, progname, "");
187
188                 if (progpathname == NULL)
189                   return NULL; /* errno is set here */
190
191                 if (eaccess (progpathname, X_OK) == 0)
192                   {
193                     struct stat statbuf;
194
195                     if (stat (progpathname, &statbuf) >= 0)
196                       {
197                         if (! S_ISDIR (statbuf.st_mode))
198                           errno = ENOEXEC;
199                         else
200                           errno = EACCES;
201                       }
202                   }
203
204                 failure_errno = errno;
205
206                 free (progpathname);
207               }
208             #endif
209
210             errno = failure_errno;
211             return NULL;
212           }
213       }
214   }
215
216   if (path == NULL)
217     /* If PATH is not set, the default search path is implementation dependent.
218        In practice, it is treated like an empty PATH.  */
219     path = "";
220
221   {
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 */
226
227     int failure_errno;
228     char *path_rest;
229     char *cp;
230
231     #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
232     bool progname_has_dot = (strchr (progname, '.') != NULL);
233     #endif
234
235     failure_errno = ENOENT;
236     for (path_rest = path_copy; ; path_rest = cp + 1)
237       {
238         const char *dir;
239         bool last;
240         char *dir_as_prefix_to_free;
241         const char *dir_as_prefix;
242         size_t i;
243
244         /* Extract next directory in PATH.  */
245         dir = path_rest;
246         for (cp = path_rest; *cp != '\0' && *cp != PATH_SEPARATOR; cp++)
247           ;
248         last = (*cp == '\0');
249         *cp = '\0';
250
251         /* Empty PATH components designate the current directory.  */
252         if (dir == cp)
253           dir = ".";
254
255         /* Concatenate directory and dir.  */
256         if (directory != NULL && IS_RELATIVE_FILE_NAME (dir))
257           {
258             dir_as_prefix_to_free =
259               concatenated_filename (directory, dir, NULL);
260             if (dir_as_prefix_to_free == NULL)
261               {
262                 /* errno is set here.  */
263                 failure_errno = errno;
264                 goto failed;
265               }
266             dir_as_prefix = dir_as_prefix_to_free;
267           }
268         else
269           {
270             dir_as_prefix_to_free = NULL;
271             dir_as_prefix = dir;
272           }
273
274         /* Try all platform-dependent suffixes.  */
275         for (i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); i++)
276           {
277             const char *suffix = suffixes[i];
278
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)
283             #endif
284               {
285                 /* Concatenate dir_as_prefix, progname, and suffix.  */
286                 char *progpathname =
287                   concatenated_filename (dir_as_prefix, progname, suffix);
288
289                 if (progpathname == NULL)
290                   {
291                     /* errno is set here.  */
292                     failure_errno = errno;
293                     free (dir_as_prefix_to_free);
294                     goto failed;
295                   }
296
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)
302                   {
303                     /* Check that the progpathname does not point to a
304                        directory.  */
305                     struct stat statbuf;
306
307                     if (stat (progpathname, &statbuf) >= 0)
308                       {
309                         if (! S_ISDIR (statbuf.st_mode))
310                           {
311                             /* Found!  */
312                             if (strcmp (progpathname, progname) == 0)
313                               {
314                                 free (progpathname);
315
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.  */
320                                 progpathname =
321                                   (char *) malloc (2 + strlen (progname) + 1);
322                                 if (progpathname == NULL)
323                                   {
324                                     /* errno is set here.  */
325                                     failure_errno = errno;
326                                     free (dir_as_prefix_to_free);
327                                     goto failed;
328                                   }
329                                 progpathname[0] = '.';
330                                 progpathname[1] = NATIVE_SLASH;
331                                 memcpy (progpathname + 2, progname,
332                                         strlen (progname) + 1);
333                               }
334
335                             free (dir_as_prefix_to_free);
336                             free (path_copy);
337                             return progpathname;
338                           }
339
340                         errno = EACCES;
341                       }
342                   }
343
344                 if (errno != ENOENT)
345                   failure_errno = errno;
346
347                 free (progpathname);
348               }
349           }
350         #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
351         if (failure_errno == ENOENT && !progname_has_dot)
352           {
353             /* In the loop above, we skipped suffix = "".  Do this loop
354                round now, merely to provide a better errno than ENOENT.  */
355
356             char *progpathname =
357               concatenated_filename (dir_as_prefix, progname, "");
358
359             if (progpathname == NULL)
360               {
361                 /* errno is set here.  */
362                 failure_errno = errno;
363                 free (dir_as_prefix_to_free);
364                 goto failed;
365               }
366
367             if (eaccess (progpathname, X_OK) == 0)
368               {
369                 struct stat statbuf;
370
371                 if (stat (progpathname, &statbuf) >= 0)
372                   {
373                     if (! S_ISDIR (statbuf.st_mode))
374                       errno = ENOEXEC;
375                     else
376                       errno = EACCES;
377                   }
378               }
379
380             failure_errno = errno;
381
382             free (progpathname);
383           }
384         #endif
385
386         free (dir_as_prefix_to_free);
387
388         if (last)
389           break;
390       }
391
392    failed:
393     /* Not found in PATH.  */
394     free (path_copy);
395
396     errno = failure_errno;
397     return NULL;
398   }
399 }