123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- /* -*- Mode: C -*- */
- /* pathfind.c --- find a FILE MODE along PATH */
- /*
- * Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk>
- * Created: Tue Jun 24 15:07:31 1997
- * Last Modified: $Date: 2005/07/27 17:26:32 $
- * by: bkorb
- *
- * $Id: pathfind.c,v 4.4 2005/07/27 17:26:32 bkorb Exp $
- */
- /* Code: */
- #include "compat.h"
- #ifndef HAVE_PATHFIND
- static char* make_absolute( const char *string, const char *dot_path );
- static char* canonicalize_pathname( char *path );
- static char* extract_colon_unit( char* dir, const char *string, int *p_index );
- /*=export_func pathfind
- *
- * what: fild a file in a list of directories
- *
- * ifndef: HAVE_PATHFIND
- *
- * arg: + const char* + path + colon separated list of search directories +
- * arg: + const char* + file + the name of the file to look for +
- * arg: + const char* + mode + the mode bits that must be set to match +
- *
- * ret_type: char*
- * ret_desc: the path to the located file
- *
- * doc:
- *
- * pathfind looks for a a file with name "FILE" and "MODE" access
- * along colon delimited "PATH", and returns the full pathname as a
- * string, or NULL if not found. If "FILE" contains a slash, then
- * it is treated as a relative or absolute path and "PATH" is ignored.
- *
- * @strong{NOTE}: this function is compiled into @file{libopts} only if
- * it is not natively supplied.
- *
- * The "MODE" argument is a string of option letters chosen from the
- * list below:
- * @example
- * Letter Meaning
- * r readable
- * w writable
- * x executable
- * f normal file (NOT IMPLEMENTED)
- * b block special (NOT IMPLEMENTED)
- * c character special (NOT IMPLEMENTED)
- * d directory (NOT IMPLEMENTED)
- * p FIFO (pipe) (NOT IMPLEMENTED)
- * u set user ID bit (NOT IMPLEMENTED)
- * g set group ID bit (NOT IMPLEMENTED)
- * k sticky bit (NOT IMPLEMENTED)
- * s size nonzero (NOT IMPLEMENTED)
- * @end example
- *
- * example:
- * To find the "ls" command using the "PATH" environment variable:
- * @example
- * #include <stdlib.h>
- * char* pz_ls = pathfind( getenv("PATH"), "ls", "rx" );
- * <<do whatever with pz_ls>>
- * free( pz_ls );
- * @end example
- * The path is allocated with @code{malloc(3C)}, so you must @code{free(3C)}
- * the result. Also, do not use unimplemented file modes. :-)
- *
- * err: returns NULL if the file is not found.
- =*/
- char*
- pathfind( const char* path,
- const char* fileName,
- const char* mode )
- {
- int p_index = 0;
- int mode_bits = 0;
- char* pathName = NULL;
- char zPath[ MAXPATHLEN + 1 ];
- if (strchr( mode, 'r' )) mode_bits |= R_OK;
- if (strchr( mode, 'w' )) mode_bits |= W_OK;
- if (strchr( mode, 'x' )) mode_bits |= X_OK;
- /*
- * FOR each non-null entry in the colon-separated path, DO ...
- */
- for (;;) {
- DIR* dirP;
- char* colon_unit = extract_colon_unit( zPath, path, &p_index );
- /*
- * IF no more entries, THEN quit
- */
- if (colon_unit == NULL)
- break;
- dirP = opendir( colon_unit );
- /*
- * IF the directory is inaccessable, THEN next directory
- */
- if (dirP == NULL)
- continue;
- /*
- * FOR every entry in the given directory, ...
- */
- for (;;) {
- struct dirent *entP = readdir( dirP );
- if (entP == (struct dirent*)NULL)
- break;
- /*
- * IF the file name matches the one we are looking for, ...
- */
- if (strcmp( entP->d_name, fileName ) == 0) {
- char* pzFullName = make_absolute( fileName, colon_unit);
- /*
- * Make sure we can access it in the way we want
- */
- if (access( pzFullName, mode_bits ) >= 0) {
- /*
- * We can, so normalize the name and return it below
- */
- pathName = canonicalize_pathname( pzFullName );
- }
- free( (void*)pzFullName );
- break;
- }
- }
-
- closedir( dirP );
- if (pathName != NULL)
- break;
- }
- return pathName;
- }
- /*
- * Turn STRING (a pathname) into an absolute pathname, assuming that
- * DOT_PATH contains the symbolic location of `.'. This always returns
- * a new string, even if STRING was an absolute pathname to begin with.
- */
- static char*
- make_absolute( const char *string, const char *dot_path )
- {
- char *result;
- int result_len;
-
- if (!dot_path || *string == '/') {
- result = strdup( string );
- } else {
- if (dot_path && dot_path[0]) {
- result = malloc( 2 + strlen( dot_path ) + strlen( string ) );
- strcpy( result, dot_path );
- result_len = strlen( result );
- if (result[result_len - 1] != '/') {
- result[result_len++] = '/';
- result[result_len] = '\0';
- }
- } else {
- result = malloc( 3 + strlen( string ) );
- result[0] = '.'; result[1] = '/'; result[2] = '\0';
- result_len = 2;
- }
- strcpy( result + result_len, string );
- }
- return result;
- }
- /*
- * Canonicalize PATH, and return a new path. The new path differs from
- * PATH in that:
- *
- * Multiple `/'s are collapsed to a single `/'.
- * Leading `./'s are removed.
- * Trailing `/.'s are removed.
- * Trailing `/'s are removed.
- * Non-leading `../'s and trailing `..'s are handled by removing
- * portions of the path.
- */
- static char*
- canonicalize_pathname( char *path )
- {
- int i, start;
- char stub_char, *result;
- /* The result cannot be larger than the input PATH. */
- result = strdup( path );
- stub_char = (*path == '/') ? '/' : '.';
- /* Walk along RESULT looking for things to compact. */
- i = 0;
- while (result[i]) {
- while (result[i] != '\0' && result[i] != '/')
- i++;
- start = i++;
- /* If we didn't find any slashes, then there is nothing left to
- * do.
- */
- if (!result[start])
- break;
- /* Handle multiple `/'s in a row. */
- while (result[i] == '/')
- i++;
- #if !defined (apollo)
- if ((start + 1) != i)
- #else
- if ((start + 1) != i && (start != 0 || i != 2))
- #endif /* apollo */
- {
- strcpy( result + start + 1, result + i );
- i = start + 1;
- }
- /* Handle backquoted `/'. */
- if (start > 0 && result[start - 1] == '\\')
- continue;
- /* Check for trailing `/', and `.' by itself. */
- if ((start && !result[i])
- || (result[i] == '.' && !result[i+1])) {
- result[--i] = '\0';
- break;
- }
- /* Check for `../', `./' or trailing `.' by itself. */
- if (result[i] == '.') {
- /* Handle `./'. */
- if (result[i + 1] == '/') {
- strcpy( result + i, result + i + 1 );
- i = (start < 0) ? 0 : start;
- continue;
- }
- /* Handle `../' or trailing `..' by itself. */
- if (result[i + 1] == '.' &&
- (result[i + 2] == '/' || !result[i + 2])) {
- while (--start > -1 && result[start] != '/')
- ;
- strcpy( result + start + 1, result + i + 2 );
- i = (start < 0) ? 0 : start;
- continue;
- }
- }
- }
- if (!*result) {
- *result = stub_char;
- result[1] = '\0';
- }
- return result;
- }
- /*
- * Given a string containing units of information separated by colons,
- * return the next one pointed to by (P_INDEX), or NULL if there are no
- * more. Advance (P_INDEX) to the character after the colon.
- */
- static char*
- extract_colon_unit( char* pzDir, const char *string, int *p_index )
- {
- char* pzDest = pzDir;
- int ix = *p_index;
- if (string == NULL)
- return NULL;
- if ((unsigned)ix >= strlen( string ))
- return NULL;
- {
- const char* pzSrc = string + ix;
- while (*pzSrc == ':') pzSrc++;
- for (;;) {
- char ch = (*(pzDest++) = *(pzSrc++));
- switch (ch) {
- case ':':
- pzDest[-1] = NUL;
- case NUL:
- goto copy_done;
- }
- if ((pzDest - pzDir) >= MAXPATHLEN)
- break;
- } copy_done:;
- ix = pzSrc - string;
- }
- if (*pzDir == NUL)
- return NULL;
- *p_index = ix;
- return pzDir;
- }
- #endif /* HAVE_PATHFIND */
- /*
- * Local Variables:
- * mode: C
- * c-file-style: "stroustrup"
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- * end of compat/pathfind.c */
|