зеркало из https://github.com/github/ruby.git
298 строки
6.1 KiB
C
298 строки
6.1 KiB
C
/**********************************************************************
|
|
|
|
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_SYS_PARAM_H
|
|
# include <sys/param.h>
|
|
#endif
|
|
#ifndef MAXPATHLEN
|
|
# define MAXPATHLEN 1024
|
|
#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) {
|
|
toolong:
|
|
PATHNAME_TOO_LONG();
|
|
goto next;
|
|
}
|
|
fspace -= i;
|
|
memcpy(bp, fname, i + 1);
|
|
|
|
#if defined(DOSISH)
|
|
if (exe_flag && !ext) {
|
|
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 /* _WIN32 or __EMX__ */
|
|
|
|
#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;
|
|
}
|
|
|
|
/* otherwise try the next component in the search path */
|
|
}
|
|
}
|