Coverage Report

Created: 2023-05-19 06:16

/src/ntp-dev/sntp/libopts/compat/pathfind.c
Line
Count
Source (jump to first uncovered line)
1
/*  -*- Mode: C -*-  */
2
3
/* pathfind.c --- find a FILE  MODE along PATH */
4
5
/* Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk> */
6
7
/* Code: */
8
9
static char *
10
pathfind( char const * path,
11
          char const * fname,
12
          char const * mode );
13
14
#include "compat.h"
15
#ifndef HAVE_PATHFIND
16
#if defined(__windows__) && !defined(__CYGWIN__)
17
static char *
18
pathfind( char const * path,
19
          char const * fname,
20
          char const * mode )
21
{
22
    return strdup(fname);
23
}
24
#else
25
26
static char * make_absolute(char const * string, char const * dot_path);
27
static char * canonicalize_pathname(char * path);
28
static char * extract_colon_unit(char * dir, char const * string, int * p_index);
29
30
/**
31
 * local implementation of pathfind.
32
 * @param[in] path  colon separated list of directories
33
 * @param[in] fname the name we are hunting for
34
 * @param[in] mode  the required file mode
35
 * @returns an allocated string with the full path, or NULL
36
 */
37
static char *
38
pathfind( char const * path,
39
          char const * fname,
40
          char const * mode )
41
0
{
42
0
    int    p_index   = 0;
43
0
    int    mode_bits = 0;
44
0
    char * res_path  = NULL;
45
0
    char   zPath[ AG_PATH_MAX + 1 ];
46
47
0
    if (strchr( mode, 'r' )) mode_bits |= R_OK;
48
0
    if (strchr( mode, 'w' )) mode_bits |= W_OK;
49
0
    if (strchr( mode, 'x' )) mode_bits |= X_OK;
50
51
    /*
52
     *  FOR each non-null entry in the colon-separated path, DO ...
53
     */
54
0
    for (;;) {
55
0
        DIR  * dirP;
56
0
        char * colon_unit = extract_colon_unit( zPath, path, &p_index );
57
58
0
        if (colon_unit == NULL)
59
0
            break;
60
61
0
        dirP = opendir( colon_unit );
62
63
        /*
64
         *  IF the directory is inaccessable, THEN next directory
65
         */
66
0
        if (dirP == NULL)
67
0
            continue;
68
69
0
        for (;;) {
70
0
            struct dirent *entP = readdir( dirP );
71
72
0
            if (entP == (struct dirent *)NULL)
73
0
                break;
74
75
            /*
76
             *  IF the file name matches the one we are looking for, ...
77
             */
78
0
            if (strcmp(entP->d_name, fname) == 0) {
79
0
                char * abs_name = make_absolute(fname, colon_unit);
80
81
                /*
82
                 *  Make sure we can access it in the way we want
83
                 */
84
0
                if (access(abs_name, mode_bits) >= 0) {
85
                    /*
86
                     *  We can, so normalize the name and return it below
87
                     */
88
0
                    res_path = canonicalize_pathname(abs_name);
89
0
                }
90
91
0
                free(abs_name);
92
0
                break;
93
0
            }
94
0
        }
95
96
0
        closedir( dirP );
97
98
0
        if (res_path != NULL)
99
0
            break;
100
0
    }
101
102
0
    return res_path;
103
0
}
104
105
/*
106
 * Turn STRING  (a pathname) into an  absolute  pathname, assuming  that
107
 * DOT_PATH contains the symbolic location of  `.'.  This always returns
108
 * a new string, even if STRING was an absolute pathname to begin with.
109
 */
110
static char *
111
make_absolute( char const * string, char const * dot_path )
112
0
{
113
0
    char * result;
114
0
    int result_len;
115
116
0
    if (!dot_path || *string == '/') {
117
0
        result = strdup( string );
118
0
    } else {
119
0
        if (dot_path && dot_path[0]) {
120
0
            result = malloc( 2 + strlen( dot_path ) + strlen( string ) );
121
0
            strcpy( result, dot_path );
122
0
            result_len = (int)strlen(result);
123
0
            if (result[result_len - 1] != '/') {
124
0
                result[result_len++] = '/';
125
0
                result[result_len] = '\0';
126
0
            }
127
0
        } else {
128
0
            result = malloc( 3 + strlen( string ) );
129
0
            result[0] = '.'; result[1] = '/'; result[2] = '\0';
130
0
            result_len = 2;
131
0
        }
132
133
0
        strcpy( result + result_len, string );
134
0
    }
135
136
0
    return result;
137
0
}
138
139
/*
140
 * Canonicalize PATH, and return a  new path.  The new path differs from
141
 * PATH in that:
142
 *
143
 *    Multiple `/'s     are collapsed to a single `/'.
144
 *    Leading `./'s     are removed.
145
 *    Trailing `/.'s    are removed.
146
 *    Trailing `/'s     are removed.
147
 *    Non-leading `../'s and trailing `..'s are handled by removing
148
 *                    portions of the path.
149
 */
150
static char *
151
canonicalize_pathname( char *path )
152
0
{
153
0
    int i, start;
154
0
    char stub_char, *result;
155
156
    /* The result cannot be larger than the input PATH. */
157
0
    result = strdup( path );
158
159
0
    stub_char = (*path == '/') ? '/' : '.';
160
161
    /* Walk along RESULT looking for things to compact. */
162
0
    i = 0;
163
0
    while (result[i]) {
164
0
        while (result[i] != '\0' && result[i] != '/')
165
0
            i++;
166
167
0
        start = i++;
168
169
        /* If we didn't find any  slashes, then there is nothing left to
170
         * do.
171
         */
172
0
        if (!result[start])
173
0
            break;
174
175
        /* Handle multiple `/'s in a row. */
176
0
        while (result[i] == '/')
177
0
            i++;
178
179
0
#if !defined (apollo)
180
0
        if ((start + 1) != i)
181
#else
182
        if ((start + 1) != i && (start != 0 || i != 2))
183
#endif /* apollo */
184
0
        {
185
0
            strcpy( result + start + 1, result + i );
186
0
            i = start + 1;
187
0
        }
188
189
        /* Handle backquoted `/'. */
190
0
        if (start > 0 && result[start - 1] == '\\')
191
0
            continue;
192
193
        /* Check for trailing `/', and `.' by itself. */
194
0
        if ((start && !result[i])
195
0
            || (result[i] == '.' && !result[i+1])) {
196
0
            result[--i] = '\0';
197
0
            break;
198
0
        }
199
200
        /* Check for `../', `./' or trailing `.' by itself. */
201
0
        if (result[i] == '.') {
202
            /* Handle `./'. */
203
0
            if (result[i + 1] == '/') {
204
0
                strcpy( result + i, result + i + 1 );
205
0
                i = (start < 0) ? 0 : start;
206
0
                continue;
207
0
            }
208
209
            /* Handle `../' or trailing `..' by itself. */
210
0
            if (result[i + 1] == '.' &&
211
0
                (result[i + 2] == '/' || !result[i + 2])) {
212
0
                while (--start > -1 && result[start] != '/')
213
0
                    ;
214
0
                strcpy( result + start + 1, result + i + 2 );
215
0
                i = (start < 0) ? 0 : start;
216
0
                continue;
217
0
            }
218
0
        }
219
0
    }
220
221
0
    if (!*result) {
222
0
        *result = stub_char;
223
0
        result[1] = '\0';
224
0
    }
225
226
0
    return result;
227
0
}
228
229
/*
230
 * Given a  string containing units of information separated  by colons,
231
 * return the next one  pointed to by (P_INDEX), or NULL if there are no
232
 * more.  Advance (P_INDEX) to the character after the colon.
233
 */
234
static char *
235
extract_colon_unit(char * pzDir, char const * string, int * p_index)
236
0
{
237
0
    char * pzDest = pzDir;
238
0
    int    ix     = *p_index;
239
240
0
    if (string == NULL)
241
0
        return NULL;
242
243
0
    if ((unsigned)ix >= strlen( string ))
244
0
        return NULL;
245
246
0
    {
247
0
        char const * pzSrc = string + ix;
248
249
0
        while (*pzSrc == ':')  pzSrc++;
250
251
0
        for (;;) {
252
0
            char ch = (*(pzDest++) = *(pzSrc++));
253
0
            switch (ch) {
254
0
            case ':':
255
0
                pzDest[-1] = NUL;
256
                /* FALLTHROUGH */
257
0
            case NUL:
258
0
                goto copy_done;
259
0
            }
260
261
0
            if ((unsigned long)(pzDest - pzDir) >= AG_PATH_MAX)
262
0
                break;
263
0
        } copy_done:;
264
265
0
        ix = (int)(pzSrc - string);
266
0
    }
267
268
0
    if (*pzDir == NUL)
269
0
        return NULL;
270
271
0
    *p_index = ix;
272
0
    return pzDir;
273
0
}
274
#endif /* __windows__ / __CYGWIN__ */
275
#endif /* HAVE_PATHFIND */
276
277
/*
278
 * Local Variables:
279
 * mode: C
280
 * c-file-style: "stroustrup"
281
 * indent-tabs-mode: nil
282
 * End:
283
 * end of compat/pathfind.c */