Ruby 3.3.7p123 (2025-01-15 revision be31f993d7fa0219d85f7b3c694d454da4ecc10b)
dln_find.c
1/**********************************************************************
2
3 dln_find.c -
4
5 $Author$
6 created at: Tue Jan 18 17:05:06 JST 1994
7
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
9
10**********************************************************************/
11
12#ifdef RUBY_EXPORT
13#include "ruby/ruby.h"
14#define dln_warning rb_warning
15#define dln_warning_arg
16#else
17#define dln_warning fprintf
18#define dln_warning_arg stderr,
19#endif
20#include "dln.h"
21
22#ifdef HAVE_STDLIB_H
23# include <stdlib.h>
24#endif
25
26#if defined(HAVE_ALLOCA_H)
27#include <alloca.h>
28#endif
29
30#ifdef HAVE_STRING_H
31# include <string.h>
32#else
33# include <strings.h>
34#endif
35
36#include <stdio.h>
37#if defined(_WIN32)
38#include "missing/file.h"
39#endif
40#include <sys/types.h>
41#include <sys/stat.h>
42
43#ifndef S_ISDIR
44# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
45#endif
46
47#ifdef HAVE_UNISTD_H
48# include <unistd.h>
49#endif
50
51#if !defined(_WIN32) && !HAVE_DECL_GETENV
52char *getenv();
53#endif
54
55static char *dln_find_1(const char *fname, const char *path, char *buf, size_t size, int exe_flag
56 DLN_FIND_EXTRA_ARG_DECL);
57
58char *
59dln_find_exe_r(const char *fname, const char *path, char *buf, size_t size
60 DLN_FIND_EXTRA_ARG_DECL)
61{
62 char *envpath = 0;
63
64 if (!path) {
65 path = getenv(PATH_ENV);
66 if (path) path = envpath = strdup(path);
67 }
68
69 if (!path) {
70 path =
71 "/usr/local/bin" PATH_SEP
72 "/usr/ucb" PATH_SEP
73 "/usr/bin" PATH_SEP
74 "/bin" PATH_SEP
75 ".";
76 }
77 buf = dln_find_1(fname, path, buf, size, 1 DLN_FIND_EXTRA_ARG);
78 free(envpath);
79 return buf;
80}
81
82char *
83dln_find_file_r(const char *fname, const char *path, char *buf, size_t size
84 DLN_FIND_EXTRA_ARG_DECL)
85{
86 if (!path) path = ".";
87 return dln_find_1(fname, path, buf, size, 0 DLN_FIND_EXTRA_ARG);
88}
89
90static char *
91dln_find_1(const char *fname, const char *path, char *fbuf, size_t size,
92 int exe_flag /* non 0 if looking for executable. */
93 DLN_FIND_EXTRA_ARG_DECL)
94{
95 register const char *dp;
96 register const char *ep;
97 register char *bp;
98 struct stat st;
99 size_t i, fnlen, fspace;
100#ifdef DOSISH
101 static const char extension[][5] = {
102 EXECUTABLE_EXTS,
103 };
104 size_t j;
105 int is_abs = 0, has_path = 0;
106 const char *ext = 0;
107#endif
108 const char *p = fname;
109
110 static const char pathname_too_long[] = "openpath: pathname too long (ignored)\n\
111\tDirectory \"%.*s\"%s\n\tFile \"%.*s\"%s\n";
112#define PATHNAME_TOO_LONG() dln_warning(dln_warning_arg pathname_too_long, \
113 ((bp - fbuf) > 100 ? 100 : (int)(bp - fbuf)), fbuf, \
114 ((bp - fbuf) > 100 ? "..." : ""), \
115 (fnlen > 100 ? 100 : (int)fnlen), fname, \
116 (fnlen > 100 ? "..." : ""))
117
118#define RETURN_IF(expr) if (expr) return (char *)fname;
119
120 RETURN_IF(!fname);
121 fnlen = strlen(fname);
122 if (fnlen >= size) {
123 dln_warning(dln_warning_arg
124 "openpath: pathname too long (ignored)\n\tFile \"%.*s\"%s\n",
125 (fnlen > 100 ? 100 : (int)fnlen), fname,
126 (fnlen > 100 ? "..." : ""));
127 return NULL;
128 }
129#ifdef DOSISH
130# ifndef CharNext
131# define CharNext(p) ((p)+1)
132# endif
133# ifdef DOSISH_DRIVE_LETTER
134 if (((p[0] | 0x20) - 'a') < 26 && p[1] == ':') {
135 p += 2;
136 is_abs = 1;
137 }
138# endif
139 switch (*p) {
140 case '/': case '\\':
141 is_abs = 1;
142 p++;
143 }
144 has_path = is_abs;
145 while (*p) {
146 switch (*p) {
147 case '/': case '\\':
148 has_path = 1;
149 ext = 0;
150 p++;
151 break;
152 case '.':
153 ext = p;
154 p++;
155 break;
156 default:
157 p = CharNext(p);
158 }
159 }
160 if (ext) {
161 for (j = 0; STRCASECMP(ext, extension[j]); ) {
162 if (++j == sizeof(extension) / sizeof(extension[0])) {
163 ext = 0;
164 break;
165 }
166 }
167 }
168 ep = bp = 0;
169 if (!exe_flag) {
170 RETURN_IF(is_abs);
171 }
172 else if (has_path) {
173 RETURN_IF(ext);
174 i = p - fname;
175 if (i + 1 > size) goto toolong;
176 fspace = size - i - 1;
177 bp = fbuf;
178 ep = p;
179 memcpy(fbuf, fname, i + 1);
180 goto needs_extension;
181 }
182 p = fname;
183#endif
184
185 if (*p == '.' && *++p == '.') ++p;
186 RETURN_IF(*p == '/');
187 RETURN_IF(exe_flag && strchr(fname, '/'));
188
189#undef RETURN_IF
190
191 for (dp = path;; dp = ++ep) {
192 register size_t l;
193
194 /* extract a component */
195 ep = strchr(dp, PATH_SEP[0]);
196 if (ep == NULL)
197 ep = dp+strlen(dp);
198
199 /* find the length of that component */
200 l = ep - dp;
201 bp = fbuf;
202 fspace = size - 2;
203 if (l > 0) {
204 /*
205 ** If the length of the component is zero length,
206 ** start from the current directory. If the
207 ** component begins with "~", start from the
208 ** user's $HOME environment variable. Otherwise
209 ** take the path literally.
210 */
211
212 if (*dp == '~' && (l == 1 ||
213#if defined(DOSISH)
214 dp[1] == '\\' ||
215#endif
216 dp[1] == '/')) {
217 const char *home;
218
219 home = getenv("HOME");
220 if (home != NULL) {
221 i = strlen(home);
222 if (fspace < i)
223 goto toolong;
224 fspace -= i;
225 memcpy(bp, home, i);
226 bp += i;
227 }
228 dp++;
229 l--;
230 }
231 if (l > 0) {
232 if (fspace < l)
233 goto toolong;
234 fspace -= l;
235 memcpy(bp, dp, l);
236 bp += l;
237 }
238
239 /* add a "/" between directory and filename */
240 if (ep[-1] != '/')
241 *bp++ = '/';
242 }
243
244 /* now append the file name */
245 i = fnlen;
246 if (fspace < i) {
247 goto toolong;
248 }
249 fspace -= i;
250 memcpy(bp, fname, i + 1);
251
252#if defined(DOSISH)
253 if (exe_flag && !ext) {
254 goto needs_extension;
255 }
256#endif
257
258#ifndef S_ISREG
259# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
260#endif
261 if (stat(fbuf, &st) == 0 && S_ISREG(st.st_mode)) {
262 if (exe_flag == 0) return fbuf;
263 /* looking for executable */
264 if (eaccess(fbuf, X_OK) == 0) return fbuf;
265 }
266 next:
267 /* if not, and no other alternatives, life is bleak */
268 if (*ep == '\0') {
269 return NULL;
270 }
271 continue;
272
273 toolong:
274 PATHNAME_TOO_LONG();
275 goto next;
276
277#if defined(DOSISH)
278 needs_extension:
279 for (j = 0; j < sizeof(extension) / sizeof(extension[0]); j++) {
280 if (fspace < strlen(extension[j])) {
281 PATHNAME_TOO_LONG();
282 continue;
283 }
284 strlcpy(bp + i, extension[j], fspace);
285 if (stat(fbuf, &st) == 0)
286 return fbuf;
287 }
288 goto next;
289#endif
290 /* otherwise try the next component in the search path */
291 }
292}
#define PATH_ENV
Definition dosish.h:63
#define PATH_SEP
The delimiter of PATH environment variable.
Definition dosish.h:45
#define STRCASECMP
Old name of st_locale_insensitive_strcasecmp.
Definition ctype.h:102
#define strdup(s)
Just another name of ruby_strdup.
Definition util.h:187