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>