/* @(#)searchinpath.c 1.5 16/08/01 Copyright 1999-2016 J. Schilling */ #include #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 #include #include #include /* getexecname() */ #include #include #include #include #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 /../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 /../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