Annotation of embedaddon/ntp/sntp/libopts/compat/pathfind.c, revision 1.1.1.1
1.1 misho 1: /* -*- Mode: C -*- */
2:
3: /* pathfind.c --- find a FILE MODE along PATH */
4:
5: /*
6: * Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk>
7: * Time-stamp: "2010-07-17 09:50:32 bkorb"
8: */
9:
10: /* Code: */
11:
12: #include "compat.h"
13: #ifndef HAVE_PATHFIND
14: #if defined(__windows__) && !defined(__CYGWIN__)
15: char*
16: pathfind( char const* path,
17: char const* fileName,
18: char const* mode )
19: {
20: return NULL;
21: }
22: #else
23:
24: static char* make_absolute( char const *string, char const *dot_path );
25: static char* canonicalize_pathname( char *path );
26: static char* extract_colon_unit( char* dir, char const *string, int *p_index );
27:
28:
29: /*=export_func pathfind
30: *
31: * what: fild a file in a list of directories
32: *
33: * ifndef: HAVE_PATHFIND
34: *
35: * arg: + char const* + path + colon separated list of search directories +
36: * arg: + char const* + file + the name of the file to look for +
37: * arg: + char const* + mode + the mode bits that must be set to match +
38: *
39: * ret_type: char*
40: * ret_desc: the path to the located file
41: *
42: * doc:
43: *
44: * pathfind looks for a a file with name "FILE" and "MODE" access
45: * along colon delimited "PATH", and returns the full pathname as a
46: * string, or NULL if not found. If "FILE" contains a slash, then
47: * it is treated as a relative or absolute path and "PATH" is ignored.
48: *
49: * @strong{NOTE}: this function is compiled into @file{libopts} only if
50: * it is not natively supplied.
51: *
52: * The "MODE" argument is a string of option letters chosen from the
53: * list below:
54: * @example
55: * Letter Meaning
56: * r readable
57: * w writable
58: * x executable
59: * f normal file (NOT IMPLEMENTED)
60: * b block special (NOT IMPLEMENTED)
61: * c character special (NOT IMPLEMENTED)
62: * d directory (NOT IMPLEMENTED)
63: * p FIFO (pipe) (NOT IMPLEMENTED)
64: * u set user ID bit (NOT IMPLEMENTED)
65: * g set group ID bit (NOT IMPLEMENTED)
66: * k sticky bit (NOT IMPLEMENTED)
67: * s size nonzero (NOT IMPLEMENTED)
68: * @end example
69: *
70: * example:
71: * To find the "ls" command using the "PATH" environment variable:
72: * @example
73: * #include <stdlib.h>
74: * char* pz_ls = pathfind( getenv("PATH"), "ls", "rx" );
75: * <<do whatever with pz_ls>>
76: * free( pz_ls );
77: * @end example
78: * The path is allocated with @code{malloc(3C)}, so you must @code{free(3C)}
79: * the result. Also, do not use unimplemented file modes. :-)
80: *
81: * err: returns NULL if the file is not found.
82: =*/
83: char*
84: pathfind( char const* path,
85: char const* fileName,
86: char const* mode )
87: {
88: int p_index = 0;
89: int mode_bits = 0;
90: char* pathName = NULL;
91: char zPath[ AG_PATH_MAX + 1 ];
92:
93: if (strchr( mode, 'r' )) mode_bits |= R_OK;
94: if (strchr( mode, 'w' )) mode_bits |= W_OK;
95: if (strchr( mode, 'x' )) mode_bits |= X_OK;
96:
97: /*
98: * FOR each non-null entry in the colon-separated path, DO ...
99: */
100: for (;;) {
101: DIR* dirP;
102: char* colon_unit = extract_colon_unit( zPath, path, &p_index );
103:
104: /*
105: * IF no more entries, THEN quit
106: */
107: if (colon_unit == NULL)
108: break;
109:
110: dirP = opendir( colon_unit );
111:
112: /*
113: * IF the directory is inaccessable, THEN next directory
114: */
115: if (dirP == NULL)
116: continue;
117:
118: /*
119: * FOR every entry in the given directory, ...
120: */
121: for (;;) {
122: struct dirent *entP = readdir( dirP );
123:
124: if (entP == (struct dirent*)NULL)
125: break;
126:
127: /*
128: * IF the file name matches the one we are looking for, ...
129: */
130: if (strcmp( entP->d_name, fileName ) == 0) {
131: char* pzFullName = make_absolute( fileName, colon_unit);
132:
133: /*
134: * Make sure we can access it in the way we want
135: */
136: if (access( pzFullName, mode_bits ) >= 0) {
137: /*
138: * We can, so normalize the name and return it below
139: */
140: pathName = canonicalize_pathname( pzFullName );
141: }
142:
143: free( (void*)pzFullName );
144: break;
145: }
146: }
147:
148: closedir( dirP );
149:
150: if (pathName != NULL)
151: break;
152: }
153:
154: return pathName;
155: }
156:
157: /*
158: * Turn STRING (a pathname) into an absolute pathname, assuming that
159: * DOT_PATH contains the symbolic location of `.'. This always returns
160: * a new string, even if STRING was an absolute pathname to begin with.
161: */
162: static char*
163: make_absolute( char const *string, char const *dot_path )
164: {
165: char *result;
166: int result_len;
167:
168: if (!dot_path || *string == '/') {
169: result = strdup( string );
170: } else {
171: if (dot_path && dot_path[0]) {
172: result = malloc( 2 + strlen( dot_path ) + strlen( string ) );
173: strcpy( result, dot_path );
174: result_len = strlen( result );
175: if (result[result_len - 1] != '/') {
176: result[result_len++] = '/';
177: result[result_len] = '\0';
178: }
179: } else {
180: result = malloc( 3 + strlen( string ) );
181: result[0] = '.'; result[1] = '/'; result[2] = '\0';
182: result_len = 2;
183: }
184:
185: strcpy( result + result_len, string );
186: }
187:
188: return result;
189: }
190:
191: /*
192: * Canonicalize PATH, and return a new path. The new path differs from
193: * PATH in that:
194: *
195: * Multiple `/'s are collapsed to a single `/'.
196: * Leading `./'s are removed.
197: * Trailing `/.'s are removed.
198: * Trailing `/'s are removed.
199: * Non-leading `../'s and trailing `..'s are handled by removing
200: * portions of the path.
201: */
202: static char*
203: canonicalize_pathname( char *path )
204: {
205: int i, start;
206: char stub_char, *result;
207:
208: /* The result cannot be larger than the input PATH. */
209: result = strdup( path );
210:
211: stub_char = (*path == '/') ? '/' : '.';
212:
213: /* Walk along RESULT looking for things to compact. */
214: i = 0;
215: while (result[i]) {
216: while (result[i] != '\0' && result[i] != '/')
217: i++;
218:
219: start = i++;
220:
221: /* If we didn't find any slashes, then there is nothing left to
222: * do.
223: */
224: if (!result[start])
225: break;
226:
227: /* Handle multiple `/'s in a row. */
228: while (result[i] == '/')
229: i++;
230:
231: #if !defined (apollo)
232: if ((start + 1) != i)
233: #else
234: if ((start + 1) != i && (start != 0 || i != 2))
235: #endif /* apollo */
236: {
237: strcpy( result + start + 1, result + i );
238: i = start + 1;
239: }
240:
241: /* Handle backquoted `/'. */
242: if (start > 0 && result[start - 1] == '\\')
243: continue;
244:
245: /* Check for trailing `/', and `.' by itself. */
246: if ((start && !result[i])
247: || (result[i] == '.' && !result[i+1])) {
248: result[--i] = '\0';
249: break;
250: }
251:
252: /* Check for `../', `./' or trailing `.' by itself. */
253: if (result[i] == '.') {
254: /* Handle `./'. */
255: if (result[i + 1] == '/') {
256: strcpy( result + i, result + i + 1 );
257: i = (start < 0) ? 0 : start;
258: continue;
259: }
260:
261: /* Handle `../' or trailing `..' by itself. */
262: if (result[i + 1] == '.' &&
263: (result[i + 2] == '/' || !result[i + 2])) {
264: while (--start > -1 && result[start] != '/')
265: ;
266: strcpy( result + start + 1, result + i + 2 );
267: i = (start < 0) ? 0 : start;
268: continue;
269: }
270: }
271: }
272:
273: if (!*result) {
274: *result = stub_char;
275: result[1] = '\0';
276: }
277:
278: return result;
279: }
280:
281: /*
282: * Given a string containing units of information separated by colons,
283: * return the next one pointed to by (P_INDEX), or NULL if there are no
284: * more. Advance (P_INDEX) to the character after the colon.
285: */
286: static char*
287: extract_colon_unit( char* pzDir, char const *string, int *p_index )
288: {
289: char* pzDest = pzDir;
290: int ix = *p_index;
291:
292: if (string == NULL)
293: return NULL;
294:
295: if ((unsigned)ix >= strlen( string ))
296: return NULL;
297:
298: {
299: char const* pzSrc = string + ix;
300:
301: while (*pzSrc == ':') pzSrc++;
302:
303: for (;;) {
304: char ch = (*(pzDest++) = *(pzSrc++));
305: switch (ch) {
306: case ':':
307: pzDest[-1] = NUL;
308: case NUL:
309: goto copy_done;
310: }
311:
312: if ((pzDest - pzDir) >= AG_PATH_MAX)
313: break;
314: } copy_done:;
315:
316: ix = pzSrc - string;
317: }
318:
319: if (*pzDir == NUL)
320: return NULL;
321:
322: *p_index = ix;
323: return pzDir;
324: }
325: #endif /* __windows__ / __CYGWIN__ */
326: #endif /* HAVE_PATHFIND */
327:
328: /*
329: * Local Variables:
330: * mode: C
331: * c-file-style: "stroustrup"
332: * indent-tabs-mode: nil
333: * End:
334: * end of compat/pathfind.c */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>