Mercurial > hg > octave-lyh
diff liboctave/kpse.cc @ 4385:de8c1d2ee728
[project @ 2003-04-24 03:27:41 by jwe]
author | jwe |
---|---|
date | Thu, 24 Apr 2003 03:27:41 +0000 |
parents | 0cbcb9d8b4ff |
children | 112a509bd2e6 |
line wrap: on
line diff
--- a/liboctave/kpse.cc +++ b/liboctave/kpse.cc @@ -1,7 +1,8 @@ /* pathsearch.c: look up a filename in a path. -Copyright (C) 1993, 94, 95, 96, 97 Karl Berry. +Copyright (C) 1993, 94, 95, 96, 97, 98 Karl Berry. Copyright (C) 1993, 94, 95, 96, 97 Karl Berry & O. Weber. +Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -25,12 +26,398 @@ #include "kpse-xfns.h" #include "kpse.h" -#include <time.h> /* for `time' */ - -#ifdef __DJGPP__ -#include <sys/stat.h> /* for stat bits */ +/* c-std.h: the first header files. */ + +/* Header files that essentially all of our sources need, and + that all implementations have. We include these first, to help with + NULL being defined multiple times. */ +#include <cstdio> +#include <cstdarg> +#include <cstdlib> +#include <cstring> +#include <climits> +#include <cerrno> +#include <cassert> + +#ifdef HAVE_UNISTD_H +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include <unistd.h> +#endif + +#ifdef WIN32 +#include <malloc.h> +#endif /* not WIN32 */ + +#include "sysdir.h" +#include "statdefs.h" + +/* define NAME_MAX, the maximum length of a single + component in a filename. No such limit may exist, or may vary + depending on the filesystem. */ + +/* Most likely the system will truncate filenames if it is not POSIX, + and so we can use the BSD value here. */ +#ifndef _POSIX_NAME_MAX +#define _POSIX_NAME_MAX 255 +#endif + +#ifndef NAME_MAX +#define NAME_MAX _POSIX_NAME_MAX +#endif + +/* c-ctype.h: ASCII-safe versions of the <ctype.h> macros. */ + +#include <cctype> + +/* What separates elements in environment variable path lists? */ +#ifndef ENV_SEP +#ifdef DOSISH +#define ENV_SEP ';' +#define ENV_SEP_STRING ";" +#else +#define ENV_SEP ':' +#define ENV_SEP_STRING ":" +#endif /* not DOS */ +#endif /* not ENV_SEP */ + +#ifndef IS_ENV_SEP +#define IS_ENV_SEP(ch) ((ch) == ENV_SEP) +#endif + +/* c-pathmx.h: define PATH_MAX, the maximum length of a filename. + Since no such limit may exist, it's preferable to dynamically grow + filenames as needed. */ + +/* Cheat and define this as a manifest constant no matter what, instead + of using pathconf. I forget why we want to do this. */ + +#ifndef _POSIX_PATH_MAX +#define _POSIX_PATH_MAX 255 +#endif + +#ifndef PATH_MAX +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else +#define PATH_MAX _POSIX_PATH_MAX +#endif +#endif /* not PATH_MAX */ + +/* debug.h: Runtime tracing. */ + +/* If NO_DEBUG is defined (not recommended), skip all this. */ +#ifndef NO_DEBUG + +/* OK, we'll have tracing support. */ +#define KPSE_DEBUG + +/* Set a bit. */ +#define KPSE_DEBUG_SET(bit) kpathsea_debug |= 1 << (bit) + +/* Test if a bit is on. */ +#define KPSE_DEBUG_P(bit) (kpathsea_debug & (1 << (bit))) + +#define KPSE_DEBUG_STAT 0 /* stat calls */ +#define KPSE_DEBUG_HASH 1 /* hash lookups */ +#define KPSE_DEBUG_FOPEN 2 /* fopen/fclose calls */ +#define KPSE_DEBUG_PATHS 3 /* search path initializations */ +#define KPSE_DEBUG_EXPAND 4 /* path element expansion */ +#define KPSE_DEBUG_SEARCH 5 /* searches */ +#define KPSE_DEBUG_VARS 6 /* variable values */ +#define KPSE_LAST_DEBUG KPSE_DEBUG_VARS + +/* A printf for the debugging. */ +#define DEBUGF_START() do { fputs ("kdebug:", stderr) +#define DEBUGF_END() fflush (stderr); } while (0) + +#define DEBUGF(str) \ + DEBUGF_START (); fputs (str, stderr); DEBUGF_END () +#define DEBUGF1(str, e1) \ + DEBUGF_START (); fprintf (stderr, str, e1); DEBUGF_END () +#define DEBUGF2(str, e1, e2) \ + DEBUGF_START (); fprintf (stderr, str, e1, e2); DEBUGF_END () +#define DEBUGF3(str, e1, e2, e3) \ + DEBUGF_START (); fprintf (stderr, str, e1, e2, e3); DEBUGF_END () +#define DEBUGF4(str, e1, e2, e3, e4) \ + DEBUGF_START (); fprintf (stderr, str, e1, e2, e3, e4); DEBUGF_END () + +#undef fopen +#define fopen kpse_fopen_trace +extern FILE *fopen (const char *filename, const char *mode); +#undef fclose +#define fclose kpse_fclose_trace +extern int fclose (FILE *); + +#endif /* not NO_DEBUG */ + +#if defined (WIN32) && !defined (__MINGW32__) + +/* System description file for Windows NT. */ + +/* + * Define symbols to identify the version of Unix this is. + * Define all the symbols that apply correctly. + */ + +#ifndef DOSISH +#define DOSISH +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN _MAX_PATH #endif +#define HAVE_DUP2 1 +#define HAVE_RENAME 1 +#define HAVE_RMDIR 1 +#define HAVE_MKDIR 1 +#define HAVE_GETHOSTNAME 1 +#define HAVE_RANDOM 1 +#define USE_UTIME 1 +#define HAVE_MOUSE 1 +#define HAVE_TZNAME 1 + +/* These have to be defined because our compilers treat __STDC__ as being + defined (most of them anyway). */ + +#define access _access +#define stat _stat +#define strcasecmp _stricmp +#define strdup _strdup +#define strncasecmp _strnicmp + +#define S_IFMT _S_IFMT +#define S_IFDIR _S_IFDIR + +/* Define this so that winsock.h definitions don't get included when + windows.h is... For this to have proper effect, config.h must + always be included before windows.h. */ +#define _WINSOCKAPI_ 1 + +#include <windows.h> + +/* Defines size_t and alloca (). */ +#include <malloc.h> + +/* For proper declaration of environ. */ +#include <io.h> +#include <fcntl.h> +#include <process.h> + +/* ============================================================ */ + +#endif /* WIN32 */ + +/* hash.h: declarations for a hash table. */ + +/* A single (key,value) pair. */ +typedef struct hash_element_struct +{ + const char *key; + const char *value; + struct hash_element_struct *next; +} hash_element_type; + +/* The usual arrangement of buckets initialized to null. */ +typedef struct +{ + hash_element_type **buckets; + unsigned size; +} hash_table_type; + +static hash_table_type hash_create (unsigned size); + + +#ifdef KPSE_DEBUG +/* How to print the hash results when debugging. */ +extern int kpse_debug_hash_lookup_int; +#endif + +/* fn.h: arbitrarily long filenames (or just strings). */ + +/* Arbitrarily long filenames; it's inconvenient to use obstacks here, + because we want to maintain a null terminator. Also used for + dynamically growing strings even when the null byte isn't necessary, + e.g., in `variable.c', since I don't want to pass obstacks around + everywhere, and one can't free parts of an obstack arbitrarily. */ + +typedef struct +{ + char *str; + unsigned allocated; + unsigned length; /* includes the terminating null byte, if any */ +} fn_type; + +#define FN_STRING(fn) ((fn).str) +#define FN_ALLOCATED(fn) ((fn).allocated) +#define FN_LENGTH(fn) ((fn).length) + +/* lib.h: other stuff. */ + +/* Define common sorts of messages. */ + +/* This should be called only after a system call fails. Don't exit + with status `errno', because that might be 256, which would mean + success (exit statuses are truncated to eight bits). */ +#define FATAL_PERROR(str) do { \ + fputs ("pathsearch: ", stderr); \ + perror (str); exit (EXIT_FAILURE); } while (0) + +#define START_FATAL() do { \ + fputs ("pathsearch: fatal: ", stderr); +#define END_FATAL() fputs (".\n", stderr); exit (1); } while (0) + +#define FATAL(str) \ + START_FATAL (); fputs (str, stderr); END_FATAL () +#define FATAL1(str, e1) \ + START_FATAL (); fprintf (stderr, str, e1); END_FATAL () +#define FATAL2(str, e1, e2) \ + START_FATAL (); fprintf (stderr, str, e1, e2); END_FATAL () +#define FATAL3(str, e1, e2, e3) \ + START_FATAL (); fprintf (stderr, str, e1, e2, e3); END_FATAL () +#define FATAL4(str, e1, e2, e3, e4) \ + START_FATAL (); fprintf (stderr, str, e1, e2, e3, e4); END_FATAL () +#define FATAL5(str, e1, e2, e3, e4, e5) \ + START_FATAL (); fprintf (stderr, str, e1, e2, e3, e4, e5); END_FATAL () +#define FATAL6(str, e1, e2, e3, e4, e5, e6) \ + START_FATAL (); fprintf (stderr, str, e1, e2, e3, e4, e5, e6); END_FATAL () + + +#define START_WARNING() do { fputs ("warning: ", stderr) +#define END_WARNING() fputs (".\n", stderr); fflush (stderr); } while (0) + +#define WARNING(str) \ + START_WARNING (); fputs (str, stderr); END_WARNING () +#define WARNING1(str, e1) \ + START_WARNING (); fprintf (stderr, str, e1); END_WARNING () +#define WARNING2(str, e1, e2) \ + START_WARNING (); fprintf (stderr, str, e1, e2); END_WARNING () +#define WARNING3(str, e1, e2, e3) \ + START_WARNING (); fprintf (stderr, str, e1, e2, e3); END_WARNING () +#define WARNING4(str, e1, e2, e3, e4) \ + START_WARNING (); fprintf (stderr, str, e1, e2, e3, e4); END_WARNING () + + +/* I find this easier to read. */ +#define STREQ(s1, s2) (strcmp (s1, s2) == 0) +#define STRNEQ(s1, s2, n) (strncmp (s1, s2, n) == 0) + +/* Support for FAT/ISO-9660 filesystems. Theoretically this should be + done at runtime, per filesystem, but that's painful to program. */ +#ifdef MONOCASE_FILENAMES +#define FILESTRCASEEQ(s1, s2) (strcasecmp (s1, s2) == 0) +#define FILESTRNCASEEQ(s1, s2, l) (strncasecmp (s1, s2, l) == 0) +#define FILECHARCASEEQ(c1, c2) (toupper (c1) == toupper (c2)) +#else +#define FILESTRCASEEQ STREQ +#define FILESTRNCASEEQ STRNEQ +#define FILECHARCASEEQ(c1, c2) ((c1) == (c2)) +#endif + +/* This is the maximum number of numerals that result when a 64-bit + integer is converted to a string, plus one for a trailing null byte, + plus one for a sign. */ +#define MAX_INT_LENGTH 21 + +/* If the environment variable TEST is set, return it; otherwise, + DEFAULT. This is useful for paths that use more than one envvar. */ +#define ENVVAR(test, default) (getenv (test) ? (test) : (default)) + + +/* (Re)Allocate N items of type T using xmalloc/xrealloc. */ +#define XTALLOC(n, t) ((t *) xmalloc ((n) * sizeof (t))) +#define XTALLOC1(t) XTALLOC (1, t) +#define XRETALLOC(addr, n, t) ((addr) = (t *) xrealloc (addr, (n) * sizeof(t))) + +extern "C" char *xbasename (const char *name); + +static FILE *xfopen (const char *filename, const char *mode); + +static void xfclose (FILE *f, const char *filename); + +unsigned long xftell (FILE *f, char *filename); + +static void xclosedir (DIR *d); + +static void *xmalloc (unsigned size); + +static void *xrealloc (void *old_ptr, unsigned size); + +static char *xstrdup (const char *s); + +extern char *xbasename (const char *name); + +static int dir_p (const char *fn); + +#ifndef WIN32 +int dir_links (const char *fn); +#endif + +static unsigned hash (hash_table_type table, const char *key); + +static void hash_insert (hash_table_type *table, const char *key, + const char *value); + +static char **hash_lookup (hash_table_type table, const char *key); + +static void hash_print (hash_table_type table, int summary_only); + +static char *concat (const char *s1, const char *s2); + +static char *concat3 (const char *s1, const char *s2, const char *s3); + +static char *concatn (const char *str1, ...); + +static char *find_suffix (const char *name); + +static char *kpse_truncate_filename (const char *name); + +static char *kpse_readable_file (const char *name); + +static int kpse_absolute_p (const char *filename, int relative_ok); + +static str_list_type str_list_init (void); + +static void str_list_add (str_list_type *l, char *s); + +static void str_list_concat (str_list_type *target, str_list_type more); + +static void str_list_free (str_list_type *l); + +static void str_llist_add (str_llist_type *l, char *str); + +static void str_llist_float (str_llist_type *l, str_llist_elt_type *mover); + +static fn_type fn_init (void); + +static fn_type fn_copy0 (const char *s, unsigned len); + +static void fn_free (fn_type *f); + +static void grow (fn_type *f, unsigned len); + +static void fn_1grow (fn_type *f, char c); + +static void fn_grow (fn_type *f, void *source, unsigned len); + +static void fn_str_grow (fn_type *f, const char *s); + +static void fn_shrink_to (fn_type *f, unsigned loc); + +static char *kpse_var_value (const char *var); + +static void expanding (const char *var, int xp); + +static int expanding_p (const char *var); + +static void expand (fn_type *expansion, const char *start, const char *end); + +static char *kpse_var_expand (const char *src); + +#include <ctime> /* for `time' */ + /* 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 @@ -56,7 +443,7 @@ char *log_name = kpse_var_value ("TEXMFLOG"); first_time = false; if (log_name) { - log_file = fopen (log_name, FOPEN_A_MODE); + log_file = xfopen (log_name, "a"); if (!log_file) perror (log_name); free (log_name); @@ -266,29 +653,6 @@ char *name; bool 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); @@ -324,11 +688,6 @@ putc ('\n', stderr); } -#ifdef __DJGPP__ - /* Undo any side effects. */ - _djstat_flags = save_djgpp_flags; -#endif - return STR_LIST (ret_list); } @@ -489,29 +848,6 @@ { str_list_type ret_list; -#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 - if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) { const char **p; @@ -560,11 +896,6 @@ putc ('\n', stderr); } -#ifdef __DJGPP__ - /* Undo any side effects. */ - _djstat_flags = save_djgpp_flags; -#endif - return STR_LIST (ret_list); } @@ -1526,7 +1857,7 @@ unsigned len = strlen (db_filename) - sizeof (DB_NAME) + 1; /* Keep the /. */ char *top_dir = (char *) xmalloc (len + 1); char *cur_dir = NULL; /* First thing in ls-R might be a filename. */ - FILE *db_file = fopen (db_filename, FOPEN_R_MODE); + FILE *db_file = xfopen (db_filename, "r"); strncpy (top_dir, db_filename, len); top_dir[len] = 0; @@ -1723,7 +2054,7 @@ { char *line, *real, *alias; unsigned count = 0; - FILE *alias_file = fopen (alias_filename, FOPEN_R_MODE); + FILE *alias_file = xfopen (alias_filename, "r"); if (alias_file) { while ((line = read_line (alias_file)) != NULL) { @@ -1733,13 +2064,13 @@ } else { /* Each line should have two fields: realname aliasname. */ real = line; - while (*real && ISSPACE (*real)) + while (*real && isspace (*real)) real++; alias = real; - while (*alias && !ISSPACE (*alias)) + while (*alias && !isspace (*alias)) alias++; *alias++ = 0; - while (*alias && ISSPACE (*alias)) + while (*alias && isspace (*alias)) alias++; /* Is the check for errors strong enough? Should we warn the user for potential errors? */ @@ -2428,3 +2759,986 @@ return element (p, false); } +/* xfopen.c: fopen and fclose with error checking. */ + +/* These routines just check the return status from standard library + routines and abort if an error happens. */ + +FILE * +xfopen (const char *filename, const char *mode) +{ + FILE *f; + + assert (filename && mode); + + f = fopen (filename, mode); + if (f == NULL) + FATAL_PERROR (filename); + + return f; +} + +void +xfclose (FILE *f, const char *filename) +{ + assert (f); + + if (fclose (f) == EOF) + FATAL_PERROR (filename); +} + +/* xftell.c: ftell with error checking. */ + +unsigned long +xftell (FILE *f, char *filename) +{ + long where = ftell (f); + + if (where < 0) + FATAL_PERROR (filename); + + return where; +} + +void +xclosedir (DIR *d) +{ +#ifdef CLOSEDIR_VOID + closedir (d); +#else + int ret = closedir (d); + + if (ret != 0) + FATAL ("closedir failed"); +#endif +} + +/* xmalloc.c: malloc with error checking. */ + +void * +xmalloc (unsigned size) +{ + void *new_mem = (void *) malloc (size); + + if (new_mem == NULL) + { + fprintf (stderr, "fatal: memory exhausted (xmalloc of %u bytes).\n", + size); + /* 1 means success on VMS, so pick a random number (ASCII `K'). */ + exit (75); + } + + return new_mem; +} + +/* xrealloc.c: realloc with error checking. */ + +extern void *xmalloc (unsigned); + +void * +xrealloc (void *old_ptr, unsigned size) +{ + void *new_mem; + + if (old_ptr == NULL) + new_mem = xmalloc (size); + else + { + new_mem = (void *) realloc (old_ptr, size); + if (new_mem == NULL) + { + /* We used to print OLD_PTR here using %x, and casting its + value to unsigned, but that lost on the Alpha, where + pointers and unsigned had different sizes. Since the info + is of little or no value anyway, just don't print it. */ + fprintf (stderr, "fatal: memory exhausted (realloc of %u bytes).\n", + size); + /* 1 means success on VMS, so pick a random number (ASCII `B'). */ + exit (66); + } + } + + return new_mem; +} + +/* xstrdup.c: strdup with error checking. */ + +/* Return a copy of S in new storage. */ + +char * +xstrdup (const char *s) +{ + char *new_string = (char *) xmalloc (strlen (s) + 1); + return strcpy (new_string, s); +} + +/* dir.c: directory operations. */ + +/* Return true if FN is a directory or a symlink to a directory, + false if not. */ + +int +dir_p (const char *fn) +{ +#ifdef WIN32 + int fa = GetFileAttributes(fn); + return (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY)); +#else + struct stat stats; + return stat (fn, &stats) == 0 && S_ISDIR (stats.st_mode); +#endif +} + +#ifndef WIN32 + +/* Return -1 if FN isn't a directory, else its number of links. + Duplicate the call to stat; no need to incur overhead of a function + call for that little bit of cleanliness. */ + +int +dir_links (const char *fn) +{ + static hash_table_type link_table; + char **hash_ret; + long ret; + + if (link_table.size == 0) + link_table = hash_create (457); + +#ifdef KPSE_DEBUG + /* This is annoying, but since we're storing integers as pointers, we + can't print them as strings. */ + if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) + kpse_debug_hash_lookup_int = 1; +#endif + + hash_ret = hash_lookup (link_table, fn); + +#ifdef KPSE_DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) + kpse_debug_hash_lookup_int = 0; +#endif + + /* Have to cast the int we need to/from the const_string that the hash + table stores for values. Let's hope an int fits in a pointer. */ + if (hash_ret) + ret = (long) *hash_ret; + else + { + struct stat stats; + ret = stat (fn, &stats) == 0 && S_ISDIR (stats.st_mode) + ? stats.st_nlink : (unsigned) -1; + + /* It's up to us to copy the value. */ + hash_insert (&link_table, xstrdup (fn), (const char *) ret); + +#ifdef KPSE_DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_STAT)) + DEBUGF2 ("dir_links(%s) => %ld\n", fn, ret); +#endif + } + + return ret; +} + +#endif /* !WIN32 */ + +/* hash.c: hash table operations. */ + +/* The hash function. We go for simplicity here. */ + +/* All our hash tables are related to filenames. */ +#ifdef MONOCASE_FILENAMES +#define TRANSFORM(x) toupper (x) +#else +#define TRANSFORM(x) (x) +#endif + +static unsigned +hash (hash_table_type table, const char *key) +{ + unsigned n = 0; + + /* Our keys aren't often anagrams of each other, so no point in + weighting the characters. */ + while (*key != 0) + n = (n + n + TRANSFORM (*key++)) % table.size; + + return n; +} + +hash_table_type +hash_create (unsigned size) +{ + /* hash_table_type ret; changed into "static ..." to work around gcc + optimizer bug for Alpha. */ + static hash_table_type ret; + unsigned b; + ret.buckets = XTALLOC (size, hash_element_type *); + ret.size = size; + + /* calloc's zeroes aren't necessarily NULL, so be safe. */ + for (b = 0; b <ret.size; b++) + ret.buckets[b] = NULL; + + return ret; +} + +/* Whether or not KEY is already in MAP, insert it and VALUE. Do not + duplicate the strings, in case they're being purposefully shared. */ + +void +hash_insert (hash_table_type *table, const char *key, const char *value) +{ + unsigned n = hash (*table, key); + hash_element_type *new_elt = XTALLOC1 (hash_element_type); + + new_elt->key = key; + new_elt->value = value; + new_elt->next = NULL; + + /* Insert the new element at the end of the list. */ + if (!table->buckets[n]) + /* first element in bucket is a special case. */ + table->buckets[n] = new_elt; + else + { + hash_element_type *loc = table->buckets[n]; + while (loc->next) /* Find the last element. */ + loc = loc->next; + loc->next = new_elt; /* Insert the new one after. */ + } +} + +/* Look up STR in MAP. Return a (dynamically-allocated) list of the + corresponding strings or NULL if no match. */ + +#ifdef KPSE_DEBUG +/* Print the hash values as integers if this is nonzero. */ +int kpse_debug_hash_lookup_int = 0; +#endif + +char ** +hash_lookup (hash_table_type table, const char *key) +{ + hash_element_type *p; + str_list_type ret; + unsigned n = hash (table, key); + ret = str_list_init (); + + /* Look at everything in this bucket. */ + for (p = table.buckets[n]; p != NULL; p = p->next) + if (FILESTRCASEEQ (key, p->key)) + /* Cast because the general str_list_type shouldn't force const data. */ + str_list_add (&ret, (char *) p->value); + + /* If we found anything, mark end of list with null. */ + if (STR_LIST (ret)) + str_list_add (&ret, NULL); + +#ifdef KPSE_DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) + { + DEBUGF1 ("hash_lookup(%s) =>", key); + if (!STR_LIST (ret)) + fputs (" (nil)\n", stderr); + else + { + char **r; + for (r = STR_LIST (ret); *r; r++) + { + putc (' ', stderr); + if (kpse_debug_hash_lookup_int) + fprintf (stderr, "%ld", (long) *r); + else + fputs (*r, stderr); + } + putc ('\n', stderr); + } + fflush (stderr); + } +#endif + + return STR_LIST (ret); +} + +/* We only print nonempty buckets, to decrease output volume. */ + +void +hash_print (hash_table_type table, int summary_only) +{ + unsigned b; + unsigned total_elements = 0, total_buckets = 0; + + for (b = 0; b < table.size; b++) { + hash_element_type *bucket = table.buckets[b]; + + if (bucket) { + unsigned len = 1; + hash_element_type *tb; + + total_buckets++; + if (!summary_only) fprintf (stderr, "%4d ", b); + + for (tb = bucket->next; tb != NULL; tb = tb->next) + len++; + if (!summary_only) fprintf (stderr, ":%-5d", len); + total_elements += len; + + if (!summary_only) { + for (tb = bucket; tb != NULL; tb = tb->next) + fprintf (stderr, " %s=>%s", tb->key, tb->value); + putc ('\n', stderr); + } + } + } + + fprintf (stderr, + "%u buckets, %u nonempty (%u%%); %u entries, average chain %.1f.\n", + table.size, + total_buckets, + 100 * total_buckets / table.size, + total_elements, + total_buckets ? total_elements / (double) total_buckets : 0.0); +} + +/* concat.c: dynamic string concatenation. */ + +/* Return the concatenation of S1 and S2. See `concatn.c' for a + `concatn', which takes a variable number of arguments. */ + +char * +concat (const char *s1, const char *s2) +{ + char *answer = (char *) xmalloc (strlen (s1) + strlen (s2) + 1); + strcpy (answer, s1); + strcat (answer, s2); + + return answer; +} + +/* concat3.c: concatenate three strings. */ + +char * +concat3 (const char *s1, const char *s2, const char *s3) +{ + char *answer + = (char *) xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1); + strcpy (answer, s1); + strcat (answer, s2); + strcat (answer, s3); + + return answer; +} + +/* concatn.c: Concatenate an arbitrary number of strings. */ + +/* OK, it would be epsilon more efficient to compute the total length + and then do the copying ourselves, but I doubt it matters in reality. */ + +char * +concatn (const char *str1, ...) +{ + char *arg; + char *ret; + va_list ap; + + va_start (ap, str1); + + if (!str1) + return NULL; + + ret = xstrdup (str1); + + while ((arg = va_arg (ap, char *)) != NULL) + { + char *temp = concat (ret, arg); + free (ret); + ret = temp; + } + va_end (ap); + + return ret; +} + +/* debug.c: Help the user discover what's going on. */ + +#ifdef KPSE_DEBUG + +unsigned int kpathsea_debug = 0; + +/* If the real definitions of fopen or fclose are macros, we lose -- the + #undef won't restore them. */ + +FILE * +fopen (const char *filename, const char *mode) +{ +#undef fopen + FILE *ret = fopen (filename, mode); + + if (KPSE_DEBUG_P (KPSE_DEBUG_FOPEN)) + DEBUGF3 ("fopen(%s, %s) => 0x%lx\n", filename, mode, (unsigned long) ret); + + return ret; +} + +int +fclose (FILE *f) +{ +#undef fclose + int ret = fclose (f); + + if (KPSE_DEBUG_P (KPSE_DEBUG_FOPEN)) + DEBUGF2 ("fclose(0x%lx) => %d\n", (unsigned long) f, ret); + + return ret; +} + +#endif + +/* find-suffix.c: return the stuff after a dot. */ + +/* Return pointer to first character after `.' in last directory element + of NAME. If the name is `foo' or `/foo.bar/baz', we have no extension. */ + +char * +find_suffix (const char *name) +{ + const char *slash_pos; + char *dot_pos = strrchr (name, '.'); + + if (dot_pos == NULL) + return NULL; + + for (slash_pos = name + strlen (name); + slash_pos > dot_pos && !IS_DIR_SEP (*slash_pos); + slash_pos--) + ; + + return slash_pos > dot_pos ? NULL : dot_pos + 1; +} + +/* rm-suffix.c: remove any suffix. */ + +/* Generic const warning -- see extend-fname.c. */ + +char * +remove_suffix (const char *s) +{ + char *ret; + const char *suffix = find_suffix (s); + + if (suffix) + { + /* Back up to before the dot. */ + suffix--; + ret = (char *) xmalloc (suffix - s + 1); + strncpy (ret, s, suffix - s); + ret[suffix - s] = 0; + } + else + ret = (char *) s; + + return ret; +} + +/* readable.c: check if a filename is a readable non-directory file. */ + +/* Truncate any too-long components in NAME, returning the result. It's + too bad this is necessary. See comments in readable.c for why. */ + +static char * +kpse_truncate_filename (const char *name) +{ + unsigned c_len = 0; /* Length of current component. */ + unsigned ret_len = 0; /* Length of constructed result. */ + + /* Allocate enough space. */ + char *ret = (char *) xmalloc (strlen (name) + 1); + + for (; *name; name++) + { + if (IS_DIR_SEP (*name) || IS_DEVICE_SEP (*name)) + { /* At a directory delimiter, reset component length. */ + c_len = 0; + } + else if (c_len > NAME_MAX) + { /* If past the max for a component, ignore this character. */ + continue; + } + + /* Copy this character. */ + ret[ret_len++] = *name; + c_len++; + } + ret[ret_len] = 0; + + return ret; +} + +/* If access can read FN, run stat (assigning to stat buffer ST) and + check that fn is not a directory. Don't check for just being a + regular file, as it is potentially useful to read fifo's or some + kinds of devices. */ + +#ifdef WIN32 +#define READABLE(fn, st) \ + (GetFileAttributes(fn) != 0xFFFFFFFF && \ + !(GetFileAttributes(fn) & FILE_ATTRIBUTE_DIRECTORY)) +#else +#define READABLE(fn, st) \ + (access (fn, R_OK) == 0 && stat (fn, &(st)) == 0 && !S_ISDIR (st.st_mode)) +#endif + +/* POSIX invented the brain-damage of not necessarily truncating + filename components; the system's behavior is defined by the value of + the symbol _POSIX_NO_TRUNC, but you can't change it dynamically! + + Generic const return warning. See extend-fname.c. */ + +char * +kpse_readable_file (const char *name) +{ + struct stat st; + char *ret; + + if (READABLE (name, st)) { + ret = (char *) name; + +#ifdef ENAMETOOLONG + } else if (errno == ENAMETOOLONG) { + ret = kpse_truncate_filename (name); + + /* Perhaps some other error will occur with the truncated name, so + let's call access again. */ + if (!READABLE (ret, st)) + { /* Failed. */ + if (ret != name) free (ret); + ret = NULL; + } +#endif /* ENAMETOOLONG */ + + } else { /* Some other error. */ + if (errno == EACCES) { /* Maybe warn them if permissions are bad. */ + perror (name); + } + ret = NULL; + } + + return ret; +} + +/* absolute.c: Test if a filename is absolute or explicitly relative. */ + +/* Sorry this is such a system-dependent mess, but I can't see any way + to usefully generalize. */ + +int +kpse_absolute_p (const char *filename, int relative_ok) +{ + int absolute = IS_DIR_SEP (*filename) +#ifdef DOSISH + /* Novell allows non-alphanumeric drive letters. */ + || (*filename && IS_DEVICE_SEP (filename[1])) +#endif /* DOSISH */ +#ifdef WIN32 + /* UNC names */ + || (*filename == '\\' && filename[1] == '\\') +#endif + ; + int explicit_relative + = relative_ok + && (*filename == '.' && (IS_DIR_SEP (filename[1]) + || (filename[1] == '.' && IS_DIR_SEP (filename[2])))); + + return absolute || explicit_relative; +} + +/* str-list.c: define routines for string lists. */ + +/* See the lib.h file for comments. */ + +str_list_type +str_list_init (void) +{ + str_list_type ret; + + STR_LIST_LENGTH (ret) = 0; + STR_LIST (ret) = NULL; + + return ret; +} + +void +str_list_add (str_list_type *l, char *s) +{ + STR_LIST_LENGTH (*l)++; + XRETALLOC (STR_LIST (*l), STR_LIST_LENGTH (*l), char *); + STR_LIST_LAST_ELT (*l) = s; +} + +/* May as well save some reallocations and do everything in a chunk + instead of calling str_list_add on each element. */ + +void +str_list_concat (str_list_type *target, str_list_type more) +{ + unsigned e; + unsigned prev_len = STR_LIST_LENGTH (*target); + + STR_LIST_LENGTH (*target) += STR_LIST_LENGTH (more); + XRETALLOC (STR_LIST (*target), STR_LIST_LENGTH (*target), char *); + + for (e = 0; e < STR_LIST_LENGTH (more); e++) + STR_LIST_ELT (*target, prev_len + e) = STR_LIST_ELT (more, e); +} + +/* Free the list (but not the elements within it). */ + +void +str_list_free (str_list_type *l) +{ + if (STR_LIST (*l)) + { + free (STR_LIST (*l)); + STR_LIST (*l) = NULL; + } +} + +/* str-llist.c: Implementation of a linked list of strings. */ + +/* Add the new string STR to the end of the list L. */ + +void +str_llist_add (str_llist_type *l, char *str) +{ + str_llist_elt_type *e; + str_llist_elt_type *new_elt = XTALLOC1 (str_llist_elt_type); + + /* The new element will be at the end of the list. */ + STR_LLIST (*new_elt) = str; + STR_LLIST_MOVED (*new_elt) = 0; + STR_LLIST_NEXT (*new_elt) = NULL; + + /* Find the current end of the list. */ + for (e = *l; e && STR_LLIST_NEXT (*e); e = STR_LLIST_NEXT (*e)) + ; + + if (!e) + *l = new_elt; + else + STR_LLIST_NEXT (*e) = new_elt; +} + +/* Move an element towards the top. The idea is that when a file is + found in a given directory, later files will likely be in that same + directory, and looking for the file in all the directories in between + is thus a waste. */ + +void +str_llist_float (str_llist_type *l, str_llist_elt_type *mover) +{ + str_llist_elt_type *last_moved, *unmoved; + + /* If we've already moved this element, never mind. */ + if (STR_LLIST_MOVED (*mover)) + return; + + /* Find the first unmoved element (to insert before). We're + guaranteed this will terminate, since MOVER itself is currently + unmoved, and it must be in L (by hypothesis). */ + for (last_moved = NULL, unmoved = *l; STR_LLIST_MOVED (*unmoved); + last_moved = unmoved, unmoved = STR_LLIST_NEXT (*unmoved)) + ; + + /* If we are the first unmoved element, nothing to relink. */ + if (unmoved != mover) + { /* Remember `mover's current successor, so we can relink `mover's + predecessor to it. */ + str_llist_elt_type *before_mover; + str_llist_elt_type *after_mover = STR_LLIST_NEXT (*mover); + + /* Find `mover's predecessor. */ + for (before_mover = unmoved; STR_LLIST_NEXT (*before_mover) != mover; + before_mover = STR_LLIST_NEXT (*before_mover)) + ; + + /* `before_mover' now links to `after_mover'. */ + STR_LLIST_NEXT (*before_mover) = after_mover; + + /* Insert `mover' before `unmoved' and after `last_moved' (or at + the head of the list). */ + STR_LLIST_NEXT (*mover) = unmoved; + if (!last_moved) + *l = mover; + else + STR_LLIST_NEXT (*last_moved) = mover; + } + + /* We've moved it. */ + STR_LLIST_MOVED (*mover) = 1; +} + +/* fn.c: arbitrarily long filenames (or just strings). */ + +/* /usr/local/lib/texmf/fonts/public/cm/pk/ljfour/cmr10.300pk is 58 + chars, so ASCII `K' seems a good choice. */ +#define CHUNK_SIZE 75 + +fn_type +fn_init (void) +{ + fn_type ret; + + FN_ALLOCATED (ret) = FN_LENGTH (ret) = 0; + FN_STRING (ret) = NULL; + + return ret; +} + +fn_type +fn_copy0 (const char *s, unsigned len) +{ + fn_type ret; + + FN_ALLOCATED (ret) = CHUNK_SIZE > len ? CHUNK_SIZE : len + 1; + FN_STRING (ret) = (char *) xmalloc (FN_ALLOCATED (ret)); + + strncpy (FN_STRING (ret), s, len); + FN_STRING (ret)[len] = 0; + FN_LENGTH (ret) = len + 1; + + return ret; +} + +/* Don't think we ever try to free something that might usefully be + empty, so give fatal error if nothing allocated. */ + +void +fn_free (fn_type *f) +{ + assert (FN_STRING (*f) != NULL); + free (FN_STRING (*f)); + FN_STRING (*f) = NULL; + FN_ALLOCATED (*f) = 0; + FN_LENGTH (*f) = 0; +} + +/* An arithmetic increase seems more reasonable than geometric. We + don't increase the length member since it may be more convenient for + the caller to add than subtract when appending the stuff that will + presumably follow. */ + +static void +grow (fn_type *f, unsigned len) +{ + while (FN_LENGTH (*f) + len > FN_ALLOCATED (*f)) + { + FN_ALLOCATED (*f) += CHUNK_SIZE; + XRETALLOC (FN_STRING (*f), FN_ALLOCATED (*f), char); + } +} + +void +fn_1grow (fn_type *f, char c) +{ + grow (f, 1); + FN_STRING (*f)[FN_LENGTH (*f)] = c; + FN_LENGTH (*f)++; +} + +void +fn_grow (fn_type *f, void *source, unsigned len) +{ + grow (f, len); + strncpy (FN_STRING (*f) + FN_LENGTH (*f), (char *) source, len); + FN_LENGTH (*f) += len; +} + +void +fn_str_grow (fn_type *f, const char *s) +{ + unsigned more_len = strlen (s); + grow (f, more_len); + strcat (FN_STRING (*f), s); + FN_LENGTH (*f) += more_len; +} + +void +fn_shrink_to (fn_type *f, unsigned loc) +{ + assert (FN_LENGTH (*f) > loc); + FN_STRING (*f)[loc] = 0; + FN_LENGTH (*f) = loc + 1; +} + +/* variable.c: variable expansion. */ + +/* Here's the simple one, when a program just wants a value. */ + +char * +kpse_var_value (const char *var) +{ + char *ret = getenv (var); + + if (ret) + ret = kpse_var_expand (ret); + +#ifdef KPSE_DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_VARS)) + DEBUGF2("variable: %s = %s\n", var, ret ? ret : "(nil)"); +#endif + + return ret; +} + +/* We have to keep track of variables being expanded, otherwise + constructs like TEXINPUTS = $TEXINPUTS result in an infinite loop. + (Or indirectly recursive variables, etc.) Our simple solution is to + add to a list each time an expansion is started, and check the list + before expanding. */ + +typedef struct { + const char *var; + int expanding; +} expansion_type; +static expansion_type *expansions; /* The sole variable of this type. */ +static unsigned expansion_len = 0; + +static void +expanding (const char *var, int xp) +{ + unsigned e; + for (e = 0; e < expansion_len; e++) { + if (STREQ (expansions[e].var, var)) { + expansions[e].expanding = xp; + return; + } + } + + /* New variable, add it to the list. */ + expansion_len++; + XRETALLOC (expansions, expansion_len, expansion_type); + expansions[expansion_len - 1].var = xstrdup (var); + expansions[expansion_len - 1].expanding = xp; +} + + +/* Return whether VAR is currently being expanding. */ + +static int +expanding_p (const char *var) +{ + unsigned e; + for (e = 0; e < expansion_len; e++) { + if (STREQ (expansions[e].var, var)) + return expansions[e].expanding; + } + + return 0; +} + +/* Append the result of value of `var' to EXPANSION, where `var' begins + at START and ends at END. If `var' is not set, do not complain. + This is a subroutine for the more complicated expansion function. */ + +static void +expand (fn_type *expansion, const char *start, const char *end) +{ + char *value; + unsigned len = end - start + 1; + char *var = (char *) xmalloc (len + 1); + strncpy (var, start, len); + var[len] = 0; + + if (expanding_p (var)) { + WARNING1 ("kpathsea: variable `%s' references itself (eventually)", var); + } else { + /* Check for an environment variable. */ + value = getenv (var); + + if (value) { + expanding (var, 1); + value = kpse_var_expand (value); + expanding (var, 0); + fn_grow (expansion, value, strlen (value)); + free (value); + } + + free (var); + } +} + +/* Can't think of when it would be useful to change these (and the + diagnostic messages assume them), but ... */ +#ifndef IS_VAR_START /* starts all variable references */ +#define IS_VAR_START(c) ((c) == '$') +#endif +#ifndef IS_VAR_CHAR /* variable name constituent */ +#define IS_VAR_CHAR(c) (isalnum (c) || (c) == '_') +#endif +#ifndef IS_VAR_BEGIN_DELIMITER /* start delimited variable name (after $) */ +#define IS_VAR_BEGIN_DELIMITER(c) ((c) == '{') +#endif +#ifndef IS_VAR_END_DELIMITER +#define IS_VAR_END_DELIMITER(c) ((c) == '}') +#endif + + +/* Maybe we should support some or all of the various shell ${...} + constructs, especially ${var-value}. */ + +char * +kpse_var_expand (const char *src) +{ + const char *s; + char *ret; + fn_type expansion; + expansion = fn_init (); + + /* Copy everything but variable constructs. */ + for (s = src; *s; s++) { + if (IS_VAR_START (*s)) { + s++; + + /* Three cases: `$VAR', `${VAR}', `$<anything-else>'. */ + if (IS_VAR_CHAR (*s)) { + /* $V: collect name constituents, then expand. */ + const char *var_end = s; + + do { + var_end++; + } while (IS_VAR_CHAR (*var_end)); + + var_end--; /* had to go one past */ + expand (&expansion, s, var_end); + s = var_end; + + } else if (IS_VAR_BEGIN_DELIMITER (*s)) { + /* ${: scan ahead for matching delimiter, then expand. */ + const char *var_end = ++s; + + while (*var_end && !IS_VAR_END_DELIMITER (*var_end)) + var_end++; + + if (! *var_end) { + WARNING1 ("%s: No matching } for ${", src); + s = var_end - 1; /* will incr to null at top of loop */ + } else { + expand (&expansion, s, var_end - 1); + s = var_end; /* will incr past } at top of loop*/ + } + + } else { + /* $<something-else>: error. */ + WARNING2 ("%s: Unrecognized variable construct `$%c'", src, *s); + /* Just ignore those chars and keep going. */ + } + } else + fn_1grow (&expansion, *s); + } + fn_1grow (&expansion, 0); + + ret = FN_STRING (expansion); + return ret; +}