pathfind.c revision 289997
1177633Sdfr/*  -*- Mode: C -*-  */
2177633Sdfr
3177633Sdfr/* pathfind.c --- find a FILE  MODE along PATH */
4177633Sdfr
5177633Sdfr/* Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk> */
6177633Sdfr
7177633Sdfr/* Code: */
8177633Sdfr
9177633Sdfrstatic char *
10177633Sdfrpathfind( char const * path,
11177633Sdfr          char const * fname,
12177633Sdfr          char const * mode );
13177633Sdfr
14177633Sdfr#include "compat.h"
15177633Sdfr#ifndef HAVE_PATHFIND
16177633Sdfr#if defined(__windows__) && !defined(__CYGWIN__)
17177633Sdfrstatic char *
18177633Sdfrpathfind( char const * path,
19177633Sdfr          char const * fname,
20177633Sdfr          char const * mode )
21177633Sdfr{
22177633Sdfr    return strdup(fname);
23177633Sdfr}
24177633Sdfr#else
25177633Sdfr
26177633Sdfrstatic char * make_absolute(char const * string, char const * dot_path);
27177633Sdfrstatic char * canonicalize_pathname(char * path);
28177633Sdfrstatic char * extract_colon_unit(char * dir, char const * string, int * p_index);
29177633Sdfr
30177633Sdfr/**
31177633Sdfr * local implementation of pathfind.
32177633Sdfr * @param[in] path  colon separated list of directories
33177633Sdfr * @param[in] fname the name we are hunting for
34177633Sdfr * @param[in] mode  the required file mode
35177633Sdfr * @returns an allocated string with the full path, or NULL
36177633Sdfr */
37177633Sdfrstatic char *
38177633Sdfrpathfind( char const * path,
39177633Sdfr          char const * fname,
40177633Sdfr          char const * mode )
41177633Sdfr{
42177633Sdfr    int    p_index   = 0;
43177633Sdfr    int    mode_bits = 0;
44177633Sdfr    char * res_path  = NULL;
45177633Sdfr    char   zPath[ AG_PATH_MAX + 1 ];
46177633Sdfr
47177633Sdfr    if (strchr( mode, 'r' )) mode_bits |= R_OK;
48177633Sdfr    if (strchr( mode, 'w' )) mode_bits |= W_OK;
49177633Sdfr    if (strchr( mode, 'x' )) mode_bits |= X_OK;
50177633Sdfr
51177633Sdfr    /*
52177633Sdfr     *  FOR each non-null entry in the colon-separated path, DO ...
53177633Sdfr     */
54177633Sdfr    for (;;) {
55177633Sdfr        DIR  * dirP;
56177633Sdfr        char * colon_unit = extract_colon_unit( zPath, path, &p_index );
57177633Sdfr
58177633Sdfr        if (colon_unit == NULL)
59177633Sdfr            break;
60177633Sdfr
61177633Sdfr        dirP = opendir( colon_unit );
62177633Sdfr
63177633Sdfr        /*
64177633Sdfr         *  IF the directory is inaccessable, THEN next directory
65177633Sdfr         */
66177633Sdfr        if (dirP == NULL)
67177633Sdfr            continue;
68177633Sdfr
69192971Skmacy        for (;;) {
70192971Skmacy            struct dirent *entP = readdir( dirP );
71177633Sdfr
72177633Sdfr            if (entP == (struct dirent *)NULL)
73177633Sdfr                break;
74177633Sdfr
75177633Sdfr            /*
76177633Sdfr             *  IF the file name matches the one we are looking for, ...
77177633Sdfr             */
78177633Sdfr            if (strcmp(entP->d_name, fname) == 0) {
79192971Skmacy                char * abs_name = make_absolute(fname, colon_unit);
80192971Skmacy
81177633Sdfr                /*
82177633Sdfr                 *  Make sure we can access it in the way we want
83177633Sdfr                 */
84177633Sdfr                if (access(abs_name, mode_bits) >= 0) {
85177633Sdfr                    /*
86177633Sdfr                     *  We can, so normalize the name and return it below
87177633Sdfr                     */
88177633Sdfr                    res_path = canonicalize_pathname(abs_name);
89177633Sdfr                }
90177633Sdfr
91192971Skmacy                free(abs_name);
92192971Skmacy                break;
93177633Sdfr            }
94177633Sdfr        }
95177633Sdfr
96177633Sdfr        closedir( dirP );
97177633Sdfr
98177633Sdfr        if (res_path != NULL)
99177633Sdfr            break;
100177633Sdfr    }
101177633Sdfr
102177633Sdfr    return res_path;
103177633Sdfr}
104177633Sdfr
105177633Sdfr/*
106177633Sdfr * Turn STRING  (a pathname) into an  absolute  pathname, assuming  that
107177633Sdfr * DOT_PATH contains the symbolic location of  `.'.  This always returns
108177633Sdfr * a new string, even if STRING was an absolute pathname to begin with.
109177633Sdfr */
110177633Sdfrstatic char *
111177633Sdfrmake_absolute( char const * string, char const * dot_path )
112177633Sdfr{
113177633Sdfr    char * result;
114177633Sdfr    int result_len;
115177633Sdfr
116177633Sdfr    if (!dot_path || *string == '/') {
117177633Sdfr        result = strdup( string );
118177633Sdfr	if (result == NULL) {
119177633Sdfr	return NULL;    /* couldn't allocate memory    */
120177633Sdfr	}
121177633Sdfr    } else {
122177633Sdfr        if (dot_path && dot_path[0]) {
123177633Sdfr            result = malloc( 2 + strlen( dot_path ) + strlen( string ) );
124177633Sdfr		if (result == NULL) {
125177633Sdfr		return NULL;    /* couldn't allocate memory    */
126177633Sdfr		}
127177633Sdfr            strcpy( result, dot_path );
128177633Sdfr            result_len = (int)strlen(result);
129177633Sdfr            if (result[result_len - 1] != '/') {
130177633Sdfr                result[result_len++] = '/';
131177633Sdfr                result[result_len] = '\0';
132177633Sdfr            }
133177633Sdfr        } else {
134177633Sdfr            result = malloc( 3 + strlen( string ) );
135177633Sdfr		if (result == NULL) {
136177633Sdfr		return NULL;    /* couldn't allocate memory    */
137177633Sdfr		}
138177633Sdfr            result[0] = '.'; result[1] = '/'; result[2] = '\0';
139177633Sdfr            result_len = 2;
140177633Sdfr        }
141177633Sdfr
142177633Sdfr        strcpy( result + result_len, string );
143177633Sdfr    }
144177633Sdfr
145177633Sdfr    return result;
146177633Sdfr}
147177633Sdfr
148177633Sdfr/*
149177633Sdfr * Canonicalize PATH, and return a  new path.  The new path differs from
150177633Sdfr * PATH in that:
151177633Sdfr *
152177633Sdfr *    Multiple `/'s     are collapsed to a single `/'.
153177633Sdfr *    Leading `./'s     are removed.
154177633Sdfr *    Trailing `/.'s    are removed.
155177633Sdfr *    Trailing `/'s     are removed.
156177633Sdfr *    Non-leading `../'s and trailing `..'s are handled by removing
157177633Sdfr *                    portions of the path.
158177633Sdfr */
159177633Sdfrstatic char *
160177633Sdfrcanonicalize_pathname( char *path )
161177633Sdfr{
162177633Sdfr    int i, start;
163177633Sdfr    char stub_char, *result;
164177633Sdfr
165177633Sdfr    /* The result cannot be larger than the input PATH. */
166177633Sdfr    result = strdup( path );
167177633Sdfr	if (result == NULL) {
168177633Sdfr	return NULL;    /* couldn't allocate memory    */
169177633Sdfr	}
170177633Sdfr    stub_char = (*path == '/') ? '/' : '.';
171177633Sdfr
172177633Sdfr    /* Walk along RESULT looking for things to compact. */
173177633Sdfr    i = 0;
174177633Sdfr    while (result[i]) {
175177633Sdfr        while (result[i] != '\0' && result[i] != '/')
176177633Sdfr            i++;
177177633Sdfr
178177633Sdfr        start = i++;
179177633Sdfr
180177633Sdfr        /* If we didn't find any  slashes, then there is nothing left to
181177633Sdfr         * do.
182177633Sdfr         */
183177633Sdfr        if (!result[start])
184177633Sdfr            break;
185177633Sdfr
186177633Sdfr        /* Handle multiple `/'s in a row. */
187177633Sdfr        while (result[i] == '/')
188177633Sdfr            i++;
189177633Sdfr
190177633Sdfr#if !defined (apollo)
191177633Sdfr        if ((start + 1) != i)
192177633Sdfr#else
193177633Sdfr        if ((start + 1) != i && (start != 0 || i != 2))
194177633Sdfr#endif /* apollo */
195177633Sdfr        {
196177633Sdfr            strcpy( result + start + 1, result + i );
197177633Sdfr            i = start + 1;
198177633Sdfr        }
199177633Sdfr
200177633Sdfr        /* Handle backquoted `/'. */
201177633Sdfr        if (start > 0 && result[start - 1] == '\\')
202177633Sdfr            continue;
203177633Sdfr
204177633Sdfr        /* Check for trailing `/', and `.' by itself. */
205177633Sdfr        if ((start && !result[i])
206177633Sdfr            || (result[i] == '.' && !result[i+1])) {
207177633Sdfr            result[--i] = '\0';
208177633Sdfr            break;
209177633Sdfr        }
210177633Sdfr
211177633Sdfr        /* Check for `../', `./' or trailing `.' by itself. */
212177633Sdfr        if (result[i] == '.') {
213177633Sdfr            /* Handle `./'. */
214177633Sdfr            if (result[i + 1] == '/') {
215177633Sdfr                strcpy( result + i, result + i + 1 );
216177633Sdfr                i = (start < 0) ? 0 : start;
217177633Sdfr                continue;
218177633Sdfr            }
219177633Sdfr
220177633Sdfr            /* Handle `../' or trailing `..' by itself. */
221177633Sdfr            if (result[i + 1] == '.' &&
222177633Sdfr                (result[i + 2] == '/' || !result[i + 2])) {
223177633Sdfr                while (--start > -1 && result[start] != '/')
224177633Sdfr                    ;
225177633Sdfr                strcpy( result + start + 1, result + i + 2 );
226177633Sdfr                i = (start < 0) ? 0 : start;
227177633Sdfr                continue;
228177633Sdfr            }
229177633Sdfr        }
230177633Sdfr    }
231177633Sdfr
232177633Sdfr    if (!*result) {
233177633Sdfr        *result = stub_char;
234192971Skmacy        result[1] = '\0';
235192971Skmacy    }
236192971Skmacy
237192971Skmacy    return result;
238192971Skmacy}
239192971Skmacy
240192971Skmacy/*
241192971Skmacy * Given a  string containing units of information separated  by colons,
242192971Skmacy * return the next one  pointed to by (P_INDEX), or NULL if there are no
243192971Skmacy * more.  Advance (P_INDEX) to the character after the colon.
244192971Skmacy */
245192971Skmacystatic char *
246192971Skmacyextract_colon_unit(char * pzDir, char const * string, int * p_index)
247192971Skmacy{
248192971Skmacy    char * pzDest = pzDir;
249192971Skmacy    int    ix     = *p_index;
250192971Skmacy
251192971Skmacy    if (string == NULL)
252192971Skmacy        return NULL;
253192971Skmacy
254192971Skmacy    if ((unsigned)ix >= strlen( string ))
255192971Skmacy        return NULL;
256192971Skmacy
257192971Skmacy    {
258192971Skmacy        char const * pzSrc = string + ix;
259192971Skmacy
260192971Skmacy        while (*pzSrc == ':')  pzSrc++;
261192971Skmacy
262192971Skmacy        for (;;) {
263192971Skmacy            char ch = (*(pzDest++) = *(pzSrc++));
264192971Skmacy            switch (ch) {
265192971Skmacy            case ':':
266192971Skmacy                pzDest[-1] = NUL;
267192971Skmacy                /* FALLTHROUGH */
268192971Skmacy            case NUL:
269192971Skmacy                goto copy_done;
270192971Skmacy            }
271192971Skmacy
272192971Skmacy            if ((unsigned long)(pzDest - pzDir) >= AG_PATH_MAX)
273192971Skmacy                break;
274192971Skmacy        } copy_done:;
275192971Skmacy
276        ix = (int)(pzSrc - string);
277    }
278
279    if (*pzDir == NUL)
280        return NULL;
281
282    *p_index = ix;
283    return pzDir;
284}
285#endif /* __windows__ / __CYGWIN__ */
286#endif /* HAVE_PATHFIND */
287
288/*
289 * Local Variables:
290 * mode: C
291 * c-file-style: "stroustrup"
292 * indent-tabs-mode: nil
293 * End:
294 * end of compat/pathfind.c */
295