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