xref: /netbsd-src/external/bsd/ntp/dist/sntp/libopts/compat/pathfind.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: pathfind.c,v 1.9 2024/08/18 20:47:25 christos Exp $	*/
2abb0f93cSkardel 
3abb0f93cSkardel /*  -*- Mode: C -*-  */
4abb0f93cSkardel 
5abb0f93cSkardel /* pathfind.c --- find a FILE  MODE along PATH */
6abb0f93cSkardel 
72950cc38Schristos /* Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk> */
8abb0f93cSkardel 
9abb0f93cSkardel /* Code: */
10abb0f93cSkardel 
112950cc38Schristos static char *
122950cc38Schristos pathfind( char const * path,
132950cc38Schristos           char const * fname,
142950cc38Schristos           char const * mode );
152950cc38Schristos 
16abb0f93cSkardel #include "compat.h"
17abb0f93cSkardel #ifndef HAVE_PATHFIND
18abb0f93cSkardel #if defined(__windows__) && !defined(__CYGWIN__)
192950cc38Schristos static char *
20abb0f93cSkardel pathfind( char const * path,
212950cc38Schristos           char const * fname,
22abb0f93cSkardel           char const * mode )
23abb0f93cSkardel {
242950cc38Schristos     return strdup(fname);
25abb0f93cSkardel }
26abb0f93cSkardel #else
27abb0f93cSkardel 
28abb0f93cSkardel static char * make_absolute(char const * string, char const * dot_path);
29abb0f93cSkardel static char * canonicalize_pathname(char * path);
30abb0f93cSkardel static char * extract_colon_unit(char * dir, char const * string, int * p_index);
31abb0f93cSkardel 
322950cc38Schristos /**
332950cc38Schristos  * local implementation of pathfind.
342950cc38Schristos  * @param[in] path  colon separated list of directories
352950cc38Schristos  * @param[in] fname the name we are hunting for
362950cc38Schristos  * @param[in] mode  the required file mode
372950cc38Schristos  * @returns an allocated string with the full path, or NULL
382950cc38Schristos  */
392950cc38Schristos static char *
40abb0f93cSkardel pathfind( char const * path,
412950cc38Schristos           char const * fname,
42abb0f93cSkardel           char const * mode )
43abb0f93cSkardel {
44abb0f93cSkardel     int    p_index   = 0;
45abb0f93cSkardel     int    mode_bits = 0;
462950cc38Schristos     char * res_path  = NULL;
47abb0f93cSkardel     char   zPath[ AG_PATH_MAX + 1 ];
48abb0f93cSkardel 
49abb0f93cSkardel     if (strchr( mode, 'r' )) mode_bits |= R_OK;
50abb0f93cSkardel     if (strchr( mode, 'w' )) mode_bits |= W_OK;
51abb0f93cSkardel     if (strchr( mode, 'x' )) mode_bits |= X_OK;
52abb0f93cSkardel 
53abb0f93cSkardel     /*
54abb0f93cSkardel      *  FOR each non-null entry in the colon-separated path, DO ...
55abb0f93cSkardel      */
56abb0f93cSkardel     for (;;) {
57abb0f93cSkardel         DIR  * dirP;
58abb0f93cSkardel         char * colon_unit = extract_colon_unit( zPath, path, &p_index );
59abb0f93cSkardel 
60abb0f93cSkardel         if (colon_unit == NULL)
61abb0f93cSkardel             break;
62abb0f93cSkardel 
63abb0f93cSkardel         dirP = opendir( colon_unit );
64abb0f93cSkardel 
65abb0f93cSkardel         /*
66abb0f93cSkardel          *  IF the directory is inaccessable, THEN next directory
67abb0f93cSkardel          */
68abb0f93cSkardel         if (dirP == NULL)
69abb0f93cSkardel             continue;
70abb0f93cSkardel 
71abb0f93cSkardel         for (;;) {
72abb0f93cSkardel             struct dirent *entP = readdir( dirP );
73abb0f93cSkardel 
74abb0f93cSkardel             if (entP == (struct dirent *)NULL)
75abb0f93cSkardel                 break;
76abb0f93cSkardel 
77abb0f93cSkardel             /*
78abb0f93cSkardel              *  IF the file name matches the one we are looking for, ...
79abb0f93cSkardel              */
802950cc38Schristos             if (strcmp(entP->d_name, fname) == 0) {
812950cc38Schristos                 char * abs_name = make_absolute(fname, colon_unit);
82abb0f93cSkardel 
83abb0f93cSkardel                 /*
84abb0f93cSkardel                  *  Make sure we can access it in the way we want
85abb0f93cSkardel                  */
862950cc38Schristos                 if (access(abs_name, mode_bits) >= 0) {
87abb0f93cSkardel                     /*
88abb0f93cSkardel                      *  We can, so normalize the name and return it below
89abb0f93cSkardel                      */
902950cc38Schristos                     res_path = canonicalize_pathname(abs_name);
91abb0f93cSkardel                 }
92abb0f93cSkardel 
932950cc38Schristos                 free(abs_name);
94abb0f93cSkardel                 break;
95abb0f93cSkardel             }
96abb0f93cSkardel         }
97abb0f93cSkardel 
98abb0f93cSkardel         closedir( dirP );
99abb0f93cSkardel 
1002950cc38Schristos         if (res_path != NULL)
101abb0f93cSkardel             break;
102abb0f93cSkardel     }
103abb0f93cSkardel 
1042950cc38Schristos     return res_path;
105abb0f93cSkardel }
106abb0f93cSkardel 
107abb0f93cSkardel /*
108abb0f93cSkardel  * Turn STRING  (a pathname) into an  absolute  pathname, assuming  that
109abb0f93cSkardel  * DOT_PATH contains the symbolic location of  `.'.  This always returns
110abb0f93cSkardel  * a new string, even if STRING was an absolute pathname to begin with.
111abb0f93cSkardel  */
112abb0f93cSkardel static char *
113abb0f93cSkardel make_absolute( char const * string, char const * dot_path )
114abb0f93cSkardel {
115abb0f93cSkardel     char * result;
116abb0f93cSkardel     int result_len;
117abb0f93cSkardel 
118abb0f93cSkardel     if (!dot_path || *string == '/') {
119abb0f93cSkardel         result = strdup( string );
120abb0f93cSkardel     } else {
121abb0f93cSkardel         if (dot_path && dot_path[0]) {
122abb0f93cSkardel             result = malloc( 2 + strlen( dot_path ) + strlen( string ) );
123abb0f93cSkardel             strcpy( result, dot_path );
1242950cc38Schristos             result_len = (int)strlen(result);
125abb0f93cSkardel             if (result[result_len - 1] != '/') {
126abb0f93cSkardel                 result[result_len++] = '/';
127abb0f93cSkardel                 result[result_len] = '\0';
128abb0f93cSkardel             }
129abb0f93cSkardel         } else {
130abb0f93cSkardel             result = malloc( 3 + strlen( string ) );
131abb0f93cSkardel             result[0] = '.'; result[1] = '/'; result[2] = '\0';
132abb0f93cSkardel             result_len = 2;
133abb0f93cSkardel         }
134abb0f93cSkardel 
135abb0f93cSkardel         strcpy( result + result_len, string );
136abb0f93cSkardel     }
137abb0f93cSkardel 
138abb0f93cSkardel     return result;
139abb0f93cSkardel }
140abb0f93cSkardel 
141abb0f93cSkardel /*
142abb0f93cSkardel  * Canonicalize PATH, and return a  new path.  The new path differs from
143abb0f93cSkardel  * PATH in that:
144abb0f93cSkardel  *
145abb0f93cSkardel  *    Multiple `/'s     are collapsed to a single `/'.
146abb0f93cSkardel  *    Leading `./'s     are removed.
147abb0f93cSkardel  *    Trailing `/.'s    are removed.
148abb0f93cSkardel  *    Trailing `/'s     are removed.
149abb0f93cSkardel  *    Non-leading `../'s and trailing `..'s are handled by removing
150abb0f93cSkardel  *                    portions of the path.
151abb0f93cSkardel  */
152abb0f93cSkardel static char *
153abb0f93cSkardel canonicalize_pathname( char *path )
154abb0f93cSkardel {
155abb0f93cSkardel     int i, start;
156abb0f93cSkardel     char stub_char, *result;
157abb0f93cSkardel 
158abb0f93cSkardel     /* The result cannot be larger than the input PATH. */
159abb0f93cSkardel     result = strdup( path );
160*eabc0478Schristos 
161abb0f93cSkardel     stub_char = (*path == '/') ? '/' : '.';
162abb0f93cSkardel 
163abb0f93cSkardel     /* Walk along RESULT looking for things to compact. */
164abb0f93cSkardel     i = 0;
165abb0f93cSkardel     while (result[i]) {
166abb0f93cSkardel         while (result[i] != '\0' && result[i] != '/')
167abb0f93cSkardel             i++;
168abb0f93cSkardel 
169abb0f93cSkardel         start = i++;
170abb0f93cSkardel 
171abb0f93cSkardel         /* If we didn't find any  slashes, then there is nothing left to
172abb0f93cSkardel          * do.
173abb0f93cSkardel          */
174abb0f93cSkardel         if (!result[start])
175abb0f93cSkardel             break;
176abb0f93cSkardel 
177abb0f93cSkardel         /* Handle multiple `/'s in a row. */
178abb0f93cSkardel         while (result[i] == '/')
179abb0f93cSkardel             i++;
180abb0f93cSkardel 
181abb0f93cSkardel #if !defined (apollo)
182abb0f93cSkardel         if ((start + 1) != i)
183abb0f93cSkardel #else
184abb0f93cSkardel         if ((start + 1) != i && (start != 0 || i != 2))
185abb0f93cSkardel #endif /* apollo */
186abb0f93cSkardel         {
187abb0f93cSkardel             strcpy( result + start + 1, result + i );
188abb0f93cSkardel             i = start + 1;
189abb0f93cSkardel         }
190abb0f93cSkardel 
191abb0f93cSkardel         /* Handle backquoted `/'. */
192abb0f93cSkardel         if (start > 0 && result[start - 1] == '\\')
193abb0f93cSkardel             continue;
194abb0f93cSkardel 
195abb0f93cSkardel         /* Check for trailing `/', and `.' by itself. */
196abb0f93cSkardel         if ((start && !result[i])
197abb0f93cSkardel             || (result[i] == '.' && !result[i+1])) {
198abb0f93cSkardel             result[--i] = '\0';
199abb0f93cSkardel             break;
200abb0f93cSkardel         }
201abb0f93cSkardel 
202abb0f93cSkardel         /* Check for `../', `./' or trailing `.' by itself. */
203abb0f93cSkardel         if (result[i] == '.') {
204abb0f93cSkardel             /* Handle `./'. */
205abb0f93cSkardel             if (result[i + 1] == '/') {
206abb0f93cSkardel                 strcpy( result + i, result + i + 1 );
207abb0f93cSkardel                 i = (start < 0) ? 0 : start;
208abb0f93cSkardel                 continue;
209abb0f93cSkardel             }
210abb0f93cSkardel 
211abb0f93cSkardel             /* Handle `../' or trailing `..' by itself. */
212abb0f93cSkardel             if (result[i + 1] == '.' &&
213abb0f93cSkardel                 (result[i + 2] == '/' || !result[i + 2])) {
214abb0f93cSkardel                 while (--start > -1 && result[start] != '/')
215abb0f93cSkardel                     ;
216abb0f93cSkardel                 strcpy( result + start + 1, result + i + 2 );
217abb0f93cSkardel                 i = (start < 0) ? 0 : start;
218abb0f93cSkardel                 continue;
219abb0f93cSkardel             }
220abb0f93cSkardel         }
221abb0f93cSkardel     }
222abb0f93cSkardel 
223abb0f93cSkardel     if (!*result) {
224abb0f93cSkardel         *result = stub_char;
225abb0f93cSkardel         result[1] = '\0';
226abb0f93cSkardel     }
227abb0f93cSkardel 
228abb0f93cSkardel     return result;
229abb0f93cSkardel }
230abb0f93cSkardel 
231abb0f93cSkardel /*
232abb0f93cSkardel  * Given a  string containing units of information separated  by colons,
233abb0f93cSkardel  * return the next one  pointed to by (P_INDEX), or NULL if there are no
234abb0f93cSkardel  * more.  Advance (P_INDEX) to the character after the colon.
235abb0f93cSkardel  */
236abb0f93cSkardel static char *
237abb0f93cSkardel extract_colon_unit(char * pzDir, char const * string, int * p_index)
238abb0f93cSkardel {
239abb0f93cSkardel     char * pzDest = pzDir;
240abb0f93cSkardel     int    ix     = *p_index;
241abb0f93cSkardel 
242abb0f93cSkardel     if (string == NULL)
243abb0f93cSkardel         return NULL;
244abb0f93cSkardel 
245abb0f93cSkardel     if ((unsigned)ix >= strlen( string ))
246abb0f93cSkardel         return NULL;
247abb0f93cSkardel 
248abb0f93cSkardel     {
249abb0f93cSkardel         char const * pzSrc = string + ix;
250abb0f93cSkardel 
251abb0f93cSkardel         while (*pzSrc == ':')  pzSrc++;
252abb0f93cSkardel 
253abb0f93cSkardel         for (;;) {
254abb0f93cSkardel             char ch = (*(pzDest++) = *(pzSrc++));
255abb0f93cSkardel             switch (ch) {
256abb0f93cSkardel             case ':':
257abb0f93cSkardel                 pzDest[-1] = NUL;
2582950cc38Schristos                 /* FALLTHROUGH */
259abb0f93cSkardel             case NUL:
260abb0f93cSkardel                 goto copy_done;
261abb0f93cSkardel             }
262abb0f93cSkardel 
2632950cc38Schristos             if ((unsigned long)(pzDest - pzDir) >= AG_PATH_MAX)
264abb0f93cSkardel                 break;
265abb0f93cSkardel         } copy_done:;
266abb0f93cSkardel 
2672950cc38Schristos         ix = (int)(pzSrc - string);
268abb0f93cSkardel     }
269abb0f93cSkardel 
270abb0f93cSkardel     if (*pzDir == NUL)
271abb0f93cSkardel         return NULL;
272abb0f93cSkardel 
273abb0f93cSkardel     *p_index = ix;
274abb0f93cSkardel     return pzDir;
275abb0f93cSkardel }
276abb0f93cSkardel #endif /* __windows__ / __CYGWIN__ */
277abb0f93cSkardel #endif /* HAVE_PATHFIND */
278abb0f93cSkardel 
279abb0f93cSkardel /*
280abb0f93cSkardel  * Local Variables:
281abb0f93cSkardel  * mode: C
282abb0f93cSkardel  * c-file-style: "stroustrup"
283abb0f93cSkardel  * indent-tabs-mode: nil
284abb0f93cSkardel  * End:
285abb0f93cSkardel  * end of compat/pathfind.c */
286