Bump to m4 1.4.19
[platform/upstream/m4.git] / tests / findprog.c
1 /* Locating a program in PATH.
2    Copyright (C) 2001-2004, 2006-2021 Free Software Foundation, Inc.
3    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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 <stdbool.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #if !(defined _WIN32 || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__)
29 # include <sys/stat.h>
30 #endif
31
32 /* Avoid collision between findprog.c and findprog-lgpl.c.  */
33 #if IN_FINDPROG_LGPL || ! GNULIB_FINDPROG_LGPL
34
35 #if !IN_FINDPROG_LGPL
36 # include "xalloc.h"
37 #endif
38 #include "concat-filename.h"
39
40
41 const char *
42 find_in_path (const char *progname)
43 {
44 #if defined _WIN32 || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
45   /* Native Windows, Cygwin, OS/2, DOS */
46   /* The searching rules with .COM, .EXE, .BAT, .CMD etc. suffixes are
47      too complicated.  Leave it to the OS.  */
48   return progname;
49 #else
50   /* Unix */
51   char *path;
52   char *path_rest;
53   char *cp;
54
55   if (strchr (progname, '/') != NULL)
56     /* If progname contains a slash, it is either absolute or relative to
57        the current directory.  PATH is not used.  */
58     return progname;
59
60   path = getenv ("PATH");
61   if (path == NULL || *path == '\0')
62     /* If PATH is not set, the default search path is implementation
63        dependent.  */
64     return progname;
65
66   /* Make a copy, to prepare for destructive modifications.  */
67 # if !IN_FINDPROG_LGPL
68   path = xstrdup (path);
69 # else
70   path = strdup (path);
71   if (path == NULL)
72     /* Out of memory.  */
73     return progname;
74 # endif
75   for (path_rest = path; ; path_rest = cp + 1)
76     {
77       const char *dir;
78       bool last;
79       char *progpathname;
80
81       /* Extract next directory in PATH.  */
82       dir = path_rest;
83       for (cp = path_rest; *cp != '\0' && *cp != ':'; cp++)
84         ;
85       last = (*cp == '\0');
86       *cp = '\0';
87
88       /* Empty PATH components designate the current directory.  */
89       if (dir == cp)
90         dir = ".";
91
92       /* Concatenate dir and progname.  */
93 # if !IN_FINDPROG_LGPL
94       progpathname = xconcatenated_filename (dir, progname, NULL);
95 # else
96       progpathname = concatenated_filename (dir, progname, NULL);
97       if (progpathname == NULL)
98         {
99           /* Out of memory.  */
100           free (path);
101           return progname;
102         }
103 # endif
104
105       /* On systems which have the eaccess() system call, let's use it.
106          On other systems, let's hope that this program is not installed
107          setuid or setgid, so that it is ok to call access() despite its
108          design flaw.  */
109       if (eaccess (progpathname, X_OK) == 0)
110         {
111           /* Check that the progpathname does not point to a directory.  */
112           struct stat statbuf;
113
114           if (stat (progpathname, &statbuf) >= 0
115               && ! S_ISDIR (statbuf.st_mode))
116             {
117               /* Found!  */
118               if (strcmp (progpathname, progname) == 0)
119                 {
120                   free (progpathname);
121
122                   /* Add the "./" prefix for real, that xconcatenated_filename()
123                      optimized away.  This avoids a second PATH search when the
124                      caller uses execlp/execvp.  */
125 # if !IN_FINDPROG_LGPL
126                   progpathname = XNMALLOC (2 + strlen (progname) + 1, char);
127 # else
128                   progpathname = (char *) malloc (2 + strlen (progname) + 1);
129                   if (progpathname == NULL)
130                     {
131                       /* Out of memory.  */
132                       free (path);
133                       return progname;
134                     }
135 # endif
136                   progpathname[0] = '.';
137                   progpathname[1] = '/';
138                   memcpy (progpathname + 2, progname, strlen (progname) + 1);
139                 }
140
141               free (path);
142               return progpathname;
143             }
144         }
145
146       free (progpathname);
147
148       if (last)
149         break;
150     }
151
152   /* Not found in PATH.  An error will be signalled at the first call.  */
153   free (path);
154   return progname;
155 #endif
156 }
157
158 #endif