reactos/sdk/tools/mkisofs/schilytools/libschily/searchinpath.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