3 /* pathfind.c --- find a FILE MODE along PATH */
5 /* Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk> */
10 pathfind( char const * path,
16 #if defined(__windows__) && !defined(__CYGWIN__)
18 pathfind( char const * path,
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);
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
38 pathfind( char const * path,
44 char * res_path = NULL;
45 char zPath[ AG_PATH_MAX + 1 ];
47 if (strchr( mode, 'r' )) mode_bits |= R_OK;
48 if (strchr( mode, 'w' )) mode_bits |= W_OK;
49 if (strchr( mode, 'x' )) mode_bits |= X_OK;
52 * FOR each non-null entry in the colon-separated path, DO ...
56 char * colon_unit = extract_colon_unit( zPath, path, &p_index );
58 if (colon_unit == NULL)
61 dirP = opendir( colon_unit );
64 * IF the directory is inaccessable, THEN next directory
70 struct dirent *entP = readdir( dirP );
72 if (entP == (struct dirent *)NULL)
76 * IF the file name matches the one we are looking for, ...
78 if (strcmp(entP->d_name, fname) == 0) {
79 char * abs_name = make_absolute(fname, colon_unit);
82 * Make sure we can access it in the way we want
84 if (access(abs_name, mode_bits) >= 0) {
86 * We can, so normalize the name and return it below
88 res_path = canonicalize_pathname(abs_name);
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.
111 make_absolute( char const * string, char const * dot_path )
116 if (!dot_path || *string == '/') {
117 result = strdup( string );
118 if (result == NULL) {
119 return NULL; /* couldn't allocate memory */
122 if (dot_path && dot_path[0]) {
123 result = malloc( 2 + strlen( dot_path ) + strlen( string ) );
124 if (result == NULL) {
125 return NULL; /* couldn't allocate memory */
127 strcpy( result, dot_path );
128 result_len = (int)strlen(result);
129 if (result[result_len - 1] != '/') {
130 result[result_len++] = '/';
131 result[result_len] = '\0';
134 result = malloc( 3 + strlen( string ) );
135 if (result == NULL) {
136 return NULL; /* couldn't allocate memory */
138 result[0] = '.'; result[1] = '/'; result[2] = '\0';
142 strcpy( result + result_len, string );
149 * Canonicalize PATH, and return a new path. The new path differs from
152 * Multiple `/'s are collapsed to a single `/'.
153 * Leading `./'s are removed.
154 * Trailing `/.'s are removed.
155 * Trailing `/'s are removed.
156 * Non-leading `../'s and trailing `..'s are handled by removing
157 * portions of the path.
160 canonicalize_pathname( char *path )
163 char stub_char, *result;
165 /* The result cannot be larger than the input PATH. */
166 result = strdup( path );
167 if (result == NULL) {
168 return NULL; /* couldn't allocate memory */
170 stub_char = (*path == '/') ? '/' : '.';
172 /* Walk along RESULT looking for things to compact. */
175 while (result[i] != '\0' && result[i] != '/')
180 /* If we didn't find any slashes, then there is nothing left to
186 /* Handle multiple `/'s in a row. */
187 while (result[i] == '/')
190 #if !defined (apollo)
191 if ((start + 1) != i)
193 if ((start + 1) != i && (start != 0 || i != 2))
196 strcpy( result + start + 1, result + i );
200 /* Handle backquoted `/'. */
201 if (start > 0 && result[start - 1] == '\\')
204 /* Check for trailing `/', and `.' by itself. */
205 if ((start && !result[i])
206 || (result[i] == '.' && !result[i+1])) {
211 /* Check for `../', `./' or trailing `.' by itself. */
212 if (result[i] == '.') {
214 if (result[i + 1] == '/') {
215 strcpy( result + i, result + i + 1 );
216 i = (start < 0) ? 0 : start;
220 /* Handle `../' or trailing `..' by itself. */
221 if (result[i + 1] == '.' &&
222 (result[i + 2] == '/' || !result[i + 2])) {
223 while (--start > -1 && result[start] != '/')
225 strcpy( result + start + 1, result + i + 2 );
226 i = (start < 0) ? 0 : start;
241 * Given a string containing units of information separated by colons,
242 * return the next one pointed to by (P_INDEX), or NULL if there are no
243 * more. Advance (P_INDEX) to the character after the colon.
246 extract_colon_unit(char * pzDir, char const * string, int * p_index)
248 char * pzDest = pzDir;
254 if ((unsigned)ix >= strlen( string ))
258 char const * pzSrc = string + ix;
260 while (*pzSrc == ':') pzSrc++;
263 char ch = (*(pzDest++) = *(pzSrc++));
272 if ((unsigned long)(pzDest - pzDir) >= AG_PATH_MAX)
276 ix = (int)(pzSrc - string);
285 #endif /* __windows__ / __CYGWIN__ */
286 #endif /* HAVE_PATHFIND */
291 * c-file-style: "stroustrup"
292 * indent-tabs-mode: nil
294 * end of compat/pathfind.c */