Ruby 1.9.3p327(2012-11-10revision37606)
dln_find.c
Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   dln_find.c -
00004 
00005   $Author: nobu $
00006   created at: Tue Jan 18 17:05:06 JST 1994
00007 
00008   Copyright (C) 1993-2007 Yukihiro Matsumoto
00009 
00010 **********************************************************************/
00011 
00012 #ifdef RUBY_EXPORT
00013 #include "ruby/ruby.h"
00014 #define dln_notimplement rb_notimplement
00015 #define dln_memerror rb_memerror
00016 #define dln_exit rb_exit
00017 #define dln_loaderror rb_loaderror
00018 #define dln_warning rb_warning
00019 #define dln_warning_arg
00020 #else
00021 #define dln_notimplement --->>> dln not implemented <<<---
00022 #define dln_memerror abort
00023 #define dln_exit exit
00024 #define dln_warning fprintf
00025 #define dln_warning_arg stderr,
00026 static void dln_loaderror(const char *format, ...);
00027 #endif
00028 #include "dln.h"
00029 
00030 #ifdef HAVE_STDLIB_H
00031 # include <stdlib.h>
00032 #endif
00033 
00034 #ifdef USE_DLN_A_OUT
00035 char *dln_argv0;
00036 #endif
00037 
00038 #if defined(HAVE_ALLOCA_H)
00039 #include <alloca.h>
00040 #endif
00041 
00042 #ifdef HAVE_STRING_H
00043 # include <string.h>
00044 #else
00045 # include <strings.h>
00046 #endif
00047 
00048 #ifndef xmalloc
00049 void *xmalloc();
00050 void *xcalloc();
00051 void *xrealloc();
00052 #endif
00053 
00054 #define free(x) xfree(x)
00055 
00056 #include <stdio.h>
00057 #if defined(_WIN32)
00058 #include "missing/file.h"
00059 #endif
00060 #include <sys/types.h>
00061 #include <sys/stat.h>
00062 
00063 #ifndef S_ISDIR
00064 #   define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
00065 #endif
00066 
00067 #ifdef HAVE_SYS_PARAM_H
00068 # include <sys/param.h>
00069 #endif
00070 #ifndef MAXPATHLEN
00071 # define MAXPATHLEN 1024
00072 #endif
00073 
00074 #ifdef HAVE_UNISTD_H
00075 # include <unistd.h>
00076 #endif
00077 
00078 #ifndef _WIN32
00079 char *getenv();
00080 #endif
00081 
00082 static char *dln_find_1(const char *fname, const char *path, char *buf, size_t size, int exe_flag);
00083 
00084 char *
00085 dln_find_exe_r(const char *fname, const char *path, char *buf, size_t size)
00086 {
00087     char *envpath = 0;
00088 
00089     if (!path) {
00090         path = getenv(PATH_ENV);
00091         if (path) path = envpath = strdup(path);
00092     }
00093 
00094     if (!path) {
00095 #if defined(_WIN32)
00096         path = "/usr/local/bin;/usr/ucb;/usr/bin;/bin;.";
00097 #else
00098         path = "/usr/local/bin:/usr/ucb:/usr/bin:/bin:.";
00099 #endif
00100     }
00101     buf = dln_find_1(fname, path, buf, size, 1);
00102     if (envpath) free(envpath);
00103     return buf;
00104 }
00105 
00106 char *
00107 dln_find_file_r(const char *fname, const char *path, char *buf, size_t size)
00108 {
00109     if (!path) path = ".";
00110     return dln_find_1(fname, path, buf, size, 0);
00111 }
00112 
00113 static char fbuf[MAXPATHLEN];
00114 
00115 char *
00116 dln_find_exe(const char *fname, const char *path)
00117 {
00118     return dln_find_exe_r(fname, path, fbuf, sizeof(fbuf));
00119 }
00120 
00121 char *
00122 dln_find_file(const char *fname, const char *path)
00123 {
00124     return dln_find_file_r(fname, path, fbuf, sizeof(fbuf));
00125 }
00126 
00127 static char *
00128 dln_find_1(const char *fname, const char *path, char *fbuf, size_t size,
00129            int exe_flag /* non 0 if looking for executable. */)
00130 {
00131     register const char *dp;
00132     register const char *ep;
00133     register char *bp;
00134     struct stat st;
00135     size_t i, fnlen, fspace;
00136 #ifdef DOSISH
00137     static const char extension[][5] = {
00138         EXECUTABLE_EXTS,
00139     };
00140     size_t j;
00141     int is_abs = 0, has_path = 0;
00142     const char *ext = 0;
00143 #endif
00144     const char *p = fname;
00145 
00146     static const char pathname_too_long[] = "openpath: pathname too long (ignored)\n\
00147 \tDirectory \"%.*s\"%s\n\tFile \"%.*s\"%s\n";
00148 #define PATHNAME_TOO_LONG() dln_warning(dln_warning_arg pathname_too_long, \
00149                                         ((bp - fbuf) > 100 ? 100 : (int)(bp - fbuf)), fbuf, \
00150                                         ((bp - fbuf) > 100 ? "..." : ""), \
00151                                         (fnlen > 100 ? 100 : (int)fnlen), fname, \
00152                                         (fnlen > 100 ? "..." : ""))
00153 
00154 #define RETURN_IF(expr) if (expr) return (char *)fname;
00155 
00156     RETURN_IF(!fname);
00157     fnlen = strlen(fname);
00158     if (fnlen >= size) {
00159         dln_warning(dln_warning_arg
00160                     "openpath: pathname too long (ignored)\n\tFile \"%.*s\"%s\n",
00161                     (fnlen > 100 ? 100 : (int)fnlen), fname,
00162                     (fnlen > 100 ? "..." : ""));
00163         return NULL;
00164     }
00165 #ifdef DOSISH
00166 # ifndef CharNext
00167 # define CharNext(p) ((p)+1)
00168 # endif
00169 # ifdef DOSISH_DRIVE_LETTER
00170     if (((p[0] | 0x20) - 'a') < 26  && p[1] == ':') {
00171         p += 2;
00172         is_abs = 1;
00173     }
00174 # endif
00175     switch (*p) {
00176       case '/': case '\\':
00177         is_abs = 1;
00178         p++;
00179     }
00180     has_path = is_abs;
00181     while (*p) {
00182         switch (*p) {
00183           case '/': case '\\':
00184             has_path = 1;
00185             ext = 0;
00186             p++;
00187             break;
00188           case '.':
00189             ext = p;
00190             p++;
00191             break;
00192           default:
00193             p = CharNext(p);
00194         }
00195     }
00196     if (ext) {
00197         for (j = 0; STRCASECMP(ext, extension[j]); ) {
00198             if (++j == sizeof(extension) / sizeof(extension[0])) {
00199                 ext = 0;
00200                 break;
00201             }
00202         }
00203     }
00204     ep = bp = 0;
00205     if (!exe_flag) {
00206         RETURN_IF(is_abs);
00207     }
00208     else if (has_path) {
00209         RETURN_IF(ext);
00210         i = p - fname;
00211         if (i + 1 > size) goto toolong;
00212         fspace = size - i - 1;
00213         bp = fbuf;
00214         ep = p;
00215         memcpy(fbuf, fname, i + 1);
00216         goto needs_extension;
00217     }
00218     p = fname;
00219 #endif
00220 
00221     if (*p == '.' && *++p == '.') ++p;
00222     RETURN_IF(*p == '/');
00223     RETURN_IF(exe_flag && strchr(fname, '/'));
00224 
00225 #undef RETURN_IF
00226 
00227     for (dp = path;; dp = ++ep) {
00228         register size_t l;
00229 
00230         /* extract a component */
00231         ep = strchr(dp, PATH_SEP[0]);
00232         if (ep == NULL)
00233             ep = dp+strlen(dp);
00234 
00235         /* find the length of that component */
00236         l = ep - dp;
00237         bp = fbuf;
00238         fspace = size - 2;
00239         if (l > 0) {
00240             /*
00241             **  If the length of the component is zero length,
00242             **  start from the current directory.  If the
00243             **  component begins with "~", start from the
00244             **  user's $HOME environment variable.  Otherwise
00245             **  take the path literally.
00246             */
00247 
00248             if (*dp == '~' && (l == 1 ||
00249 #if defined(DOSISH)
00250                                dp[1] == '\\' ||
00251 #endif
00252                                dp[1] == '/')) {
00253                 char *home;
00254 
00255                 home = getenv("HOME");
00256                 if (home != NULL) {
00257                     i = strlen(home);
00258                     if (fspace < i)
00259                         goto toolong;
00260                     fspace -= i;
00261                     memcpy(bp, home, i);
00262                     bp += i;
00263                 }
00264                 dp++;
00265                 l--;
00266             }
00267             if (l > 0) {
00268                 if (fspace < l)
00269                     goto toolong;
00270                 fspace -= l;
00271                 memcpy(bp, dp, l);
00272                 bp += l;
00273             }
00274 
00275             /* add a "/" between directory and filename */
00276             if (ep[-1] != '/')
00277                 *bp++ = '/';
00278         }
00279 
00280         /* now append the file name */
00281         i = fnlen;
00282         if (fspace < i) {
00283           toolong:
00284             PATHNAME_TOO_LONG();
00285             goto next;
00286         }
00287         fspace -= i;
00288         memcpy(bp, fname, i + 1);
00289 
00290 #if defined(DOSISH)
00291         if (exe_flag && !ext) {
00292           needs_extension:
00293             for (j = 0; j < sizeof(extension) / sizeof(extension[0]); j++) {
00294                 if (fspace < strlen(extension[j])) {
00295                     PATHNAME_TOO_LONG();
00296                     continue;
00297                 }
00298                 strlcpy(bp + i, extension[j], fspace);
00299                 if (stat(fbuf, &st) == 0)
00300                     return fbuf;
00301             }
00302             goto next;
00303         }
00304 #endif /* _WIN32 or __EMX__ */
00305 
00306         if (stat(fbuf, &st) == 0) {
00307             if (exe_flag == 0) return fbuf;
00308             /* looking for executable */
00309             if (!S_ISDIR(st.st_mode) && eaccess(fbuf, X_OK) == 0)
00310                 return fbuf;
00311         }
00312       next:
00313         /* if not, and no other alternatives, life is bleak */
00314         if (*ep == '\0') {
00315             return NULL;
00316         }
00317 
00318         /* otherwise try the next component in the search path */
00319     }
00320 }
00321