Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upGitHub is where the world builds software
Millions of developers and companies build, ship, and maintain their software on GitHub — the largest and most advanced development platform in the world.
/********************************************************************** | |
dln_find.c - | |
$Author$ | |
created at: Tue Jan 18 17:05:06 JST 1994 | |
Copyright (C) 1993-2007 Yukihiro Matsumoto | |
**********************************************************************/ | |
#ifdef RUBY_EXPORT | |
#include "ruby/ruby.h" | |
#define dln_warning rb_warning | |
#define dln_warning_arg | |
#else | |
#define dln_warning fprintf | |
#define dln_warning_arg stderr, | |
#endif | |
#include "dln.h" | |
#ifdef HAVE_STDLIB_H | |
# include <stdlib.h> | |
#endif | |
#ifdef USE_DLN_A_OUT | |
char *dln_argv0; | |
#endif | |
#if defined(HAVE_ALLOCA_H) | |
#include <alloca.h> | |
#endif | |
#ifdef HAVE_STRING_H | |
# include <string.h> | |
#else | |
# include <strings.h> | |
#endif | |
#include <stdio.h> | |
#if defined(_WIN32) | |
#include "missing/file.h" | |
#endif | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#ifndef S_ISDIR | |
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) | |
#endif | |
#ifdef HAVE_UNISTD_H | |
# include <unistd.h> | |
#endif | |
#if !defined(_WIN32) && !HAVE_DECL_GETENV | |
char *getenv(); | |
#endif | |
static char *dln_find_1(const char *fname, const char *path, char *buf, size_t size, int exe_flag | |
DLN_FIND_EXTRA_ARG_DECL); | |
char * | |
dln_find_exe_r(const char *fname, const char *path, char *buf, size_t size | |
DLN_FIND_EXTRA_ARG_DECL) | |
{ | |
char *envpath = 0; | |
if (!path) { | |
path = getenv(PATH_ENV); | |
if (path) path = envpath = strdup(path); | |
} | |
if (!path) { | |
path = | |
"/usr/local/bin" PATH_SEP | |
"/usr/ucb" PATH_SEP | |
"/usr/bin" PATH_SEP | |
"/bin" PATH_SEP | |
"."; | |
} | |
buf = dln_find_1(fname, path, buf, size, 1 DLN_FIND_EXTRA_ARG); | |
if (envpath) free(envpath); | |
return buf; | |
} | |
char * | |
dln_find_file_r(const char *fname, const char *path, char *buf, size_t size | |
DLN_FIND_EXTRA_ARG_DECL) | |
{ | |
if (!path) path = "."; | |
return dln_find_1(fname, path, buf, size, 0 DLN_FIND_EXTRA_ARG); | |
} | |
static char * | |
dln_find_1(const char *fname, const char *path, char *fbuf, size_t size, | |
int exe_flag /* non 0 if looking for executable. */ | |
DLN_FIND_EXTRA_ARG_DECL) | |
{ | |
register const char *dp; | |
register const char *ep; | |
register char *bp; | |
struct stat st; | |
size_t i, fnlen, fspace; | |
#ifdef DOSISH | |
static const char extension[][5] = { | |
EXECUTABLE_EXTS, | |
}; | |
size_t j; | |
int is_abs = 0, has_path = 0; | |
const char *ext = 0; | |
#endif | |
const char *p = fname; | |
static const char pathname_too_long[] = "openpath: pathname too long (ignored)\n\ | |
\tDirectory \"%.*s\"%s\n\tFile \"%.*s\"%s\n"; | |
#define PATHNAME_TOO_LONG() dln_warning(dln_warning_arg pathname_too_long, \ | |
((bp - fbuf) > 100 ? 100 : (int)(bp - fbuf)), fbuf, \ | |
((bp - fbuf) > 100 ? "..." : ""), \ | |
(fnlen > 100 ? 100 : (int)fnlen), fname, \ | |
(fnlen > 100 ? "..." : "")) | |
#define RETURN_IF(expr) if (expr) return (char *)fname; | |
RETURN_IF(!fname); | |
fnlen = strlen(fname); | |
if (fnlen >= size) { | |
dln_warning(dln_warning_arg | |
"openpath: pathname too long (ignored)\n\tFile \"%.*s\"%s\n", | |
(fnlen > 100 ? 100 : (int)fnlen), fname, | |
(fnlen > 100 ? "..." : "")); | |
return NULL; | |
} | |
#ifdef DOSISH | |
# ifndef CharNext | |
# define CharNext(p) ((p)+1) | |
# endif | |
# ifdef DOSISH_DRIVE_LETTER | |
if (((p[0] | 0x20) - 'a') < 26 && p[1] == ':') { | |
p += 2; | |
is_abs = 1; | |
} | |
# endif | |
switch (*p) { | |
case '/': case '\\': | |
is_abs = 1; | |
p++; | |
} | |
has_path = is_abs; | |
while (*p) { | |
switch (*p) { | |
case '/': case '\\': | |
has_path = 1; | |
ext = 0; | |
p++; | |
break; | |
case '.': | |
ext = p; | |
p++; | |
break; | |
default: | |
p = CharNext(p); | |
} | |
} | |
if (ext) { | |
for (j = 0; STRCASECMP(ext, extension[j]); ) { | |
if (++j == sizeof(extension) / sizeof(extension[0])) { | |
ext = 0; | |
break; | |
} | |
} | |
} | |
ep = bp = 0; | |
if (!exe_flag) { | |
RETURN_IF(is_abs); | |
} | |
else if (has_path) { | |
RETURN_IF(ext); | |
i = p - fname; | |
if (i + 1 > size) goto toolong; | |
fspace = size - i - 1; | |
bp = fbuf; | |
ep = p; | |
memcpy(fbuf, fname, i + 1); | |
goto needs_extension; | |
} | |
p = fname; | |
#endif | |
if (*p == '.' && *++p == '.') ++p; | |
RETURN_IF(*p == '/'); | |
RETURN_IF(exe_flag && strchr(fname, '/')); | |
#undef RETURN_IF | |
for (dp = path;; dp = ++ep) { | |
register size_t l; | |
/* extract a component */ | |
ep = strchr(dp, PATH_SEP[0]); | |
if (ep == NULL) | |
ep = dp+strlen(dp); | |
/* find the length of that component */ | |
l = ep - dp; | |
bp = fbuf; | |
fspace = size - 2; | |
if (l > 0) { | |
/* | |
** If the length of the component is zero length, | |
** start from the current directory. If the | |
** component begins with "~", start from the | |
** user's $HOME environment variable. Otherwise | |
** take the path literally. | |
*/ | |
if (*dp == '~' && (l == 1 || | |
#if defined(DOSISH) | |
dp[1] == '\\' || | |
#endif | |
dp[1] == '/')) { | |
char *home; | |
home = getenv("HOME"); | |
if (home != NULL) { | |
i = strlen(home); | |
if (fspace < i) | |
goto toolong; | |
fspace -= i; | |
memcpy(bp, home, i); | |
bp += i; | |
} | |
dp++; | |
l--; | |
} | |
if (l > 0) { | |
if (fspace < l) | |
goto toolong; | |
fspace -= l; | |
memcpy(bp, dp, l); | |
bp += l; | |
} | |
/* add a "/" between directory and filename */ | |
if (ep[-1] != '/') | |
*bp++ = '/'; | |
} | |
/* now append the file name */ | |
i = fnlen; | |
if (fspace < i) { | |
goto toolong; | |
} | |
fspace -= i; | |
memcpy(bp, fname, i + 1); | |
#if defined(DOSISH) | |
if (exe_flag && !ext) { | |
goto needs_extension; | |
} | |
#endif | |
#ifndef S_ISREG | |
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) | |
#endif | |
if (stat(fbuf, &st) == 0 && S_ISREG(st.st_mode)) { | |
if (exe_flag == 0) return fbuf; | |
/* looking for executable */ | |
if (eaccess(fbuf, X_OK) == 0) return fbuf; | |
} | |
next: | |
/* if not, and no other alternatives, life is bleak */ | |
if (*ep == '\0') { | |
return NULL; | |
} | |
continue; | |
toolong: | |
PATHNAME_TOO_LONG(); | |
goto next; | |
#if defined(DOSISH) | |
needs_extension: | |
for (j = 0; j < sizeof(extension) / sizeof(extension[0]); j++) { | |
if (fspace < strlen(extension[j])) { | |
PATHNAME_TOO_LONG(); | |
continue; | |
} | |
strlcpy(bp + i, extension[j], fspace); | |
if (stat(fbuf, &st) == 0) | |
return fbuf; | |
} | |
goto next; | |
#endif | |
/* otherwise try the next component in the search path */ | |
} | |
} |