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