~ubuntu-branches/ubuntu/gutsy/ntp/gutsy

« back to all changes in this revision

Viewing changes to libopts/compat/pathfind.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-05-18 22:41:56 UTC
  • mfrom: (1.2.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20070518224156-563ruqxsxvqvoy8h
Tags: 1:4.2.4p0+dfsg-1ubuntu1
* Merge from Debian unstable.
* Remaining Ubuntu changes:
  - Update version in conflicts/replaces to that which was shipped in edgy,
    which was later than that in Debian (due to the ubuntuX).
  - Change default server to ntp.ubuntu.com.
  - Remove stop links from rc0 and rc6
  - Call dh_installinit with --error-handler
  - Set Ubuntu maintainer address.

Show diffs side-by-side

added added

removed removed

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