summaryrefslogtreecommitdiff
path: root/Build/source/texk/kpathsea/pathsearch.c
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
committerKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
commit487ca4806cc046076293cf6cc5fbba0db282bac7 (patch)
tree847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/kpathsea/pathsearch.c
parenta3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff)
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/kpathsea/pathsearch.c')
-rw-r--r--Build/source/texk/kpathsea/pathsearch.c644
1 files changed, 644 insertions, 0 deletions
diff --git a/Build/source/texk/kpathsea/pathsearch.c b/Build/source/texk/kpathsea/pathsearch.c
new file mode 100644
index 00000000000..90d21945556
--- /dev/null
+++ b/Build/source/texk/kpathsea/pathsearch.c
@@ -0,0 +1,644 @@
+/* pathsearch.c: look up a filename in a path.
+
+ Copyright 1997-2005 Olaf Weber.
+ Copyright 1993, 94, 95, 97 Karl Berry.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+#include <kpathsea/config.h>
+#include <kpathsea/c-pathch.h>
+#include <kpathsea/c-fopen.h>
+#include <kpathsea/absolute.h>
+#include <kpathsea/expand.h>
+#include <kpathsea/db.h>
+#include <kpathsea/pathsearch.h>
+#include <kpathsea/readable.h>
+#include <kpathsea/str-list.h>
+#include <kpathsea/str-llist.h>
+#include <kpathsea/variable.h>
+
+#include <time.h> /* for `time' */
+
+#ifdef __DJGPP__
+#include <sys/stat.h> /* for stat bits */
+#endif
+
+/* The very first search is for texmf.cnf, called when someone tries to
+ initialize the TFM path or whatever. init_path calls kpse_cnf_get
+ which calls kpse_all_path_search to find all the texmf.cnf's. We
+ need to do various special things in this case, since we obviously
+ don't yet have the configuration files when we're searching for the
+ configuration files. */
+static boolean first_search = true;
+
+
+
+/* This function is called after every search (except the first, since
+ we definitely want to allow enabling the logging in texmf.cnf) to
+ record the filename(s) found in $TEXMFLOG. */
+
+static void
+log_search P1C(str_list_type, filenames)
+{
+ static FILE *log_file = NULL;
+ static boolean first_time = true; /* Need to open the log file? */
+
+ if (first_time) {
+ /* Get name from either envvar or config file. */
+ string log_name = kpse_var_value ("TEXMFLOG");
+ first_time = false;
+ if (log_name) {
+ log_file = fopen (log_name, FOPEN_A_MODE);
+ if (!log_file)
+ perror (log_name);
+ free (log_name);
+ }
+ }
+
+ if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH) || log_file) {
+ unsigned e;
+
+ /* FILENAMES should never be null, but safety doesn't hurt. */
+ for (e = 0; e < STR_LIST_LENGTH (filenames) && STR_LIST_ELT (filenames, e);
+ e++) {
+ string filename = STR_LIST_ELT (filenames, e);
+
+ /* Only record absolute filenames, for privacy. */
+ if (log_file && kpse_absolute_p (filename, false))
+ fprintf (log_file, "%lu %s\n", (long unsigned) time (NULL),
+ filename);
+
+ /* And show them online, if debugging. We've already started
+ the debugging line in `search', where this is called, so
+ just print the filename here, don't use DEBUGF. */
+ if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) {
+ putc (' ', stderr);
+ fputs (filename, stderr);
+ }
+ }
+ }
+}
+
+/* Concatenate each element in DIRS with NAME (assume each ends with a
+ /, to save time). If SEARCH_ALL is false, return the first readable
+ regular file. Else continue to search for more. In any case, if
+ none, return a list containing just NULL.
+
+ We keep a single buffer for the potential filenames and reallocate
+ only when necessary. I'm not sure it's noticeably faster, but it
+ does seem cleaner. (We do waste a bit of space in the return
+ value, though, since we don't shrink it to the final size returned.) */
+
+#define INIT_ALLOC 75 /* Doesn't much matter what this number is. */
+
+static str_list_type
+dir_list_search P3C(str_llist_type *, dirs, const_string, name,
+ boolean, search_all)
+{
+ str_llist_elt_type *elt;
+ str_list_type ret;
+ unsigned name_len = strlen (name);
+ unsigned allocated = INIT_ALLOC;
+ string potential = (string)xmalloc (allocated);
+
+ ret = str_list_init ();
+
+ for (elt = *dirs; elt; elt = STR_LLIST_NEXT (*elt))
+ {
+ const_string dir = STR_LLIST (*elt);
+ unsigned dir_len = strlen (dir);
+
+ while (dir_len + name_len + 1 > allocated)
+ {
+ allocated += allocated;
+ XRETALLOC (potential, allocated, char);
+ }
+
+ strcpy (potential, dir);
+ strcat (potential, name);
+
+ if (kpse_readable_file (potential))
+ {
+ str_list_add (&ret, potential);
+
+ /* Move this element towards the top of the list. */
+ str_llist_float (dirs, elt);
+
+ /* If caller only wanted one file returned, no need to
+ terminate the list with NULL; the caller knows to only look
+ at the first element. */
+ if (!search_all)
+ return ret;
+
+ /* Start new filename. */
+ allocated = INIT_ALLOC;
+ potential = (string)xmalloc (allocated);
+ }
+ }
+
+ /* If we get here, either we didn't find any files, or we were finding
+ all the files. But we're done with the last filename, anyway. */
+ free (potential);
+
+ return ret;
+}
+
+static str_list_type
+dir_list_search_list P3C(str_llist_type *, dirs, const_string*, names,
+ boolean, search_all)
+{
+ str_llist_elt_type *elt;
+ str_list_type ret;
+ unsigned allocated = INIT_ALLOC;
+ string potential = XTALLOC(allocated, char);
+
+ ret = str_list_init ();
+
+ for (elt = *dirs; elt; elt = STR_LLIST_NEXT(*elt)) {
+ const_string dir = STR_LLIST (*elt);
+ unsigned dir_len = strlen (dir);
+ int i;
+
+ for (i = 0; names[i]; i++) {
+ const_string name = names[i];
+ unsigned name_len;
+
+ /* Don't bother with absolute & explicit relative. */
+ if (kpse_absolute_p(name, true))
+ continue;
+
+ name_len = strlen(name);
+
+ while (dir_len + name_len + 1 > allocated) {
+ allocated += allocated;
+ XRETALLOC (potential, allocated, char);
+ }
+
+ strcpy (potential, dir);
+ strcat (potential+dir_len, name);
+
+ if (kpse_readable_file (potential)) {
+ str_list_add (&ret, potential);
+
+ /* Move this element towards the top of the list. */
+ str_llist_float (dirs, elt);
+
+ /* If caller only wanted one file returned, no need to
+ terminate the list with NULL; the caller knows to only look
+ at the first element. */
+ if (!search_all)
+ return ret;
+
+ /* Start new filename. */
+ allocated = INIT_ALLOC;
+ potential = XTALLOC(allocated, char);
+ }
+ }
+ }
+
+ /* If we get here, either we didn't find any files, or we were finding
+ all the files. But we're done with the last filename, anyway. */
+ free (potential);
+
+ return ret;
+}
+
+/* This is called when NAME is absolute or explicitly relative; if it's
+ readable, return (a list containing) it; otherwise, return NULL. */
+
+static str_list_type
+absolute_search P1C(string, name)
+{
+ str_list_type ret_list;
+ string found = kpse_readable_file (name);
+
+ /* Some old compilers can't initialize structs. */
+ ret_list = str_list_init ();
+
+ /* If NAME wasn't found, free the expansion. */
+ if (name != found)
+ free (name);
+
+ /* Add `found' to the return list even if it's null; that tells
+ the caller we didn't find anything. */
+ str_list_add (&ret_list, found);
+
+ return ret_list;
+}
+
+/* This is the hard case -- look for NAME in PATH. If ALL is false,
+ return the first file found. Otherwise, search all elements of PATH. */
+
+static str_list_type
+path_search P4C(const_string, path, string, name,
+ boolean, must_exist, boolean, all)
+{
+ string elt;
+ str_list_type ret_list;
+ boolean done = false;
+ ret_list = str_list_init (); /* some compilers lack struct initialization */
+
+ for (elt = kpse_path_element (path); !done && elt;
+ elt = kpse_path_element (NULL)) {
+ str_list_type *found;
+ boolean allow_disk_search = true;
+
+ if (*elt == '!' && *(elt + 1) == '!') {
+ /* Those magic leading chars in a path element means don't search the
+ disk for this elt. And move past the magic to get to the name. */
+ allow_disk_search = false;
+ elt += 2;
+ }
+
+ /* See elt-dirs.c for side effects of this function */
+ kpse_normalize_path(elt);
+
+ /* Try ls-R, unless we're searching for texmf.cnf. Our caller
+ (search), also tests first_search, and does the resetting. */
+ found = first_search ? NULL : kpse_db_search (name, elt, all);
+
+ /* Search the filesystem if (1) the path spec allows it, and either
+ (2a) we are searching for texmf.cnf ; or
+ (2b) no db exists; or
+ (2c) no db's are relevant to this elt; or
+ (3) MUST_EXIST && NAME was not in the db.
+ In (2*), `found' will be NULL.
+ In (3), `found' will be an empty list. */
+ if (allow_disk_search && (!found || (must_exist && !STR_LIST (*found)))) {
+ str_llist_type *dirs = kpse_element_dirs (elt);
+ if (dirs && *dirs) {
+ if (!found)
+ found = XTALLOC1 (str_list_type);
+ *found = dir_list_search (dirs, name, all);
+ }
+ }
+
+ /* Did we find anything anywhere? */
+ if (found && STR_LIST (*found))
+ if (all)
+ str_list_concat (&ret_list, *found);
+ else {
+ str_list_add (&ret_list, STR_LIST_ELT (*found, 0));
+ done = true;
+ }
+
+ /* Free the list space, if any (but not the elements). */
+ if (found) {
+ str_list_free (found);
+ free (found);
+ }
+ }
+
+ /* Free the expanded name we were passed. It can't be in the return
+ list, since the path directories got unconditionally prepended. */
+ free (name);
+
+ return ret_list;
+}
+
+/* Search PATH for ORIGINAL_NAME. If ALL is false, or ORIGINAL_NAME is
+ absolute_p, check ORIGINAL_NAME itself. Otherwise, look at each
+ element of PATH for the first readable ORIGINAL_NAME.
+
+ Always return a list; if no files are found, the list will
+ contain just NULL. If ALL is true, the list will be
+ terminated with NULL. */
+
+static string *
+search P4C(const_string, path, const_string, original_name,
+ boolean, must_exist, boolean, all)
+{
+ str_list_type ret_list;
+ string name;
+ boolean absolute_p;
+
+#ifdef __DJGPP__
+ /* We will use `stat' heavily, so let's request for
+ the fastest possible version of `stat', by telling
+ it what members of struct stat do we really need.
+
+ We need to set this on each call because this is a
+ library function; the caller might need other options
+ from `stat'. Thus save the flags and restore them
+ before exit.
+
+ This call tells `stat' that we do NOT need to recognize
+ executable files (neither by an extension nor by a magic
+ signature); that we do NOT need time stamp of root directories;
+ and that we do NOT need the write access bit in st_mode.
+
+ Note that `kpse_set_progname' needs the EXEC bits,
+ but it was already called by the time we get here. */
+ unsigned short save_djgpp_flags = _djstat_flags;
+
+ _djstat_flags = _STAT_EXEC_MAGIC | _STAT_EXEC_EXT
+ | _STAT_ROOT_TIME | _STAT_WRITEBIT;
+#endif
+
+ /* Make a leading ~ count as an absolute filename, and expand $FOO's. */
+ name = kpse_expand (original_name);
+
+ /* If the first name is absolute or explicitly relative, no need to
+ consider PATH at all. */
+ absolute_p = kpse_absolute_p (name, true);
+
+ if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
+ DEBUGF4 ("start search(file=%s, must_exist=%d, find_all=%d, path=%s).\n",
+ name, must_exist, all, path);
+
+ /* Find the file(s). */
+ ret_list = absolute_p ? absolute_search (name)
+ : path_search (path, name, must_exist, all);
+
+ /* Append NULL terminator if we didn't find anything at all, or we're
+ supposed to find ALL and the list doesn't end in NULL now. */
+ if (STR_LIST_LENGTH (ret_list) == 0
+ || (all && STR_LIST_LAST_ELT (ret_list) != NULL))
+ str_list_add (&ret_list, NULL);
+
+ /* The very first search is for texmf.cnf. We can't log that, since
+ we want to allow setting TEXMFLOG in texmf.cnf. */
+ if (first_search) {
+ first_search = false;
+ } else {
+ /* Record the filenames we found, if desired. And wrap them in a
+ debugging line if we're doing that. */
+ if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
+ DEBUGF1 ("search(%s) =>", original_name);
+ log_search (ret_list);
+ if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
+ putc ('\n', stderr);
+ }
+
+#ifdef __DJGPP__
+ /* Undo any side effects. */
+ _djstat_flags = save_djgpp_flags;
+#endif
+
+ return STR_LIST (ret_list);
+}
+
+/* Search PATH for NAMES.
+
+ Always return a list; if no files are found, the list will
+ contain just NULL. If ALL is true, the list will be
+ terminated with NULL. */
+static string *
+search_list P4C(const_string, path, const_string*, names,
+ boolean, must_exist, boolean, all)
+{
+ str_list_type ret_list;
+ const_string* namep;
+ string elt;
+ boolean done = false;
+ boolean all_absolute = true;
+
+#ifdef __DJGPP__
+ /* We will use `stat' heavily, so let's request for
+ the fastest possible version of `stat', by telling
+ it what members of struct stat do we really need.
+
+ We need to set this on each call because this is a
+ library function; the caller might need other options
+ from `stat'. Thus save the flags and restore them
+ before exit.
+
+ This call tells `stat' that we do NOT need to recognize
+ executable files (neither by an extension nor by a magic
+ signature); that we do NOT need time stamp of root directories;
+ and that we do NOT need the write access bit in st_mode.
+
+ Note that `kpse_set_progname' needs the EXEC bits,
+ but it was already called by the time we get here. */
+ unsigned short save_djgpp_flags = _djstat_flags;
+
+ _djstat_flags = _STAT_EXEC_MAGIC | _STAT_EXEC_EXT
+ | _STAT_ROOT_TIME | _STAT_WRITEBIT;
+#endif
+
+ ret_list = str_list_init();
+
+ if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) {
+ DEBUGF1 ("start search(files=[%s", *names);
+ for (namep = names+1; *namep != NULL; namep++) {
+ fputc(' ', stderr);
+ fputs(*namep, stderr);
+ }
+ fprintf (stderr, "], must_exist=%d, find_all=%d, path=%s).\n",
+ must_exist, all, path);
+ }
+
+ /* FIXME: is this really true? No need to do any expansion on names. */
+
+ /* First catch any absolute or explicit relative names. */
+ for (namep = names; *namep; namep++) {
+ if (kpse_absolute_p(*namep, true)) {
+ if (kpse_readable_file(*namep)) {
+ str_list_add(&ret_list, xstrdup(*namep));
+ /* I know, I know... */
+ if (!all)
+ goto out;
+ }
+ } else {
+ all_absolute = false;
+ }
+ }
+ /* Shortcut: if we were only given absolute/explicit relative names,
+ we can skip the rest. Typically, if one name is absolute, they
+ all are, because our caller derived them from each other. */
+ if (all_absolute)
+ goto out;
+
+ /* Look at each path element in turn. */
+ for (elt = kpse_path_element (path); !done && elt;
+ elt = kpse_path_element (NULL))
+ {
+ str_list_type *found;
+ boolean allow_disk_search = true;
+ if (elt[0] == '!' && elt[1] == '!') {
+ /* !! magic -> disallow disk searches. */
+ allow_disk_search = false;
+ elt += 2;
+ }
+
+ /* See elt-dirs.c for side effects of this function. */
+ kpse_normalize_path(elt);
+
+ /* Try ls-R, unless we're searching for texmf.cnf. */
+ found = first_search ? NULL : kpse_db_search_list(names, elt, all);
+
+ /* Search the filesystem if (1) the path spec allows it, and either
+ (2a) we are searching for texmf.cnf ; or
+ (2b) no db exists; or
+ (2c) no db's are relevant to this elt; or
+ (3) MUST_EXIST && NAME was not in the db.
+ In (2*), `found' will be NULL.
+ In (3), `found' will be an empty list. */
+ if (allow_disk_search && (!found || (must_exist && !STR_LIST(*found)))) {
+ str_llist_type *dirs = kpse_element_dirs (elt);
+ if (dirs && *dirs) {
+ if (!found)
+ found = XTALLOC1 (str_list_type);
+ *found = dir_list_search_list (dirs, names, all);
+ }
+ }
+
+ /* Did we find anything? */
+ if (found && STR_LIST (*found)) {
+ if (all) {
+ str_list_concat (&ret_list, *found);
+ } else {
+ str_list_add (&ret_list, STR_LIST_ELT (*found, 0));
+ done = true;
+ }
+ }
+ }
+
+ out:
+ if (STR_LIST_LENGTH (ret_list) == 0
+ || (all && STR_LIST_LAST_ELT (ret_list) != NULL))
+ str_list_add (&ret_list, NULL);
+
+ if (first_search) {
+ first_search = false;
+ } else {
+ /* Record the filenames we found, if desired. And wrap them in a
+ debugging line if we're doing that. */
+ if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) {
+ DEBUGF1 ("search([%s", *names);
+ for (namep = names+1; *namep != NULL; namep++) {
+ fputc(' ', stderr);
+ fputs(*namep, stderr);
+ }
+ fputs ("]) =>", stderr);
+ }
+ log_search (ret_list);
+ if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
+ putc ('\n', stderr);
+ }
+
+#ifdef __DJGPP__
+ /* Undo any side effects. */
+ _djstat_flags = save_djgpp_flags;
+#endif
+
+ return STR_LIST (ret_list);
+}
+
+/* Search PATH for the first NAME. */
+
+string
+kpse_path_search P3C(const_string, path, const_string, name,
+ boolean, must_exist)
+{
+ string *ret_list = search (path, name, must_exist, false);
+ string ret = *ret_list;
+ free (ret_list);
+ return ret;
+}
+
+/* Search PATH for the first occurence of one of the NAMES. */
+
+string
+kpse_path_search_list P3C(const_string, path, const_string*, names,
+ boolean, must_exist)
+{
+ string *ret_list = search_list (path, names, must_exist, false);
+ string ret = *ret_list;
+ free (ret_list);
+ return ret;
+}
+
+/* Search all elements of PATH for files named NAME. Not sure if it's
+ right to assert `must_exist' here, but it suffices now. */
+
+string *
+kpse_all_path_search P2C(const_string, path, const_string, name)
+{
+ string *ret = search (path, name, true, true);
+ return ret;
+}
+
+string *
+kpse_all_path_search_list P2C(const_string, path, const_string*, names)
+{
+ string *ret = search_list (path, names, true, true);
+ return ret;
+}
+
+#ifdef TEST
+
+void
+test_path_search (const_string path, const_string file)
+{
+ string answer;
+ string *answer_list;
+
+ printf ("\nSearch %s for %s:\t", path, file);
+ answer = kpse_path_search (path, file);
+ puts (answer ? answer : "(nil)");
+
+ printf ("Search %s for all %s:\t", path, file);
+ answer_list = kpse_all_path_search (path, file);
+ putchar ('\n');
+ while (*answer_list)
+ {
+ putchar ('\t');
+ puts (*answer_list);
+ answer_list++;
+ }
+}
+
+#define TEXFONTS "/usr/local/lib/tex/fonts"
+
+int
+main ()
+{
+ /* All lists end with NULL. */
+ test_path_search (".", "nonexistent");
+ test_path_search (".", "/nonexistent");
+ test_path_search ("/k" ENV_SEP_STRING ".", "kpathsea.texi");
+ test_path_search ("/k" ENV_SEP_STRING ".", "/etc/fstab");
+ test_path_search ("." ENV_SEP_STRING TEXFONTS "//", "cmr10.tfm");
+ test_path_search ("." ENV_SEP_STRING TEXFONTS "//", "logo10.tfm");
+ test_path_search (TEXFONTS "//times" ENV_SEP_STRING "."
+ ENV_SEP_STRING ENV_SEP_STRING, "ptmr.vf");
+ test_path_search (TEXFONTS "/adobe//" ENV_SEP_STRING
+ "/usr/local/src/TeX+MF/typefaces//", "plcr.pfa");
+
+ test_path_search ("~karl", ".bashrc");
+ test_path_search ("/k", "~karl/.bashrc");
+
+ xputenv ("NONEXIST", "nonexistent");
+ test_path_search (".", "$NONEXISTENT");
+ xputenv ("KPATHSEA", "kpathsea");
+ test_path_search ("/k" ENV_SEP_STRING ".", "$KPATHSEA.texi");
+ test_path_search ("/k" ENV_SEP_STRING ".", "${KPATHSEA}.texi");
+ test_path_search ("$KPATHSEA" ENV_SEP_STRING ".", "README");
+ test_path_search ("." ENV_SEP_STRING "$KPATHSEA", "README");
+
+ return 0;
+}
+
+#endif /* TEST */
+
+
+/*
+Local variables:
+test-compile-command: "gcc -posix -g -I. -I.. -DTEST pathsearch.c kpathsea.a"
+End:
+*/