1 /* @(#)searchinpath.c 1.5 16/08/01 Copyright 1999-2016 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)searchinpath.c 1.5 16/08/01 Copyright 1999-2016 J. Schilling";
6 #endif
7 /*
8 * Search a file name in the PATH of the current exeecutable.
9 * Return the path to the file name in allocated space.
10 *
11 * Copyright (c) 1999-2016 J. Schilling
12 */
13 /*
14 * The contents of this file are subject to the terms of the
15 * Common Development and Distribution License, Version 1.0 only
16 * (the "License"). You may not use this file except in compliance
17 * with the License.
18 *
19 * See the file CDDL.Schily.txt in this distribution for details.
20 * A copy of the CDDL is also available via the Internet at
21 * http://www.opensource.org/licenses/cddl1.txt
22 *
23 * When distributing Covered Code, include this CDDL HEADER in each
24 * file and include the License file CDDL.Schily.txt from this distribution.
25 */
26
27 #include <schily/mconfig.h>
28 #include <schily/string.h>
29 #include <schily/unistd.h>
30 #include <schily/stdlib.h> /* getexecname() */
31 #include <schily/stat.h>
32 #include <schily/standard.h>
33 #include <schily/schily.h>
34 #include <schily/errno.h>
35
36
37 #define NAMEMAX 4096
38
39 EXPORT char *searchfileinpath __PR((char *name, int mode,
40 int file_mode,
41 char *path));
42 LOCAL char *searchonefile __PR((char *name, int mode,
43 BOOL plain_file,
44 char *xn,
45 char *nbuf,
46 char *np, char *ep));
47 #if defined(__DJGPP__)
48 LOCAL char *strbs2s __PR((char *s));
49 #endif
50
51 #ifdef JOS
52 #define enofile(t) ((t) == EMISSDIR || \
53 (t) == ENOFILE || \
54 (t) == EISADIR || \
55 (t) == EIOERR)
56 #else
57 #define enofile(t) ((t) == ENOENT || \
58 (t) == ENOTDIR || \
59 (t) == EISDIR || \
60 (t) == EIO)
61 #endif
62
63 /*
64 * Search for the "name" file in the PATH of the user.
65 * Assume that the file is ... bin/../name.
66 */
67 EXPORT char *
searchfileinpath(name,mode,file_mode,path)68 searchfileinpath(name, mode, file_mode, path)
69 char *name; /* Find <execname>/../name in PATH */
70 int mode; /* Mode for access() e.g. X_OK */
71 int file_mode; /* How to check files */
72 char *path; /* PATH to use if not NULL */
73 {
74 char pbuf[NAMEMAX];
75 char *nbuf = pbuf;
76 char *np;
77 char *ep;
78 char *xn;
79 int nlen = strlen(name);
80 int oerrno = geterrno();
81 int err = 0;
82 #ifdef HAVE_GETEXECNAME
83 char *pn = (char *)getexecname(); /* pn is on the stack */
84 #else
85 char *pn = getexecpath(); /* pn is from strdup() */
86 char ebuf[NAMEMAX];
87
88 if (pn) {
89 strlcpy(ebuf, pn, sizeof (ebuf));
90 free(pn);
91 pn = ebuf;
92 }
93 #endif
94
95 if (pn == NULL)
96 xn = get_progname();
97 else
98 xn = pn;
99 if ((np = strrchr(xn, '/')) != NULL)
100 xn = ++np;
101
102 /*
103 * getexecname() is the best choice for our seach. getexecname()
104 * returns either "foo" (if called from the current directory) or
105 * an absolute path after symlinks have been resolved.
106 * If getexecname() returns a path with slashes, try to search
107 * first relatively to the known location of the current binary.
108 */
109 if ((file_mode & SIP_ONLY_PATH) == 0 &&
110 pn != NULL && strchr(pn, '/') != NULL) {
111 strlcpy(nbuf, pn, sizeof (pbuf));
112 np = nbuf + strlen(nbuf);
113
114 while (np > nbuf && np[-1] != '/')
115 *--np = '\0';
116 pn = &nbuf[sizeof (pbuf) - 1];
117 if ((np = searchonefile(name, mode,
118 file_mode & (SIP_PLAIN_FILE|SIP_NO_STRIPBIN),
119 xn,
120 nbuf, np, pn)) != NULL) {
121 seterrno(oerrno);
122 return (np);
123 }
124 }
125
126 if (file_mode & SIP_NO_PATH)
127 return (NULL);
128
129 if (path == NULL)
130 path = getenv("PATH");
131 if (path == NULL)
132 return (NULL);
133
134
135 #ifdef __DJGPP__
136 path = strdup(path);
137 if (path == NULL)
138 return (NULL);
139 strbs2s(path); /* PATH under DJGPP can contain both slashes */
140 #endif
141
142 /*
143 * A PATH name search should lead us to the path under which we
144 * called the binary, but not necessarily to the install path as
145 * we may have been called thorugh a symlink or hardlink. In case
146 * of a symlink, we can follow the link. In case of a hardlink, we
147 * are lost.
148 */
149 ep = &nbuf[sizeof (pbuf) - 1];
150 for (;;) {
151 np = nbuf;
152 while (*path != PATH_ENV_DELIM && *path != '\0' &&
153 np < &nbuf[sizeof (pbuf) - nlen])
154 *np++ = *path++;
155 *np = '\0';
156 if ((np = searchonefile(name, mode,
157 file_mode & (SIP_PLAIN_FILE|SIP_NO_STRIPBIN),
158 xn,
159 nbuf, np, ep)) != NULL) {
160 #ifdef __DJGPP__
161 free(path);
162 #endif
163 seterrno(oerrno);
164 return (np);
165 }
166 if (err == 0) {
167 err = geterrno();
168 if (enofile(err))
169 err = 0;
170 }
171 if (*path == '\0')
172 break;
173 path++;
174 }
175 #ifdef __DJGPP__
176 free(path);
177 #endif
178 if (err)
179 seterrno(err);
180 else
181 seterrno(oerrno);
182 return (NULL);
183 }
184
185 LOCAL char *
searchonefile(name,mode,plain_file,xn,nbuf,np,ep)186 searchonefile(name, mode, plain_file, xn, nbuf, np, ep)
187 register char *name; /* Find <execname>/../name in PATH */
188 int mode; /* Mode for access() e.g. X_OK */
189 BOOL plain_file; /* Whether to check only plain files */
190 char *xn; /* The basename of the executable */
191 register char *nbuf; /* Name buffer base */
192 register char *np; /* Where to append name to path */
193 register char *ep; /* Point to last valid char in nbuf */
194 {
195 struct stat sb;
196
197 while (np > nbuf && np[-1] == '/')
198 *--np = '\0';
199 if (xn) {
200 *np++ = '/';
201 strlcpy(np, xn, ep - np);
202 if (stat(nbuf, &sb) < 0)
203 return (NULL);
204 if (!S_ISREG(sb.st_mode))
205 return (NULL);
206 *--np = '\0';
207 }
208 if ((plain_file & SIP_NO_STRIPBIN) == 0) {
209 if (np >= &nbuf[4] && streql(&np[-4], "/bin"))
210 np = &np[-4];
211 }
212 plain_file &= SIP_PLAIN_FILE;
213 *np++ = '/';
214 *np = '\0';
215 strlcpy(np, name, ep - np);
216
217 seterrno(0);
218 if (stat(nbuf, &sb) >= 0) {
219 if ((!plain_file || S_ISREG(sb.st_mode)) &&
220 (eaccess(nbuf, mode) >= 0)) {
221 return (strdup(nbuf));
222 }
223 if (geterrno() == 0)
224 seterrno(EACCES);
225 }
226 return (NULL);
227 }
228
229 #ifdef __DJGPP__
230 LOCAL char *
strbs2s(s)231 strbs2s(s)
232 char *s;
233 {
234 char *tmp = s;
235
236 if (tmp) {
237 while (*tmp) {
238 if (*tmp == '\\')
239 *tmp = '/';
240 tmp++;
241 }
242 }
243 return (s);
244 }
245 #endif
246