Annotation of embedaddon/ntp/sntp/libopts/compat/pathfind.c, revision 1.1
1.1 ! misho 1: /* -*- Mode: C -*- */
! 2:
! 3: /* pathfind.c --- find a FILE MODE along PATH */
! 4:
! 5: /*
! 6: * Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk>
! 7: * Time-stamp: "2010-07-17 09:50:32 bkorb"
! 8: */
! 9:
! 10: /* Code: */
! 11:
! 12: #include "compat.h"
! 13: #ifndef HAVE_PATHFIND
! 14: #if defined(__windows__) && !defined(__CYGWIN__)
! 15: char*
! 16: pathfind( char const* path,
! 17: char const* fileName,
! 18: char const* mode )
! 19: {
! 20: return NULL;
! 21: }
! 22: #else
! 23:
! 24: static char* make_absolute( char const *string, char const *dot_path );
! 25: static char* canonicalize_pathname( char *path );
! 26: static char* extract_colon_unit( char* dir, char const *string, int *p_index );
! 27:
! 28:
! 29: /*=export_func pathfind
! 30: *
! 31: * what: fild a file in a list of directories
! 32: *
! 33: * ifndef: HAVE_PATHFIND
! 34: *
! 35: * arg: + char const* + path + colon separated list of search directories +
! 36: * arg: + char const* + file + the name of the file to look for +
! 37: * arg: + char const* + mode + the mode bits that must be set to match +
! 38: *
! 39: * ret_type: char*
! 40: * ret_desc: the path to the located file
! 41: *
! 42: * doc:
! 43: *
! 44: * pathfind looks for a a file with name "FILE" and "MODE" access
! 45: * along colon delimited "PATH", and returns the full pathname as a
! 46: * string, or NULL if not found. If "FILE" contains a slash, then
! 47: * it is treated as a relative or absolute path and "PATH" is ignored.
! 48: *
! 49: * @strong{NOTE}: this function is compiled into @file{libopts} only if
! 50: * it is not natively supplied.
! 51: *
! 52: * The "MODE" argument is a string of option letters chosen from the
! 53: * list below:
! 54: * @example
! 55: * Letter Meaning
! 56: * r readable
! 57: * w writable
! 58: * x executable
! 59: * f normal file (NOT IMPLEMENTED)
! 60: * b block special (NOT IMPLEMENTED)
! 61: * c character special (NOT IMPLEMENTED)
! 62: * d directory (NOT IMPLEMENTED)
! 63: * p FIFO (pipe) (NOT IMPLEMENTED)
! 64: * u set user ID bit (NOT IMPLEMENTED)
! 65: * g set group ID bit (NOT IMPLEMENTED)
! 66: * k sticky bit (NOT IMPLEMENTED)
! 67: * s size nonzero (NOT IMPLEMENTED)
! 68: * @end example
! 69: *
! 70: * example:
! 71: * To find the "ls" command using the "PATH" environment variable:
! 72: * @example
! 73: * #include <stdlib.h>
! 74: * char* pz_ls = pathfind( getenv("PATH"), "ls", "rx" );
! 75: * <<do whatever with pz_ls>>
! 76: * free( pz_ls );
! 77: * @end example
! 78: * The path is allocated with @code{malloc(3C)}, so you must @code{free(3C)}
! 79: * the result. Also, do not use unimplemented file modes. :-)
! 80: *
! 81: * err: returns NULL if the file is not found.
! 82: =*/
! 83: char*
! 84: pathfind( char const* path,
! 85: char const* fileName,
! 86: char const* mode )
! 87: {
! 88: int p_index = 0;
! 89: int mode_bits = 0;
! 90: char* pathName = NULL;
! 91: char zPath[ AG_PATH_MAX + 1 ];
! 92:
! 93: if (strchr( mode, 'r' )) mode_bits |= R_OK;
! 94: if (strchr( mode, 'w' )) mode_bits |= W_OK;
! 95: if (strchr( mode, 'x' )) mode_bits |= X_OK;
! 96:
! 97: /*
! 98: * FOR each non-null entry in the colon-separated path, DO ...
! 99: */
! 100: for (;;) {
! 101: DIR* dirP;
! 102: char* colon_unit = extract_colon_unit( zPath, path, &p_index );
! 103:
! 104: /*
! 105: * IF no more entries, THEN quit
! 106: */
! 107: if (colon_unit == NULL)
! 108: break;
! 109:
! 110: dirP = opendir( colon_unit );
! 111:
! 112: /*
! 113: * IF the directory is inaccessable, THEN next directory
! 114: */
! 115: if (dirP == NULL)
! 116: continue;
! 117:
! 118: /*
! 119: * FOR every entry in the given directory, ...
! 120: */
! 121: for (;;) {
! 122: struct dirent *entP = readdir( dirP );
! 123:
! 124: if (entP == (struct dirent*)NULL)
! 125: break;
! 126:
! 127: /*
! 128: * IF the file name matches the one we are looking for, ...
! 129: */
! 130: if (strcmp( entP->d_name, fileName ) == 0) {
! 131: char* pzFullName = make_absolute( fileName, colon_unit);
! 132:
! 133: /*
! 134: * Make sure we can access it in the way we want
! 135: */
! 136: if (access( pzFullName, mode_bits ) >= 0) {
! 137: /*
! 138: * We can, so normalize the name and return it below
! 139: */
! 140: pathName = canonicalize_pathname( pzFullName );
! 141: }
! 142:
! 143: free( (void*)pzFullName );
! 144: break;
! 145: }
! 146: }
! 147:
! 148: closedir( dirP );
! 149:
! 150: if (pathName != NULL)
! 151: break;
! 152: }
! 153:
! 154: return pathName;
! 155: }
! 156:
! 157: /*
! 158: * Turn STRING (a pathname) into an absolute pathname, assuming that
! 159: * DOT_PATH contains the symbolic location of `.'. This always returns
! 160: * a new string, even if STRING was an absolute pathname to begin with.
! 161: */
! 162: static char*
! 163: make_absolute( char const *string, char const *dot_path )
! 164: {
! 165: char *result;
! 166: int result_len;
! 167:
! 168: if (!dot_path || *string == '/') {
! 169: result = strdup( string );
! 170: } else {
! 171: if (dot_path && dot_path[0]) {
! 172: result = malloc( 2 + strlen( dot_path ) + strlen( string ) );
! 173: strcpy( result, dot_path );
! 174: result_len = strlen( result );
! 175: if (result[result_len - 1] != '/') {
! 176: result[result_len++] = '/';
! 177: result[result_len] = '\0';
! 178: }
! 179: } else {
! 180: result = malloc( 3 + strlen( string ) );
! 181: result[0] = '.'; result[1] = '/'; result[2] = '\0';
! 182: result_len = 2;
! 183: }
! 184:
! 185: strcpy( result + result_len, string );
! 186: }
! 187:
! 188: return result;
! 189: }
! 190:
! 191: /*
! 192: * Canonicalize PATH, and return a new path. The new path differs from
! 193: * PATH in that:
! 194: *
! 195: * Multiple `/'s are collapsed to a single `/'.
! 196: * Leading `./'s are removed.
! 197: * Trailing `/.'s are removed.
! 198: * Trailing `/'s are removed.
! 199: * Non-leading `../'s and trailing `..'s are handled by removing
! 200: * portions of the path.
! 201: */
! 202: static char*
! 203: canonicalize_pathname( char *path )
! 204: {
! 205: int i, start;
! 206: char stub_char, *result;
! 207:
! 208: /* The result cannot be larger than the input PATH. */
! 209: result = strdup( path );
! 210:
! 211: stub_char = (*path == '/') ? '/' : '.';
! 212:
! 213: /* Walk along RESULT looking for things to compact. */
! 214: i = 0;
! 215: while (result[i]) {
! 216: while (result[i] != '\0' && result[i] != '/')
! 217: i++;
! 218:
! 219: start = i++;
! 220:
! 221: /* If we didn't find any slashes, then there is nothing left to
! 222: * do.
! 223: */
! 224: if (!result[start])
! 225: break;
! 226:
! 227: /* Handle multiple `/'s in a row. */
! 228: while (result[i] == '/')
! 229: i++;
! 230:
! 231: #if !defined (apollo)
! 232: if ((start + 1) != i)
! 233: #else
! 234: if ((start + 1) != i && (start != 0 || i != 2))
! 235: #endif /* apollo */
! 236: {
! 237: strcpy( result + start + 1, result + i );
! 238: i = start + 1;
! 239: }
! 240:
! 241: /* Handle backquoted `/'. */
! 242: if (start > 0 && result[start - 1] == '\\')
! 243: continue;
! 244:
! 245: /* Check for trailing `/', and `.' by itself. */
! 246: if ((start && !result[i])
! 247: || (result[i] == '.' && !result[i+1])) {
! 248: result[--i] = '\0';
! 249: break;
! 250: }
! 251:
! 252: /* Check for `../', `./' or trailing `.' by itself. */
! 253: if (result[i] == '.') {
! 254: /* Handle `./'. */
! 255: if (result[i + 1] == '/') {
! 256: strcpy( result + i, result + i + 1 );
! 257: i = (start < 0) ? 0 : start;
! 258: continue;
! 259: }
! 260:
! 261: /* Handle `../' or trailing `..' by itself. */
! 262: if (result[i + 1] == '.' &&
! 263: (result[i + 2] == '/' || !result[i + 2])) {
! 264: while (--start > -1 && result[start] != '/')
! 265: ;
! 266: strcpy( result + start + 1, result + i + 2 );
! 267: i = (start < 0) ? 0 : start;
! 268: continue;
! 269: }
! 270: }
! 271: }
! 272:
! 273: if (!*result) {
! 274: *result = stub_char;
! 275: result[1] = '\0';
! 276: }
! 277:
! 278: return result;
! 279: }
! 280:
! 281: /*
! 282: * Given a string containing units of information separated by colons,
! 283: * return the next one pointed to by (P_INDEX), or NULL if there are no
! 284: * more. Advance (P_INDEX) to the character after the colon.
! 285: */
! 286: static char*
! 287: extract_colon_unit( char* pzDir, char const *string, int *p_index )
! 288: {
! 289: char* pzDest = pzDir;
! 290: int ix = *p_index;
! 291:
! 292: if (string == NULL)
! 293: return NULL;
! 294:
! 295: if ((unsigned)ix >= strlen( string ))
! 296: return NULL;
! 297:
! 298: {
! 299: char const* pzSrc = string + ix;
! 300:
! 301: while (*pzSrc == ':') pzSrc++;
! 302:
! 303: for (;;) {
! 304: char ch = (*(pzDest++) = *(pzSrc++));
! 305: switch (ch) {
! 306: case ':':
! 307: pzDest[-1] = NUL;
! 308: case NUL:
! 309: goto copy_done;
! 310: }
! 311:
! 312: if ((pzDest - pzDir) >= AG_PATH_MAX)
! 313: break;
! 314: } copy_done:;
! 315:
! 316: ix = pzSrc - string;
! 317: }
! 318:
! 319: if (*pzDir == NUL)
! 320: return NULL;
! 321:
! 322: *p_index = ix;
! 323: return pzDir;
! 324: }
! 325: #endif /* __windows__ / __CYGWIN__ */
! 326: #endif /* HAVE_PATHFIND */
! 327:
! 328: /*
! 329: * Local Variables:
! 330: * mode: C
! 331: * c-file-style: "stroustrup"
! 332: * indent-tabs-mode: nil
! 333: * End:
! 334: * end of compat/pathfind.c */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>