1 /*
2  *
3  *   Gutenprint path functions - split and search paths.
4  *
5  *   Copyright 2002 Roger Leigh (rleigh@debian.org)
6  *
7  *   This program is free software; you can redistribute it and/or modify it
8  *   under the terms of the GNU General Public License as published by the Free
9  *   Software Foundation; either version 2 of the License, or (at your option)
10  *   any later version.
11  *
12  *   This program is distributed in the hope that it will be useful, but
13  *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  *   for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include <gutenprint/gutenprint.h>
22 #include "gutenprint-internal.h"
23 #include <gutenprint/gutenprint-intl-internal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <dirent.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 
33 static int stpi_path_check(const struct dirent *module,
34 			   const char *path,
35 			   const char *suffix);
36 static int stpi_scandir (const char *dir,
37 			 struct dirent ***namelist,
38 			 const char *path,
39 			 const char *suffix,
40 			 int (*sel) (const struct dirent *, const char *,
41 				     const char *),
42 			 int (*cmp) (const void *, const void *));
43 
44 static int
dirent_sort(const void * a,const void * b)45 dirent_sort(const void *a,
46 	    const void *b)
47 {
48   return strcoll ((*(const struct dirent * const *) a)->d_name,
49 		  (*(const struct dirent * const *) b)->d_name);
50 }
51 
52 
53 /*
54  * Make a list of all modules in the search path.
55  */
56 stp_list_t *
stp_path_search(stp_list_t * dirlist,const char * suffix)57 stp_path_search(stp_list_t *dirlist, /* List of directories to search */
58 		const char *suffix)  /* Required filename suffix */
59 {
60   stp_list_t *findlist;              /* List of files to return */
61   stp_list_item_t *diritem;          /* Current directory */
62   struct dirent** module_dir = NULL; /* Current directory contents */
63   char *module_name;                 /* File name to check */
64   int n;                             /* Number of directory entries */
65 
66   if (!dirlist)
67     return NULL;
68 
69   findlist = stp_list_create();
70   if (!findlist)
71     return NULL;
72   stp_list_set_freefunc(findlist, stp_list_node_free_data);
73 
74   diritem = stp_list_get_start(dirlist);
75   while (diritem)
76     {
77       const char *check_path = (const char *) stp_list_item_get_data(diritem);
78       stp_deprintf(STP_DBG_PATH, "stp-path: directory: %s\n",
79 		   (const char *) stp_list_item_get_data(diritem));
80       n = stpi_scandir ((const char *) stp_list_item_get_data(diritem),
81 			&module_dir, check_path, suffix,
82 			stpi_path_check, dirent_sort);
83       if (n >= 0)
84 	{
85 	  int idx;
86 	  for (idx = 0; idx < n; ++idx)
87 	    {
88 	      module_name = stpi_path_merge((const char *) stp_list_item_get_data(diritem),
89 					   module_dir[idx]->d_name);
90 	      stp_list_item_create(findlist, NULL, module_name);
91 	      free (module_dir[idx]); /* Must use plain free() */
92 	    }
93 	  free (module_dir); /* Must use plain free() */
94 	}
95       diritem = stp_list_item_next(diritem);
96     }
97   return findlist;
98 }
99 
100 
101 /*
102  * stpi_scandir() callback.  Check the filename is sane, has the
103  * correct mode bits and suffix.
104  */
105 static int
stpi_path_check(const struct dirent * module,const char * check_path,const char * check_suffix)106 stpi_path_check(const struct dirent *module, /* File to check */
107 		const char *check_path,	     /* Path to search */
108 		const char *check_suffix)    /* Suffix */
109 {
110   int namelen;                              /* Filename length */
111   int status = 0;                           /* Error status */
112   int savederr;                             /* Saved errno */
113   char *filename;                           /* Filename */
114   struct stat modstat;                      /* stat() output */
115 
116   savederr = errno; /* since we are a callback, preserve
117 		       stpi_scandir() state */
118 
119   filename = stpi_path_merge(check_path, module->d_name);
120 
121   namelen = strlen(filename);
122   /* make sure we can take off suffix (e.g. .la)
123      and still have a sane filename */
124   if (namelen >= strlen(check_suffix) + 1)
125     {
126       if (!stat (filename, &modstat))
127 	{
128 	  /* check file exists, and is a regular file */
129 	  if (S_ISREG(modstat.st_mode))
130 	    status = 1;
131 	  if (strncmp(filename + (namelen - strlen(check_suffix)),
132 		      check_suffix, strlen(check_suffix)))
133 	    {
134 	      status = 0;
135 	    }
136 	}
137     }
138 
139   if (status)
140     stp_deprintf(STP_DBG_PATH, "stp-path: file: `%s'\n", filename);
141 
142   stp_free(filename);
143   filename = NULL;
144 
145   errno = savederr;
146   return status;
147 }
148 
149 stp_list_t *
stp_generate_path(const char * path)150 stp_generate_path(const char *path)
151 {
152   stp_list_t *dir_list;                  /* List of directories to scan */
153   if (!(dir_list = stp_list_create()))
154     return NULL;
155   stp_list_set_freefunc(dir_list, stp_list_node_free_data);
156   stp_path_split(dir_list, path);
157   return dir_list;
158 }
159 
160 stp_list_t *
stp_data_path(void)161 stp_data_path(void)
162 {
163   if (getenv("STP_DATA_PATH"))
164     return stp_generate_path(getenv("STP_DATA_PATH"));
165   else
166     return stp_generate_path(PKGXMLDATADIR);
167 }
168 
169 stp_list_t *
stpi_list_files_on_data_path(const char * name)170 stpi_list_files_on_data_path(const char *name)
171 {
172   stp_list_t *dir_list = stp_data_path(); /* List of directories to scan */
173   stp_list_t *file_list = stp_path_search(dir_list, name);
174   stp_list_destroy(dir_list);
175   return file_list;
176 }
177 
178 /*
179  * Join a path and filename together.
180  */
181 char *
stpi_path_merge(const char * path,const char * file)182 stpi_path_merge(const char *path, /* Path */
183 		const char *file) /* Filename */
184 {
185   char *filename;                /* Filename to return */
186   int namelen = strlen(path) + strlen(file) + 2;
187   filename = (char *) stp_malloc(namelen * sizeof(char));
188   strcpy (filename, path);
189   strcat (filename, "/");
190   strcat (filename, file);
191   filename[namelen - 1] = '\0';
192   return filename;
193 }
194 
195 /*
196  * Find the first occurrence of <file> on <path>.
197  * File must be a plain file and readable.
198  * Return value must be freed
199  */
200 char *
stp_path_find_file(const char * path,const char * file)201 stp_path_find_file(const char *path, /* Path, or NULL for STP_DATA_PATH */
202 		   const char *file) /* File/relative pathname */
203 {
204   stp_list_t *path_to_search;
205   stp_list_item_t *dir;
206   if (path)
207     path_to_search = stp_generate_path(path);
208   else
209     path_to_search = stp_data_path();
210   dir = stp_list_get_start(path_to_search);
211   while (dir)
212     {
213       struct stat modstat;                      /* stat() output */
214       const char *check_path = (const char *) stp_list_item_get_data(dir);
215       char *filename = stpi_path_merge(check_path, file);
216       if (!stat(filename, &modstat) && S_ISREG(modstat.st_mode))
217 	{
218 	  stp_list_destroy(path_to_search);
219 	  return filename;
220 	}
221       stp_free(filename);
222       dir = stp_list_item_next(dir);
223     }
224   stp_list_destroy(path_to_search);
225   return NULL;
226 }
227 
228 /*
229  * Split a PATH-type string (colon-delimited) into separate
230  * directories.
231  */
232 void
stp_path_split(stp_list_t * list,const char * path)233 stp_path_split(stp_list_t *list, /* List to add directories to */
234 	       const char *path) /* Path to split */
235 {
236   const char *start = path;      /* Start of path name */
237   const char *end = NULL;        /* End of path name */
238   char *dir = NULL;              /* Path name */
239   int len;                       /* Length of path name */
240 
241   while (start)
242     {
243       end = (const char *) strchr(start, ':');
244       if (!end)
245 	len = strlen(start) + 1;
246       else
247 	len = (end - start);
248 
249       if (len && !(len == 1 && !end))
250 	{
251 	  dir = (char *) stp_malloc(len + 1);
252 	  strncpy(dir, start, len);
253 	  dir[len] = '\0';
254 	  stp_list_item_create(list, NULL, dir);
255 	}
256       if (!end)
257 	{
258 	  start = NULL;
259 	  break;
260 	}
261       start = end + 1;
262     }
263 }
264 
265 /* Adapted from GNU libc <dirent.h>
266    These macros extract size information from a `struct dirent *'.
267    They may evaluate their argument multiple times, so it must not
268    have side effects.  Each of these may involve a relatively costly
269    call to `strlen' on some systems, so these values should be cached.
270 
271    _D_EXACT_NAMLEN (DP) returns the length of DP->d_name, not including
272    its terminating null character.
273 
274    _D_ALLOC_NAMLEN (DP) returns a size at least (_D_EXACT_NAMLEN (DP) + 1);
275    that is, the allocation size needed to hold the DP->d_name string.
276    Use this macro when you don't need the exact length, just an upper bound.
277    This macro is less likely to require calling `strlen' than _D_EXACT_NAMLEN.
278    */
279 
280 #ifdef _DIRENT_HAVE_D_NAMLEN
281 # ifndef _D_EXACT_NAMLEN
282 #  define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
283 # endif
284 # ifndef _D_ALLOC_NAMLEN
285 #  define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
286 # endif
287 #else
288 # ifndef _D_EXACT_NAMLEN
289 #  define _D_EXACT_NAMLEN(d) (strlen ((d)->d_name))
290 # endif
291 # ifndef _D_ALLOC_NAMLEN
292 #  ifdef _DIRENT_HAVE_D_RECLEN
293 #   define _D_ALLOC_NAMLEN(d) (((char *) (d) + (d)->d_reclen) - &(d)->d_name[0])
294 #  else
295 #   define _D_ALLOC_NAMLEN(d) (sizeof (d)->d_name > 1 ? sizeof (d)->d_name : \
296                                _D_EXACT_NAMLEN (d) + 1)
297 #  endif
298 # endif
299 #endif
300 
301 /*
302  * BSD scandir() replacement, from glibc
303  */
304 static int
stpi_scandir(const char * dir,struct dirent *** namelist,const char * path,const char * suffix,int (* sel)(const struct dirent *,const char * path,const char * suffix),int (* cmp)(const void *,const void *))305 stpi_scandir (const char *dir,
306 	      struct dirent ***namelist,
307 	      const char *path,
308 	      const char *suffix,
309 	      int (*sel) (const struct dirent *, const char *path, const char *suffix),
310 	      int (*cmp) (const void *, const void *))
311 {
312   DIR *dp = opendir (dir);
313   struct dirent **v = NULL;
314   size_t vsize = 0, i;
315   struct dirent *d;
316   int save;
317 
318   if (dp == NULL)
319     return -1;
320 
321   save = errno;
322   errno = 0;
323 
324   i = 0;
325   while ((d = readdir (dp)) != NULL)
326     if (sel == NULL || (*sel) (d, path, suffix))
327       {
328 	struct dirent *vnew;
329 	size_t dsize;
330 
331 	/* Ignore errors from sel or readdir */
332         errno = 0;
333 
334 	if (i == vsize)
335 	  {
336 	    struct dirent **new;
337 	    if (vsize == 0)
338 	      vsize = 10;
339 	    else
340 	      vsize *= 2;
341 	    new = (struct dirent **) realloc (v, vsize * sizeof (*v));
342 	    if (new == NULL)
343 	      break;
344 	    v = new;
345 	  }
346 
347 	dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
348 	vnew = (struct dirent *) malloc (dsize);
349 	if (vnew == NULL)
350 	  break;
351 
352 	v[i++] = (struct dirent *) memcpy (vnew, d, dsize);
353       }
354 
355   if (errno != 0)
356     {
357       save = errno;
358 
359       while (i > 0)
360 	free (v[--i]);
361       free (v);
362 
363       i = -1;
364     }
365   else
366     {
367       /* Sort the list if we have a comparison function to sort with.  */
368       if (cmp != NULL)
369 	qsort (v, i, sizeof (*v), cmp);
370 
371       *namelist = v;
372     }
373 
374   (void) closedir (dp);
375   errno = save;
376 
377   return i;
378 }
379