/* * * Gutenprint path functions - split and search paths. * * Copyright 2002 Roger Leigh (rleigh@debian.org) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "gutenprint-internal.h" #include #include #include #include #include #include #include #include #include static int stpi_path_check(const struct dirent *module, const char *path, const char *suffix); static int stpi_scandir (const char *dir, struct dirent ***namelist, const char *path, const char *suffix, int (*sel) (const struct dirent *, const char *, const char *), int (*cmp) (const void *, const void *)); static int dirent_sort(const void *a, const void *b) { return strcoll ((*(const struct dirent * const *) a)->d_name, (*(const struct dirent * const *) b)->d_name); } /* * Make a list of all modules in the search path. */ stp_list_t * stp_path_search(stp_list_t *dirlist, /* List of directories to search */ const char *suffix) /* Required filename suffix */ { stp_list_t *findlist; /* List of files to return */ stp_list_item_t *diritem; /* Current directory */ struct dirent** module_dir = NULL; /* Current directory contents */ char *module_name; /* File name to check */ int n; /* Number of directory entries */ if (!dirlist) return NULL; findlist = stp_list_create(); if (!findlist) return NULL; stp_list_set_freefunc(findlist, stp_list_node_free_data); diritem = stp_list_get_start(dirlist); while (diritem) { const char *check_path = (const char *) stp_list_item_get_data(diritem); stp_deprintf(STP_DBG_PATH, "stp-path: directory: %s\n", (const char *) stp_list_item_get_data(diritem)); n = stpi_scandir ((const char *) stp_list_item_get_data(diritem), &module_dir, check_path, suffix, stpi_path_check, dirent_sort); if (n >= 0) { int idx; for (idx = 0; idx < n; ++idx) { module_name = stpi_path_merge((const char *) stp_list_item_get_data(diritem), module_dir[idx]->d_name); stp_list_item_create(findlist, NULL, module_name); free (module_dir[idx]); /* Must use plain free() */ } free (module_dir); /* Must use plain free() */ } diritem = stp_list_item_next(diritem); } return findlist; } /* * stpi_scandir() callback. Check the filename is sane, has the * correct mode bits and suffix. */ static int stpi_path_check(const struct dirent *module, /* File to check */ const char *check_path, /* Path to search */ const char *check_suffix) /* Suffix */ { int namelen; /* Filename length */ int status = 0; /* Error status */ int savederr; /* Saved errno */ char *filename; /* Filename */ struct stat modstat; /* stat() output */ savederr = errno; /* since we are a callback, preserve stpi_scandir() state */ filename = stpi_path_merge(check_path, module->d_name); namelen = strlen(filename); /* make sure we can take off suffix (e.g. .la) and still have a sane filename */ if (namelen >= strlen(check_suffix) + 1) { if (!stat (filename, &modstat)) { /* check file exists, and is a regular file */ if (S_ISREG(modstat.st_mode)) status = 1; if (strncmp(filename + (namelen - strlen(check_suffix)), check_suffix, strlen(check_suffix))) { status = 0; } } } if (status) stp_deprintf(STP_DBG_PATH, "stp-path: file: `%s'\n", filename); stp_free(filename); filename = NULL; errno = savederr; return status; } stp_list_t * stp_generate_path(const char *path) { stp_list_t *dir_list; /* List of directories to scan */ if (!(dir_list = stp_list_create())) return NULL; stp_list_set_freefunc(dir_list, stp_list_node_free_data); stp_path_split(dir_list, path); return dir_list; } stp_list_t * stp_data_path(void) { if (getenv("STP_DATA_PATH")) return stp_generate_path(getenv("STP_DATA_PATH")); else return stp_generate_path(PKGXMLDATADIR); } stp_list_t * stpi_list_files_on_data_path(const char *name) { stp_list_t *dir_list = stp_data_path(); /* List of directories to scan */ stp_list_t *file_list = stp_path_search(dir_list, name); stp_list_destroy(dir_list); return file_list; } /* * Join a path and filename together. */ char * stpi_path_merge(const char *path, /* Path */ const char *file) /* Filename */ { char *filename; /* Filename to return */ int namelen = strlen(path) + strlen(file) + 2; filename = (char *) stp_malloc(namelen * sizeof(char)); strcpy (filename, path); strcat (filename, "/"); strcat (filename, file); filename[namelen - 1] = '\0'; return filename; } /* * Find the first occurrence of on . * File must be a plain file and readable. * Return value must be freed */ char * stp_path_find_file(const char *path, /* Path, or NULL for STP_DATA_PATH */ const char *file) /* File/relative pathname */ { stp_list_t *path_to_search; stp_list_item_t *dir; if (path) path_to_search = stp_generate_path(path); else path_to_search = stp_data_path(); dir = stp_list_get_start(path_to_search); while (dir) { struct stat modstat; /* stat() output */ const char *check_path = (const char *) stp_list_item_get_data(dir); char *filename = stpi_path_merge(check_path, file); if (!stat(filename, &modstat) && S_ISREG(modstat.st_mode)) { stp_list_destroy(path_to_search); return filename; } stp_free(filename); dir = stp_list_item_next(dir); } stp_list_destroy(path_to_search); return NULL; } /* * Split a PATH-type string (colon-delimited) into separate * directories. */ void stp_path_split(stp_list_t *list, /* List to add directories to */ const char *path) /* Path to split */ { const char *start = path; /* Start of path name */ const char *end = NULL; /* End of path name */ char *dir = NULL; /* Path name */ int len; /* Length of path name */ while (start) { end = (const char *) strchr(start, ':'); if (!end) len = strlen(start) + 1; else len = (end - start); if (len && !(len == 1 && !end)) { dir = (char *) stp_malloc(len + 1); strncpy(dir, start, len); dir[len] = '\0'; stp_list_item_create(list, NULL, dir); } if (!end) { start = NULL; break; } start = end + 1; } } /* Adapted from GNU libc These macros extract size information from a `struct dirent *'. They may evaluate their argument multiple times, so it must not have side effects. Each of these may involve a relatively costly call to `strlen' on some systems, so these values should be cached. _D_EXACT_NAMLEN (DP) returns the length of DP->d_name, not including its terminating null character. _D_ALLOC_NAMLEN (DP) returns a size at least (_D_EXACT_NAMLEN (DP) + 1); that is, the allocation size needed to hold the DP->d_name string. Use this macro when you don't need the exact length, just an upper bound. This macro is less likely to require calling `strlen' than _D_EXACT_NAMLEN. */ #ifdef _DIRENT_HAVE_D_NAMLEN # ifndef _D_EXACT_NAMLEN # define _D_EXACT_NAMLEN(d) ((d)->d_namlen) # endif # ifndef _D_ALLOC_NAMLEN # define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1) # endif #else # ifndef _D_EXACT_NAMLEN # define _D_EXACT_NAMLEN(d) (strlen ((d)->d_name)) # endif # ifndef _D_ALLOC_NAMLEN # ifdef _DIRENT_HAVE_D_RECLEN # define _D_ALLOC_NAMLEN(d) (((char *) (d) + (d)->d_reclen) - &(d)->d_name[0]) # else # define _D_ALLOC_NAMLEN(d) (sizeof (d)->d_name > 1 ? sizeof (d)->d_name : \ _D_EXACT_NAMLEN (d) + 1) # endif # endif #endif /* * BSD scandir() replacement, from glibc */ 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 *)) { DIR *dp = opendir (dir); struct dirent **v = NULL; size_t vsize = 0, i; struct dirent *d; int save; if (dp == NULL) return -1; save = errno; errno = 0; i = 0; while ((d = readdir (dp)) != NULL) if (sel == NULL || (*sel) (d, path, suffix)) { struct dirent *vnew; size_t dsize; /* Ignore errors from sel or readdir */ errno = 0; if (i == vsize) { struct dirent **new; if (vsize == 0) vsize = 10; else vsize *= 2; new = (struct dirent **) realloc (v, vsize * sizeof (*v)); if (new == NULL) break; v = new; } dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d; vnew = (struct dirent *) malloc (dsize); if (vnew == NULL) break; v[i++] = (struct dirent *) memcpy (vnew, d, dsize); } if (errno != 0) { save = errno; while (i > 0) free (v[--i]); free (v); i = -1; } else { /* Sort the list if we have a comparison function to sort with. */ if (cmp != NULL) qsort (v, i, sizeof (*v), cmp); *namelist = v; } (void) closedir (dp); errno = save; return i; }