Ruby  3.1.4p223 (2023-03-30 revision HEAD)
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
52 char *getenv();
53 #endif
54 
55 static 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 
58 char *
59 dln_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  if (envpath) free(envpath);
79  return buf;
80 }
81 
82 char *
83 dln_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 
90 static char *
91 dln_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:176