mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 20:02:55 +00:00
246 lines
5.7 KiB
C
246 lines
5.7 KiB
C
/* @(#)searchinpath.c 1.5 16/08/01 Copyright 1999-2016 J. Schilling */
|
|
#include <schily/mconfig.h>
|
|
#ifndef lint
|
|
static UConst char sccsid[] =
|
|
"@(#)searchinpath.c 1.5 16/08/01 Copyright 1999-2016 J. Schilling";
|
|
#endif
|
|
/*
|
|
* Search a file name in the PATH of the current exeecutable.
|
|
* Return the path to the file name in allocated space.
|
|
*
|
|
* Copyright (c) 1999-2016 J. Schilling
|
|
*/
|
|
/*
|
|
* The contents of this file are subject to the terms of the
|
|
* Common Development and Distribution License, Version 1.0 only
|
|
* (the "License"). You may not use this file except in compliance
|
|
* with the License.
|
|
*
|
|
* See the file CDDL.Schily.txt in this distribution for details.
|
|
* A copy of the CDDL is also available via the Internet at
|
|
* http://www.opensource.org/licenses/cddl1.txt
|
|
*
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
* file and include the License file CDDL.Schily.txt from this distribution.
|
|
*/
|
|
|
|
#include <schily/mconfig.h>
|
|
#include <schily/string.h>
|
|
#include <schily/unistd.h>
|
|
#include <schily/stdlib.h> /* getexecname() */
|
|
#include <schily/stat.h>
|
|
#include <schily/standard.h>
|
|
#include <schily/schily.h>
|
|
#include <schily/errno.h>
|
|
|
|
|
|
#define NAMEMAX 4096
|
|
|
|
EXPORT char *searchfileinpath __PR((char *name, int mode,
|
|
int file_mode,
|
|
char *path));
|
|
LOCAL char *searchonefile __PR((char *name, int mode,
|
|
BOOL plain_file,
|
|
char *xn,
|
|
char *nbuf,
|
|
char *np, char *ep));
|
|
#if defined(__DJGPP__)
|
|
LOCAL char *strbs2s __PR((char *s));
|
|
#endif
|
|
|
|
#ifdef JOS
|
|
#define enofile(t) ((t) == EMISSDIR || \
|
|
(t) == ENOFILE || \
|
|
(t) == EISADIR || \
|
|
(t) == EIOERR)
|
|
#else
|
|
#define enofile(t) ((t) == ENOENT || \
|
|
(t) == ENOTDIR || \
|
|
(t) == EISDIR || \
|
|
(t) == EIO)
|
|
#endif
|
|
|
|
/*
|
|
* Search for the "name" file in the PATH of the user.
|
|
* Assume that the file is ... bin/../name.
|
|
*/
|
|
EXPORT char *
|
|
searchfileinpath(name, mode, file_mode, path)
|
|
char *name; /* Find <execname>/../name in PATH */
|
|
int mode; /* Mode for access() e.g. X_OK */
|
|
int file_mode; /* How to check files */
|
|
char *path; /* PATH to use if not NULL */
|
|
{
|
|
char pbuf[NAMEMAX];
|
|
char *nbuf = pbuf;
|
|
char *np;
|
|
char *ep;
|
|
char *xn;
|
|
int nlen = strlen(name);
|
|
int oerrno = geterrno();
|
|
int err = 0;
|
|
#ifdef HAVE_GETEXECNAME
|
|
char *pn = (char *)getexecname(); /* pn is on the stack */
|
|
#else
|
|
char *pn = getexecpath(); /* pn is from strdup() */
|
|
char ebuf[NAMEMAX];
|
|
|
|
if (pn) {
|
|
strlcpy(ebuf, pn, sizeof (ebuf));
|
|
free(pn);
|
|
pn = ebuf;
|
|
}
|
|
#endif
|
|
|
|
if (pn == NULL)
|
|
xn = get_progname();
|
|
else
|
|
xn = pn;
|
|
if ((np = strrchr(xn, '/')) != NULL)
|
|
xn = ++np;
|
|
|
|
/*
|
|
* getexecname() is the best choice for our seach. getexecname()
|
|
* returns either "foo" (if called from the current directory) or
|
|
* an absolute path after symlinks have been resolved.
|
|
* If getexecname() returns a path with slashes, try to search
|
|
* first relatively to the known location of the current binary.
|
|
*/
|
|
if ((file_mode & SIP_ONLY_PATH) == 0 &&
|
|
pn != NULL && strchr(pn, '/') != NULL) {
|
|
strlcpy(nbuf, pn, sizeof (pbuf));
|
|
np = nbuf + strlen(nbuf);
|
|
|
|
while (np > nbuf && np[-1] != '/')
|
|
*--np = '\0';
|
|
pn = &nbuf[sizeof (pbuf) - 1];
|
|
if ((np = searchonefile(name, mode,
|
|
file_mode & (SIP_PLAIN_FILE|SIP_NO_STRIPBIN),
|
|
xn,
|
|
nbuf, np, pn)) != NULL) {
|
|
seterrno(oerrno);
|
|
return (np);
|
|
}
|
|
}
|
|
|
|
if (file_mode & SIP_NO_PATH)
|
|
return (NULL);
|
|
|
|
if (path == NULL)
|
|
path = getenv("PATH");
|
|
if (path == NULL)
|
|
return (NULL);
|
|
|
|
|
|
#ifdef __DJGPP__
|
|
path = strdup(path);
|
|
if (path == NULL)
|
|
return (NULL);
|
|
strbs2s(path); /* PATH under DJGPP can contain both slashes */
|
|
#endif
|
|
|
|
/*
|
|
* A PATH name search should lead us to the path under which we
|
|
* called the binary, but not necessarily to the install path as
|
|
* we may have been called thorugh a symlink or hardlink. In case
|
|
* of a symlink, we can follow the link. In case of a hardlink, we
|
|
* are lost.
|
|
*/
|
|
ep = &nbuf[sizeof (pbuf) - 1];
|
|
for (;;) {
|
|
np = nbuf;
|
|
while (*path != PATH_ENV_DELIM && *path != '\0' &&
|
|
np < &nbuf[sizeof (pbuf) - nlen])
|
|
*np++ = *path++;
|
|
*np = '\0';
|
|
if ((np = searchonefile(name, mode,
|
|
file_mode & (SIP_PLAIN_FILE|SIP_NO_STRIPBIN),
|
|
xn,
|
|
nbuf, np, ep)) != NULL) {
|
|
#ifdef __DJGPP__
|
|
free(path);
|
|
#endif
|
|
seterrno(oerrno);
|
|
return (np);
|
|
}
|
|
if (err == 0) {
|
|
err = geterrno();
|
|
if (enofile(err))
|
|
err = 0;
|
|
}
|
|
if (*path == '\0')
|
|
break;
|
|
path++;
|
|
}
|
|
#ifdef __DJGPP__
|
|
free(path);
|
|
#endif
|
|
if (err)
|
|
seterrno(err);
|
|
else
|
|
seterrno(oerrno);
|
|
return (NULL);
|
|
}
|
|
|
|
LOCAL char *
|
|
searchonefile(name, mode, plain_file, xn, nbuf, np, ep)
|
|
register char *name; /* Find <execname>/../name in PATH */
|
|
int mode; /* Mode for access() e.g. X_OK */
|
|
BOOL plain_file; /* Whether to check only plain files */
|
|
char *xn; /* The basename of the executable */
|
|
register char *nbuf; /* Name buffer base */
|
|
register char *np; /* Where to append name to path */
|
|
register char *ep; /* Point to last valid char in nbuf */
|
|
{
|
|
struct stat sb;
|
|
|
|
while (np > nbuf && np[-1] == '/')
|
|
*--np = '\0';
|
|
if (xn) {
|
|
*np++ = '/';
|
|
strlcpy(np, xn, ep - np);
|
|
if (stat(nbuf, &sb) < 0)
|
|
return (NULL);
|
|
if (!S_ISREG(sb.st_mode))
|
|
return (NULL);
|
|
*--np = '\0';
|
|
}
|
|
if ((plain_file & SIP_NO_STRIPBIN) == 0) {
|
|
if (np >= &nbuf[4] && streql(&np[-4], "/bin"))
|
|
np = &np[-4];
|
|
}
|
|
plain_file &= SIP_PLAIN_FILE;
|
|
*np++ = '/';
|
|
*np = '\0';
|
|
strlcpy(np, name, ep - np);
|
|
|
|
seterrno(0);
|
|
if (stat(nbuf, &sb) >= 0) {
|
|
if ((!plain_file || S_ISREG(sb.st_mode)) &&
|
|
(eaccess(nbuf, mode) >= 0)) {
|
|
return (strdup(nbuf));
|
|
}
|
|
if (geterrno() == 0)
|
|
seterrno(EACCES);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
#ifdef __DJGPP__
|
|
LOCAL char *
|
|
strbs2s(s)
|
|
char *s;
|
|
{
|
|
char *tmp = s;
|
|
|
|
if (tmp) {
|
|
while (*tmp) {
|
|
if (*tmp == '\\')
|
|
*tmp = '/';
|
|
tmp++;
|
|
}
|
|
}
|
|
return (s);
|
|
}
|
|
#endif
|