Annotation of embedaddon/ntp/sntp/libopts/compat/pathfind.c, revision 1.1.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>