5 /* pathfind.c --- find a FILE MODE along PATH */
8 * Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk>
9 * Time-stamp: "2006-09-23 19:46:16 bkorb"
10 * Created: Tue Jun 24 15:07:31 1997
11 * Last Modified: $Date: 2007/06/24 15:51:26 $
14 * Id: pathfind.c,v 4.10 2006/11/27 01:52:23 bkorb Exp
21 #if defined(__windows__) && !defined(__CYGWIN__)
23 pathfind( char const* path
,
31 static char* make_absolute( char const *string
, char const *dot_path
);
32 static char* canonicalize_pathname( char *path
);
33 static char* extract_colon_unit( char* dir
, char const *string
, int *p_index
);
36 /*=export_func pathfind
38 * what: fild a file in a list of directories
40 * ifndef: HAVE_PATHFIND
42 * arg: + char const* + path + colon separated list of search directories +
43 * arg: + char const* + file + the name of the file to look for +
44 * arg: + char const* + mode + the mode bits that must be set to match +
47 * ret_desc: the path to the located file
51 * pathfind looks for a a file with name "FILE" and "MODE" access
52 * along colon delimited "PATH", and returns the full pathname as a
53 * string, or NULL if not found. If "FILE" contains a slash, then
54 * it is treated as a relative or absolute path and "PATH" is ignored.
56 * @strong{NOTE}: this function is compiled into @file{libopts} only if
57 * it is not natively supplied.
59 * The "MODE" argument is a string of option letters chosen from the
66 * f normal file (NOT IMPLEMENTED)
67 * b block special (NOT IMPLEMENTED)
68 * c character special (NOT IMPLEMENTED)
69 * d directory (NOT IMPLEMENTED)
70 * p FIFO (pipe) (NOT IMPLEMENTED)
71 * u set user ID bit (NOT IMPLEMENTED)
72 * g set group ID bit (NOT IMPLEMENTED)
73 * k sticky bit (NOT IMPLEMENTED)
74 * s size nonzero (NOT IMPLEMENTED)
78 * To find the "ls" command using the "PATH" environment variable:
81 * char* pz_ls = pathfind( getenv("PATH"), "ls", "rx" );
82 * <<do whatever with pz_ls>>
85 * The path is allocated with @code{malloc(3C)}, so you must @code{free(3C)}
86 * the result. Also, do not use unimplemented file modes. :-)
88 * err: returns NULL if the file is not found.
91 pathfind( char const* path
,
97 char* pathName
= NULL
;
98 char zPath
[ AG_PATH_MAX
+ 1 ];
100 if (strchr( mode
, 'r' )) mode_bits
|= R_OK
;
101 if (strchr( mode
, 'w' )) mode_bits
|= W_OK
;
102 if (strchr( mode
, 'x' )) mode_bits
|= X_OK
;
105 * FOR each non-null entry in the colon-separated path, DO ...
109 char* colon_unit
= extract_colon_unit( zPath
, path
, &p_index
);
112 * IF no more entries, THEN quit
114 if (colon_unit
== NULL
)
117 dirP
= opendir( colon_unit
);
120 * IF the directory is inaccessable, THEN next directory
126 * FOR every entry in the given directory, ...
129 struct dirent
*entP
= readdir( dirP
);
131 if (entP
== (struct dirent
*)NULL
)
135 * IF the file name matches the one we are looking for, ...
137 if (strcmp( entP
->d_name
, fileName
) == 0) {
138 char* pzFullName
= make_absolute( fileName
, colon_unit
);
141 * Make sure we can access it in the way we want
143 if (access( pzFullName
, mode_bits
) >= 0) {
145 * We can, so normalize the name and return it below
147 pathName
= canonicalize_pathname( pzFullName
);
150 free( (void*)pzFullName
);
157 if (pathName
!= NULL
)
165 * Turn STRING (a pathname) into an absolute pathname, assuming that
166 * DOT_PATH contains the symbolic location of `.'. This always returns
167 * a new string, even if STRING was an absolute pathname to begin with.
170 make_absolute( char const *string
, char const *dot_path
)
175 if (!dot_path
|| *string
== '/') {
176 result
= strdup( string
);
178 if (dot_path
&& dot_path
[0]) {
179 result
= malloc( 2 + strlen( dot_path
) + strlen( string
) );
180 strcpy( result
, dot_path
);
181 result_len
= strlen( result
);
182 if (result
[result_len
- 1] != '/') {
183 result
[result_len
++] = '/';
184 result
[result_len
] = '\0';
187 result
= malloc( 3 + strlen( string
) );
188 result
[0] = '.'; result
[1] = '/'; result
[2] = '\0';
192 strcpy( result
+ result_len
, string
);
199 * Canonicalize PATH, and return a new path. The new path differs from
202 * Multiple `/'s are collapsed to a single `/'.
203 * Leading `./'s are removed.
204 * Trailing `/.'s are removed.
205 * Trailing `/'s are removed.
206 * Non-leading `../'s and trailing `..'s are handled by removing
207 * portions of the path.
210 canonicalize_pathname( char *path
)
213 char stub_char
, *result
;
215 /* The result cannot be larger than the input PATH. */
216 result
= strdup( path
);
218 stub_char
= (*path
== '/') ? '/' : '.';
220 /* Walk along RESULT looking for things to compact. */
223 while (result
[i
] != '\0' && result
[i
] != '/')
228 /* If we didn't find any slashes, then there is nothing left to
234 /* Handle multiple `/'s in a row. */
235 while (result
[i
] == '/')
238 #if !defined (apollo)
239 if ((start
+ 1) != i
)
241 if ((start
+ 1) != i
&& (start
!= 0 || i
!= 2))
244 strcpy( result
+ start
+ 1, result
+ i
);
248 /* Handle backquoted `/'. */
249 if (start
> 0 && result
[start
- 1] == '\\')
252 /* Check for trailing `/', and `.' by itself. */
253 if ((start
&& !result
[i
])
254 || (result
[i
] == '.' && !result
[i
+1])) {
259 /* Check for `../', `./' or trailing `.' by itself. */
260 if (result
[i
] == '.') {
262 if (result
[i
+ 1] == '/') {
263 strcpy( result
+ i
, result
+ i
+ 1 );
264 i
= (start
< 0) ? 0 : start
;
268 /* Handle `../' or trailing `..' by itself. */
269 if (result
[i
+ 1] == '.' &&
270 (result
[i
+ 2] == '/' || !result
[i
+ 2])) {
271 while (--start
> -1 && result
[start
] != '/')
273 strcpy( result
+ start
+ 1, result
+ i
+ 2 );
274 i
= (start
< 0) ? 0 : start
;
289 * Given a string containing units of information separated by colons,
290 * return the next one pointed to by (P_INDEX), or NULL if there are no
291 * more. Advance (P_INDEX) to the character after the colon.
294 extract_colon_unit( char* pzDir
, char const *string
, int *p_index
)
296 char* pzDest
= pzDir
;
302 if ((unsigned)ix
>= strlen( string
))
306 char const* pzSrc
= string
+ ix
;
308 while (*pzSrc
== ':') pzSrc
++;
311 char ch
= (*(pzDest
++) = *(pzSrc
++));
319 if ((pzDest
- pzDir
) >= AG_PATH_MAX
)
332 #endif /* __windows__ / __CYGWIN__ */
333 #endif /* HAVE_PATHFIND */
338 * c-file-style: "stroustrup"
339 * indent-tabs-mode: nil
341 * end of compat/pathfind.c */