/*
*
* 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;
}