PageRenderTime 58ms CodeModel.GetById 13ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/ntp/sntp/libopts/compat/pathfind.c

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