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