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