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