Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / ntp / sntp / libopts / compat / pathfind.c
blob8f2428070052e2f90faf607b0fbb447851e47eba
1 /* $NetBSD$ */
3 /* -*- Mode: C -*- */
5 /* pathfind.c --- find a FILE MODE along PATH */
7 /*
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 $
12 * by: bkorb
14 * Id: pathfind.c,v 4.10 2006/11/27 01:52:23 bkorb Exp
17 /* Code: */
19 #include "compat.h"
20 #ifndef HAVE_PATHFIND
21 #if defined(__windows__) && !defined(__CYGWIN__)
22 char*
23 pathfind( char const* path,
24 char const* fileName,
25 char const* mode )
27 return NULL;
29 #else
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 +
46 * ret_type: char*
47 * ret_desc: the path to the located file
49 * doc:
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
60 * list below:
61 * @example
62 * Letter Meaning
63 * r readable
64 * w writable
65 * x executable
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)
75 * @end example
77 * example:
78 * To find the "ls" command using the "PATH" environment variable:
79 * @example
80 * #include <stdlib.h>
81 * char* pz_ls = pathfind( getenv("PATH"), "ls", "rx" );
82 * <<do whatever with pz_ls>>
83 * free( pz_ls );
84 * @end example
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.
89 =*/
90 char*
91 pathfind( char const* path,
92 char const* fileName,
93 char const* mode )
95 int p_index = 0;
96 int mode_bits = 0;
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 ...
107 for (;;) {
108 DIR* dirP;
109 char* colon_unit = extract_colon_unit( zPath, path, &p_index );
112 * IF no more entries, THEN quit
114 if (colon_unit == NULL)
115 break;
117 dirP = opendir( colon_unit );
120 * IF the directory is inaccessable, THEN next directory
122 if (dirP == NULL)
123 continue;
126 * FOR every entry in the given directory, ...
128 for (;;) {
129 struct dirent *entP = readdir( dirP );
131 if (entP == (struct dirent*)NULL)
132 break;
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 );
151 break;
155 closedir( dirP );
157 if (pathName != NULL)
158 break;
161 return pathName;
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.
169 static char*
170 make_absolute( char const *string, char const *dot_path )
172 char *result;
173 int result_len;
175 if (!dot_path || *string == '/') {
176 result = strdup( string );
177 } else {
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';
186 } else {
187 result = malloc( 3 + strlen( string ) );
188 result[0] = '.'; result[1] = '/'; result[2] = '\0';
189 result_len = 2;
192 strcpy( result + result_len, string );
195 return result;
199 * Canonicalize PATH, and return a new path. The new path differs from
200 * PATH in that:
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.
209 static char*
210 canonicalize_pathname( char *path )
212 int i, start;
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. */
221 i = 0;
222 while (result[i]) {
223 while (result[i] != '\0' && result[i] != '/')
224 i++;
226 start = i++;
228 /* If we didn't find any slashes, then there is nothing left to
229 * do.
231 if (!result[start])
232 break;
234 /* Handle multiple `/'s in a row. */
235 while (result[i] == '/')
236 i++;
238 #if !defined (apollo)
239 if ((start + 1) != i)
240 #else
241 if ((start + 1) != i && (start != 0 || i != 2))
242 #endif /* apollo */
244 strcpy( result + start + 1, result + i );
245 i = start + 1;
248 /* Handle backquoted `/'. */
249 if (start > 0 && result[start - 1] == '\\')
250 continue;
252 /* Check for trailing `/', and `.' by itself. */
253 if ((start && !result[i])
254 || (result[i] == '.' && !result[i+1])) {
255 result[--i] = '\0';
256 break;
259 /* Check for `../', `./' or trailing `.' by itself. */
260 if (result[i] == '.') {
261 /* Handle `./'. */
262 if (result[i + 1] == '/') {
263 strcpy( result + i, result + i + 1 );
264 i = (start < 0) ? 0 : start;
265 continue;
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;
275 continue;
280 if (!*result) {
281 *result = stub_char;
282 result[1] = '\0';
285 return result;
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.
293 static char*
294 extract_colon_unit( char* pzDir, char const *string, int *p_index )
296 char* pzDest = pzDir;
297 int ix = *p_index;
299 if (string == NULL)
300 return NULL;
302 if ((unsigned)ix >= strlen( string ))
303 return NULL;
306 char const* pzSrc = string + ix;
308 while (*pzSrc == ':') pzSrc++;
310 for (;;) {
311 char ch = (*(pzDest++) = *(pzSrc++));
312 switch (ch) {
313 case ':':
314 pzDest[-1] = NUL;
315 case NUL:
316 goto copy_done;
319 if ((pzDest - pzDir) >= AG_PATH_MAX)
320 break;
321 } copy_done:;
323 ix = pzSrc - string;
326 if (*pzDir == NUL)
327 return NULL;
329 *p_index = ix;
330 return pzDir;
332 #endif /* __windows__ / __CYGWIN__ */
333 #endif /* HAVE_PATHFIND */
336 * Local Variables:
337 * mode: C
338 * c-file-style: "stroustrup"
339 * indent-tabs-mode: nil
340 * End:
341 * end of compat/pathfind.c */