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