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