exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / findprog.c
blob3b0853f92828dac293817edd7631b88d1484923f
1 /* Locating a program in PATH.
2 Copyright (C) 2001-2004, 2006-2024 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of the
8 License, or (at your option) any later version.
10 This file 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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 <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #if !(defined _WIN32 || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__)
28 # include <sys/stat.h>
29 #endif
31 /* Avoid collision between findprog.c and findprog-lgpl.c. */
32 #if IN_FINDPROG_LGPL || ! GNULIB_FINDPROG_LGPL
34 #if !IN_FINDPROG_LGPL
35 # include "xalloc.h"
36 #endif
37 #include "concat-filename.h"
40 const char *
41 find_in_path (const char *progname)
43 #if defined _WIN32 || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
44 /* Native Windows, Cygwin, OS/2, DOS */
45 /* The searching rules with .COM, .EXE, .BAT, .CMD etc. suffixes are
46 too complicated. Leave it to the OS. */
47 return progname;
48 #else
49 /* Unix */
50 char *path;
51 char *path_rest;
52 char *cp;
54 if (strchr (progname, '/') != NULL)
55 /* If progname contains a slash, it is either absolute or relative to
56 the current directory. PATH is not used. */
57 return progname;
59 path = getenv ("PATH");
60 if (path == NULL || *path == '\0')
61 /* If PATH is not set, the default search path is implementation
62 dependent. */
63 return progname;
65 /* Make a copy, to prepare for destructive modifications. */
66 # if !IN_FINDPROG_LGPL
67 path = xstrdup (path);
68 # else
69 path = strdup (path);
70 if (path == NULL)
71 /* Out of memory. */
72 return progname;
73 # endif
74 for (path_rest = path; ; path_rest = cp + 1)
76 const char *dir;
77 bool last;
78 char *progpathname;
80 /* Extract next directory in PATH. */
81 dir = path_rest;
82 for (cp = path_rest; *cp != '\0' && *cp != ':'; cp++)
84 last = (*cp == '\0');
85 *cp = '\0';
87 /* Empty PATH components designate the current directory. */
88 if (dir == cp)
89 dir = ".";
91 /* Concatenate dir and progname. */
92 # if !IN_FINDPROG_LGPL
93 progpathname = xconcatenated_filename (dir, progname, NULL);
94 # else
95 progpathname = concatenated_filename (dir, progname, NULL);
96 if (progpathname == NULL)
98 /* Out of memory. */
99 free (path);
100 return progname;
102 # endif
104 /* On systems which have the eaccess() system call, let's use it.
105 On other systems, let's hope that this program is not installed
106 setuid or setgid, so that it is ok to call access() despite its
107 design flaw. */
108 if (eaccess (progpathname, X_OK) == 0)
110 /* Check that the progpathname does not point to a directory. */
111 struct stat statbuf;
113 if (stat (progpathname, &statbuf) >= 0
114 && ! S_ISDIR (statbuf.st_mode))
116 /* Found! */
117 if (strcmp (progpathname, progname) == 0)
119 free (progpathname);
121 /* Add the "./" prefix for real, that xconcatenated_filename()
122 optimized away. This avoids a second PATH search when the
123 caller uses execlp/execvp. */
124 # if !IN_FINDPROG_LGPL
125 progpathname = XNMALLOC (2 + strlen (progname) + 1, char);
126 # else
127 progpathname = (char *) malloc (2 + strlen (progname) + 1);
128 if (progpathname == NULL)
130 /* Out of memory. */
131 free (path);
132 return progname;
134 # endif
135 progpathname[0] = '.';
136 progpathname[1] = '/';
137 memcpy (progpathname + 2, progname, strlen (progname) + 1);
140 free (path);
141 return progpathname;
145 free (progpathname);
147 if (last)
148 break;
151 /* Not found in PATH. An error will be signalled at the first call. */
152 free (path);
153 return progname;
154 #endif
157 #endif