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