xref: /freebsd/contrib/ntp/sntp/libopts/compat/pathfind.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
1ea906c41SOllivier Robert /*  -*- Mode: C -*-  */
2ea906c41SOllivier Robert 
3ea906c41SOllivier Robert /* pathfind.c --- find a FILE  MODE along PATH */
4ea906c41SOllivier Robert 
52b15cb3dSCy Schubert /* Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk> */
6ea906c41SOllivier Robert 
7ea906c41SOllivier Robert /* Code: */
8ea906c41SOllivier Robert 
92b15cb3dSCy Schubert static char *
102b15cb3dSCy Schubert pathfind( char const * path,
112b15cb3dSCy Schubert           char const * fname,
122b15cb3dSCy Schubert           char const * mode );
132b15cb3dSCy Schubert 
14ea906c41SOllivier Robert #include "compat.h"
15ea906c41SOllivier Robert #ifndef HAVE_PATHFIND
16ea906c41SOllivier Robert #if defined(__windows__) && !defined(__CYGWIN__)
172b15cb3dSCy Schubert static char *
pathfind(char const * path,char const * fname,char const * mode)18ea906c41SOllivier Robert pathfind( char const * path,
192b15cb3dSCy Schubert           char const * fname,
20ea906c41SOllivier Robert           char const * mode )
21ea906c41SOllivier Robert {
222b15cb3dSCy 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 
302b15cb3dSCy Schubert /**
312b15cb3dSCy Schubert  * local implementation of pathfind.
322b15cb3dSCy Schubert  * @param[in] path  colon separated list of directories
332b15cb3dSCy Schubert  * @param[in] fname the name we are hunting for
342b15cb3dSCy Schubert  * @param[in] mode  the required file mode
352b15cb3dSCy Schubert  * @returns an allocated string with the full path, or NULL
362b15cb3dSCy Schubert  */
372b15cb3dSCy Schubert static char *
pathfind(char const * path,char const * fname,char const * mode)38ea906c41SOllivier Robert pathfind( char const * path,
392b15cb3dSCy Schubert           char const * fname,
40ea906c41SOllivier Robert           char const * mode )
41ea906c41SOllivier Robert {
42ea906c41SOllivier Robert     int    p_index   = 0;
43ea906c41SOllivier Robert     int    mode_bits = 0;
442b15cb3dSCy 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              */
782b15cb3dSCy Schubert             if (strcmp(entP->d_name, fname) == 0) {
792b15cb3dSCy 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                  */
842b15cb3dSCy Schubert                 if (access(abs_name, mode_bits) >= 0) {
85ea906c41SOllivier Robert                     /*
86ea906c41SOllivier Robert                      *  We can, so normalize the name and return it below
87ea906c41SOllivier Robert                      */
882b15cb3dSCy Schubert                     res_path = canonicalize_pathname(abs_name);
89ea906c41SOllivier Robert                 }
90ea906c41SOllivier Robert 
912b15cb3dSCy Schubert                 free(abs_name);
92ea906c41SOllivier Robert                 break;
93ea906c41SOllivier Robert             }
94ea906c41SOllivier Robert         }
95ea906c41SOllivier Robert 
96ea906c41SOllivier Robert         closedir( dirP );
97ea906c41SOllivier Robert 
982b15cb3dSCy Schubert         if (res_path != NULL)
99ea906c41SOllivier Robert             break;
100ea906c41SOllivier Robert     }
101ea906c41SOllivier Robert 
1022b15cb3dSCy 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 *
make_absolute(char const * string,char const * dot_path)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 );
1222b15cb3dSCy 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 *
canonicalize_pathname(char * path)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 );
158*a466cc55SCy Schubert 
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 *
extract_colon_unit(char * pzDir,char const * string,int * p_index)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;
2562b15cb3dSCy Schubert                 /* FALLTHROUGH */
257ea906c41SOllivier Robert             case NUL:
258ea906c41SOllivier Robert                 goto copy_done;
259ea906c41SOllivier Robert             }
260ea906c41SOllivier Robert 
2612b15cb3dSCy Schubert             if ((unsigned long)(pzDest - pzDir) >= AG_PATH_MAX)
262ea906c41SOllivier Robert                 break;
263ea906c41SOllivier Robert         } copy_done:;
264ea906c41SOllivier Robert 
2652b15cb3dSCy 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