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