Mercurial > hg > octave-terminal
changeset 1268:76a0c05089d4
[project @ 1995-04-20 19:15:51 by jwe]
Initial revision
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/kpathsea/absolute.c @@ -0,0 +1,51 @@ +/* absolute.c: Test if a filename is absolute or explicitly relative. + +Copyright (C) 1993, 94 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/absolute.h> +#include <kpathsea/c-pathch.h> + +#ifdef DOS +#include <kpathsea/c-ctype.h> /* For ISALPHA */ +#endif /* DOS */ + + +/* Sorry this is such a system-dependent mess, but I can't see any way + to usefully generalize. */ + +boolean +kpse_absolute_p P2C(const_string, filename, boolean, relative_ok) +{ +#ifdef VMS +#include <string.h> + return strcspn (filename, "]>:") != strlen (filename); +#else /* not VMS */ + boolean absolute = IS_DIR_SEP (*filename) +#ifdef DOS + || ISALPHA (*filename) && filename[1] == ':' +#endif /* DOS */ + ; + boolean explicit_relative + = relative_ok && (*filename == '.' + && (IS_DIR_SEP (filename[1]) + || (filename[1] == '.' && IS_DIR_SEP (filename[2])))); + + return absolute || explicit_relative; +#endif /* not VMS */ +}
new file mode 100644 --- /dev/null +++ b/kpathsea/basename.c @@ -0,0 +1,51 @@ +/* basename.c: return the last element in a path. + +Copyright (C) 1992, 94, 95 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Have to include this first to get c-auto.h. */ +#include <kpathsea/config.h> + +#ifndef HAVE_BASENAME /* rest of file */ + +#include <kpathsea/c-pathch.h> + +/* Return NAME with any leading path stripped off. This returns a + pointer into NAME. For example, `basename ("/foo/bar.baz")' + returns "bar.baz". */ + +const_string +basename P1C(const_string, name) +{ + const_string base = NULL; + unsigned len = strlen (name); + + for (len = strlen (name); len > 0; len--) + { + if (IS_DIR_SEP (name[len - 1])) + { + base = name + len; + break; + } + } + + if (!base) + base = name; + + return base; +} + +#endif /* not HAVE_BASENAME */
new file mode 100644 --- /dev/null +++ b/kpathsea/cnf.c @@ -0,0 +1,190 @@ +/* cnf.c: read config files. + +Copyright (C) 1994 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-fopen.h> +#include <kpathsea/c-ctype.h> +#include <kpathsea/cnf.h> +#include <kpathsea/db.h> +#include <kpathsea/hash.h> +#include <kpathsea/line.h> +#include <kpathsea/paths.h> +#include <kpathsea/pathsearch.h> +#include <kpathsea/tex-file.h> +#include <kpathsea/variable.h> + + +/* By using our own hash table, instead of the environment, we + complicate variable expansion (because now we have to look in two + places), but we don't bang so heavily on the system. DOS and System + V have very limited environment space. Also, this way + `kpse_init_format' can distinguish between values originating from + the cnf file and ones from environment variables, which can be useful + for users trying to figure out what's going on. */ +#ifndef CNF_HASH_SIZE +#define CNF_HASH_SIZE 751 +#endif +static hash_table_type cnf_hash; + +/* Do a single line in a cnf file: if it's blank or a comment, skip it. + Otherwise, parse <variable>[.<program>] [=] <value>. Do + this even if the <variable> is already set in the environment, since + the envvalue might contain a trailing :, in which case we'll be + looking for the cnf value. */ + +static void +do_line P1C(string, line) +{ + unsigned len; + string start; + string value, var; + + /* Skip leading whitespace. */ + while (ISSPACE (*line)) + line++; + + /* More to do only if we have non-comment material left. */ + if (*line == 0 || *line == '%' || *line == '#') + return; + + /* The variable name is everything up to the next space or = or `.'. */ + start = line; + while (!ISSPACE (*line) && *line != '=' && *line != '.') + line++; + + /* `line' is now one character past the end of the variable name. */ + len = line - start; + var = xmalloc (len + 1); + strncpy (var, start, len); + var[len] = 0; + + /* If the variable is qualified with a program name, we might be + ignoring it. */ + while (ISSPACE (*line)) + line++; + if (*line == '.') + { /* Skip spaces, then everything up to the next space or =. */ + string prog; + extern string program_invocation_short_name; /* must be set by main */ + + line++; + while (ISSPACE (*line)) + line++; + start = line; + while (!ISSPACE (*line) && *line != '=') + line++; + + /* It's annoying to repeat all this, but making a tokenizing + subroutine would be just as long and annoying. */ + len = line - start; + prog = xmalloc (len + 1); + strncpy (prog, start, len); + prog[len] = 0; + + /* If we are running `prog', fine; otherwise, we're done. */ + assert (program_invocation_short_name); + if (!STREQ (prog, program_invocation_short_name)) + { + free (var); + free (prog); + return; + } + } + + /* Skip whitespace, an optional =, more whitespace. */ + while (ISSPACE (*line)) + line++; + if (*line == '=') + { + line++; + while (ISSPACE (*line)) + line++; + } + + /* Everything up to the next whitespace or eol is the value. */ + start = line; + while (*line && !ISSPACE (*line)) + line++; + len = line - start; + + value = xmalloc (len + 1); + strncpy (value, start, len); + value[len] = 0; + + /* For cnf files, multiple values for a single variable make no sense, + but instead of removing them, we'll just take the most recent in + `kpse_cnf_get'. Thus, we are assuming here that `hash_insert' puts + the most recent entries in first. */ + hash_insert (&cnf_hash, var, value); + + /* We could check that anything remaining is preceded by a comment + character, but let's not bother. */ +} + +/* Read all the configuration files in the path. */ + +static void +read_files P1H(void) +{ + string *cnf_files; + const_string cnf_path = kpse_init_format (kpse_cnf_format); + + cnf_hash = hash_create (CNF_HASH_SIZE); + + for (cnf_files = kpse_all_path_search (cnf_path, + kpse_format_info[kpse_cnf_format].program); + cnf_files && *cnf_files; cnf_files++) + { + string line; + string cnf_filename = *cnf_files; + FILE *cnf_file = xfopen (cnf_filename, FOPEN_R_MODE); + + while ((line = read_line (cnf_file)) != NULL) + { + do_line (line); + free (line); + } + + xfclose (cnf_file, cnf_filename); + } + + /* After (*after*) reading the cnf files, expand the db directory, for + use by `elt_in_db' in pathsearch.c. The default value of $TEXMF + has to be able to get TEXMF from a cnf file, therefore in the + `kpse_all_path_search' call above, we do not have DB_DIR. */ + kpse_db_dir = kpse_var_expand (KPSE_DB_DIR); + if (! *kpse_db_dir) + kpse_db_dir = kpse_var_expand (DEFAULT_TEXMF); +} + +/* Read the cnf files on the first call. Return the first value in the + returned list -- this will be from the last-read cnf file. */ + +string +kpse_cnf_get P1C(const_string, name) +{ + string *ret_list; + + if (cnf_hash.size == 0) + read_files (); + + ret_list = hash_lookup (cnf_hash, name); + + return ret_list ? *ret_list : NULL; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/concat.c @@ -0,0 +1,33 @@ +/* concat.c: dynamic string concatenation. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + + +/* Return the concatenation of S1 and S2. See `concatn.c' for a + `concatn', which takes a variable number of arguments. */ + +string +concat P2C(const_string, s1, const_string, s2) +{ + string answer = (string) xmalloc (strlen (s1) + strlen (s2) + 1); + strcpy (answer, s1); + strcat (answer, s2); + + return answer; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/concat3.c @@ -0,0 +1,32 @@ +/* concat3.c: concatenate three strings. + +Copyright (C) 1992 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + + +string +concat3 P3C(const_string, s1, const_string, s2, const_string, s3) +{ + string answer + = (string) xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1); + strcpy (answer, s1); + strcat (answer, s2); + strcat (answer, s3); + + return answer; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/concatn.c @@ -0,0 +1,68 @@ +/* concatn.c: Concatenate an arbitrary number of strings. + +Copyright (C) 1993 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/concatn.h> + + +/* 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. */ + +string +concatn PVAR1C(const_string, str1, ap) +{ + string arg; + string ret; + + if (!str1) + return NULL; + + ret = xstrdup (str1); + + while ((arg = va_arg (ap, string)) != NULL) + { + ret = concat (ret, arg); + } + va_end (ap); + + return ret; +}} + +#ifdef TEST +int +main () +{ + printf ("null = \"%s\"\n", concatn (NULL)); + printf ("\"a\" = \"%s\"\n", concatn ("a", NULL)); + printf ("\"ab\" = \"%s\"\n", concatn ("a", "b", NULL)); + printf ("\"abc\" = \"%s\"\n", concatn ("a", "b", "c", NULL)); + printf ("\"abcd\" = \"%s\"\n", concatn ("ab", "cd", NULL)); + printf ("\"abcde\" = \"%s\"\n", concatn ("ab", "c", "de", NULL)); + printf ("\"abcdef\" = \"%s\"\n", concatn ("", "a", "", "bcd", "ef", NULL)); + return 0; +} + +#endif /* TEST */ + + +/* +Local variables: +standalone-compile-command: "gcc -posix -g -I. -I.. -DTEST concatn.c kpathsea.a" +End: +*/
new file mode 100644 --- /dev/null +++ b/kpathsea/db.c @@ -0,0 +1,269 @@ +/* db.c: an external database to avoid filesystem lookups. + +Copyright (C) 1994 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-fopen.h> +#include <kpathsea/c-pathch.h> +#include <kpathsea/db.h> +#include <kpathsea/hash.h> +#include <kpathsea/line.h> +#include <kpathsea/readable.h> +#include <kpathsea/str-list.h> +#include <kpathsea/variable.h> + +/* See comments in `read_files' in cnf.c. */ +string kpse_db_dir = NULL; + +/* The hash table for ls-R. */ +static hash_table_type db; + + +/* If no DB_FILE, return false (maybe they aren't using this feature). + Otherwise, build the db and return true. */ + +static boolean +db_build P1C(hash_table_type *, table) +{ + string line; + unsigned dir_count = 0, file_count = 0; /* for debugging */ + string cur_dir = NULL; /* First thing in ls-R might be a filename. */ + string db_filename = concat3 (kpse_db_dir, DIR_SEP_STRING, KPSE_DB_NAME); + FILE *db_file = fopen (db_filename, FOPEN_R_MODE); + + if (db_file) + { + while ((line = read_line (db_file)) != NULL) + { + unsigned len = strlen (line); + /* A line like `/foo:' = new dir foo */ + if (IS_DIR_SEP (line[0]) && line[len - 1] == ':') + { + cur_dir = xstrdup (line); + cur_dir[len - 1] = DIR_SEP; + dir_count++; + } + else if (line[0] != 0 && cur_dir) /* other nonblank line */ + { /* New hash table entry with a key of `line' and a data of + `cur_dir'. Already-existing identical keys are ok, since + a file named `foo' can be in more than one directory. + Since it doesn't hurt, share the directory name string + among all the files in the directory. */ + hash_insert (table, xstrdup (line), cur_dir); + file_count++; + } + /* else ignore blank lines */ + + free (line); + } + xfclose (db_file, db_filename); + + if (file_count == 0) + { + fprintf (stderr, "kpathsea: no usable entries in %s; see the\n", + db_filename); + fprintf (stderr, "kpathsea: manual for how to generate ls-R.\n"); + } + +#ifdef DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) + { + /* Don't make this a debugging bit, since the output is so + voluminous, and being able to specify -1 is too useful. + Instead, let people who want it run the program under + a debugger and change the variable that way. */ + boolean print_hash_table = false; + + DEBUGF3 ("%u entries (in %d directories) read from %s.\n", + file_count, dir_count, db_filename); + if (print_hash_table) + { + DEBUGF ("Hash table built from ls-R:"); + hash_print (*table); + } + fflush (stderr); + } +#endif + } + + free (db_filename); + return db_file != NULL; +} + + +/* Insert FNAME into the hash table. This is for files that get built + during a run. We wouldn't want to reread all of ls-R, even if it got + rebuilt. */ + +void +db_insert P1C(const_string, passed_fname) +{ + /* We should always have called `kpse_db_search' before this. */ + assert (db.size > 0); + + /* But we might not have found ls-R; in that case, we'll have cleared + the buckets (but left the size). */ + if (db.buckets) + { + const_string dir_part; + string fname = xstrdup (passed_fname); + string baseptr = (string) basename (fname); + const_string file_part = xstrdup (baseptr); + + *baseptr = '\0'; /* Chop off the filename. */ + dir_part = fname; /* That leaves the dir, with the trailing /. */ + + hash_insert (&db, file_part, dir_part); + } +} + +/* Return true if FILENAME could be in PATH_ELT, i.e., if the directory + part of FILENAME matches PATH_ELT. Have to consider // wildcards, but + $ and ~ expansion have already been done. */ + +static boolean +match P2C(const_string, filename, const_string, path_elt) +{ + const_string original_filename = filename; + boolean matched = false; + boolean done = false; + + for (; !done && *filename && *path_elt; filename++, path_elt++) + { + if (*filename == *path_elt) /* normal character match */ + ; + + else if (IS_DIR_SEP (*path_elt) /* at // */ + && original_filename < filename && IS_DIR_SEP (path_elt[-1])) + { + path_elt++; /* get past second / */ + if (*path_elt == 0) + { /* Have a trailing //, which matches anything. We + could make this part of the other case, but it seems + pointless to do the extra work. */ + matched = true; + done = true; + } + else + { /* intermediate //, have to match rest of PATH_ELT */ + for (; !matched && *filename; filename++) + { /* Try matching at each possible character. */ + if (*filename == *path_elt) + matched = match (filename, path_elt); + } + } + } + + else /* normal character nonmatch, quit */ + done = true; + } + + /* If we've reached the end of PATH_ELT, and we're at the last + component of FILENAME, we've matched. */ + if (!matched && *path_elt == 0 && IS_DIR_SEP (*filename)) + { + filename++; + while (*filename && !IS_DIR_SEP (*filename)) + filename++; + matched = *filename == 0; + } + + return matched; +} + +/* Don't bother implementing a search path for the database itself. We + get multiple databases, sort of, with the $TEXMF value for DB_DIR. */ + +str_list_type * +kpse_db_search P3C(const_string, name, const_string, orig_path_elt, + boolean, all) +{ + string *db_dirs, *orig_dirs; + const_string last_slash; + string path_elt; + boolean done; + str_list_type *ret; + + /* Hash up the database if this is the first call. */ + if (db.size == 0) + { + db = hash_create (7603); /* What the heck, sparse is ok. */ + if (!db_build (&db)) + { /* If db can't be built, leave `size' nonzero (so we don't + rebuild it), but clear `buckets' (so we don't look in it). */ + free (db.buckets); + db.buckets = NULL; + } + } + + /* If we failed to build the database, quit. */ + if (db.buckets == NULL) + return NULL; + + /* When tex-glyph.c calls us looking for, e.g., dpi600/cmr10.pk, we + won't find it unless we change NAME to just `cmr10.pk' and append + `/dpi600' to PATH_ELT. We are justified in using a literal `/' + here, since that's what tex-glyph.c unconditionally uses in + DPI_BITMAP_SPEC. But don't do anything if the / begins NAME; that + should never happen. */ + last_slash = strrchr (name, '/'); + if (last_slash && last_slash != name) + { + unsigned len = last_slash - name + 1; + string dir_part = xmalloc (len); + strncpy (dir_part, name, len - 1); + dir_part[len - 1] = 0; + path_elt = concat3 (orig_path_elt, "/", dir_part); + name = last_slash + 1; + } + else + path_elt = (string) orig_path_elt; + + /* We have a db. Look up NAME. */ + orig_dirs = db_dirs = hash_lookup (db, name); + + done = false; + ret = XTALLOC1 (str_list_type); + *ret = str_list_init (); + + /* For each filename found, see if it matches the path element. For + example, if we have ../cx/cmr10.300pk and .../ricoh/cmr10.300pk, + and the path looks like .../cx, we don't want the ricoh file. */ + while (!done && db_dirs && *db_dirs) + { + string db_file = concat (*db_dirs, name); + + if (match (db_file, path_elt) && kpse_readable_file (db_file)) + { + str_list_add (ret, db_file); + if (!all) done = true; + } + else + free (db_file); + + /* On to the next directory, if any. */ + db_dirs++; + } + + /* This is just the space for the pointers, not the strings. */ + if (orig_dirs && *orig_dirs) free (orig_dirs); + + /* If we had to break up NAME, free the temporary PATH_ELT. */ + if (path_elt != orig_path_elt) free (path_elt); + return ret; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/debug.c @@ -0,0 +1,52 @@ +/* debug.c: Help the user discover what's going on. + +Copyright (C) 1993, 94 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#ifdef DEBUG + +unsigned kpathsea_debug = 0; + +/* If the real definitions of fopen or fclose are macros, we lose -- the + #undef won't restore them. */ + +FILE * +fopen P2C(const_string, filename, const_string, 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 P1C(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 /* DEBUG */
new file mode 100644 --- /dev/null +++ b/kpathsea/dir.c @@ -0,0 +1,85 @@ +/* dir.c: directory operations. + +Copyright (C) 1992, 93, 94 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-dir.h> +#include <kpathsea/c-stat.h> +#include <kpathsea/hash.h> + + +/* Return true if FN is a directory or a symlink to a directory, + false if not. */ + +boolean +dir_p P1C(const_string, fn) +{ + struct stat stats; + return stat (fn, &stats) == 0 && S_ISDIR (stats.st_mode); +} + + +/* 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 P1C(const_string, fn) +{ + static hash_table_type link_table; + string *hash_ret; + long ret; + + if (link_table.size == 0) + link_table = hash_create (457); + +#ifdef 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 = true; +#endif + + hash_ret = hash_lookup (link_table, fn); + +#ifdef DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) + kpse_debug_hash_lookup_int = false; +#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 : -1; + + /* It's up to us to copy the value. */ + hash_insert (&link_table, xstrdup (fn), (const_string) ret); + +#ifdef DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_STAT)) + DEBUGF2 ("dir_links(%s) => %ld\n", fn, ret); +#endif + } + + return ret; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/elt-dirs.c @@ -0,0 +1,367 @@ +/* elt-dirs.c: Translate a path element to its corresponding director{y,ies}. + +Copyright (C) 1993, 94 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-pathch.h> +#include <kpathsea/expand.h> +#include <kpathsea/fn.h> +#include <kpathsea/pathsearch.h> +#include <kpathsea/xopendir.h> + +/* To avoid giving prototypes for all the routines and then their real + definitions, we give all the subroutines first. The entry point is + the last routine in the file. */ + +/* Make a copy of DIR (unless it's null) and save it in L. Ensure that + DIR ends with a DIR_SEP for the benefit of later searches. */ + +static void +dir_list_add P2C(str_llist_type *, l, const_string, dir) +{ + string saved_dir + = IS_DIR_SEP (dir[strlen (dir) - 1]) + ? xstrdup (dir) + : concat (dir, DIR_SEP_STRING); + + str_llist_add (l, saved_dir); +} + + +/* If DIR is a directory, add it to the list L. */ + +static void +checked_dir_list_add P2C(str_llist_type *, l, const_string, dir) +{ + if (dir_p (dir)) + dir_list_add (l, dir); +} + +/* The cache. Typically, several paths have the same element; for + example, /usr/local/lib/texmf/fonts//. We don't want to compute the + expansion of such a thing more than once. Even though we also cache + the dir_links call, that's not enough -- without this path element + caching as well, the execution time doubles. */ + +typedef struct +{ + const_string key; + str_llist_type *value; +} cache_entry; + +static cache_entry *the_cache = NULL; +static unsigned cache_length = 0; + + +/* Associate KEY with VALUE. We implement the cache as a simple linear + list, since it's unlikely to ever be more than a dozen or so elements + long. We don't bother to check here if PATH has already been saved; + we always add it to our list. We copy KEY but not VALUE; not sure + that's right, but it seems to be all that's needed. */ + +static void +cache P2C(const_string, key, str_llist_type *, value) +{ + cache_length++; + XRETALLOC (the_cache, cache_length, cache_entry); + the_cache[cache_length - 1].key = xstrdup (key); + the_cache[cache_length - 1].value = value; +} + + +/* To retrieve, just check the list in order. */ + +static str_llist_type * +cached P1C(const_string, key) +{ + unsigned p; + + for (p = 0; p < cache_length; p++) + { + if (STREQ (the_cache[p].key, key)) + return the_cache[p].value; + } + + return NULL; +} + +/* Handle the magic path constructs. */ + +/* Declare recursively called routine. */ +static void expand_elt P3H(str_llist_type *, const_string, unsigned); + + +/* POST is a pointer into the original element (which may no longer be + ELT) to just after the doubled DIR_SEP, perhaps to the null. Append + subdirectories of ELT (up to ELT_LENGTH, which must be a /) to + STR_LIST_PTR. */ + +static void +do_subdir P4C(str_llist_type *, str_list_ptr, const_string, elt, + unsigned, elt_length, const_string, post) +{ + DIR *dir; + struct dirent *e; + fn_type name; + + /* Some old compilers don't allow aggregate initialization. */ + name = fn_copy0 (elt, elt_length); + + assert (IS_DIR_SEP (elt[elt_length - 1])); + + /* If we can't open it, quit. */ + dir = opendir (FN_STRING (name)); + if (dir == NULL) + { + fn_free (&name); + return; + } + + /* Include top level before subdirectories, if nothing to match. */ + if (*post == 0) + dir_list_add (str_list_ptr, FN_STRING (name)); + else + { /* If we do have something to match, see if it exists. For + example, POST might be `pk/ljfour', and they might have a + directory `$TEXMF/fonts/pk/ljfour' that we should find. */ + fn_str_grow (&name, post); + if (dir_p (FN_STRING (name))) + dir_list_add (str_list_ptr, FN_STRING (name)); + fn_shrink_to (&name, elt_length); + } + + while ((e = readdir (dir)) != NULL) + { /* If it begins with a `.', never mind. (This allows ``hidden'' + directories that the algorithm won't find.) */ + if (e->d_name[0] != '.') + { + int links; + + /* Construct the potential subdirectory name. */ + fn_str_grow (&name, e->d_name); + + /* If we can't stat it, or if it isn't a directory, continue. */ + links = dir_links (FN_STRING (name)); + + if (links >= 0) + { + unsigned potential_len = FN_LENGTH (name); + + /* It's a directory, so append the separator. */ + fn_str_grow (&name, DIR_SEP_STRING); + + if (*post != 0) + { + fn_str_grow (&name, post); + /* Unfortunately we can't check if the new element is + a leaf directory, because we don't have a directory + name here, we just have a path spec. This means we + may descend into a leaf directory cm/pk, if the + spec is ...fonts//pk//. */ + expand_elt (str_list_ptr, FN_STRING (name), potential_len); + fn_shrink_to (&name, potential_len); + } + + /* Should we recurse? To see if the subdirectory is a + leaf, check if it has two links (one for . and one for + ..). This means that symbolic links to directories do + not affect the leaf-ness. This is arguably wrong, but + the only alternative I know of is to stat every entry + in the directory, and that is unacceptably slow. + + The #ifdef here makes all this configurable at + compile-time, so that if we're using VMS directories or + some such, we can still find subdirectories, even if it + is much slower. */ +#ifdef UNIX_ST_NLINK + if (links > 2) +#endif + /* All criteria are met; find subdirectories. */ + do_subdir (str_list_ptr, FN_STRING (name), + potential_len, post); +#ifdef UNIX_ST_NLINK + else if (*post == 0) + /* Nothing to match, no recursive subdirectories to + look for: we're done with this branch. Add it. */ + dir_list_add (str_list_ptr, FN_STRING (name)); +#endif + } + + /* Remove the directory entry we just checked from `name'. */ + fn_shrink_to (&name, elt_length); + } + } + + fn_free (&name); + xclosedir (dir); +} + + +/* Assume ELT is non-empty and non-NULL. Return list of corresponding + directories (with no terminating NULL entry) in STR_LIST_PTR. Start + looking for magic constructs at START. */ + +static void +expand_elt P3C(str_llist_type *, str_list_ptr, const_string, elt, + unsigned, start) +{ + boolean found_special = false; + const_string dir = elt + start; + + while (*dir != 0) + { + if (IS_DIR_SEP (*dir)) + { + /* If two consecutive directory separators, find subdirectories. */ + if (IS_DIR_SEP (dir[1])) + { + do_subdir (str_list_ptr, elt, dir - elt + 1, dir + 2); + found_special = true; + } +#if 0 +/* Maybe eventually I'll implement this, but probably not. */ + /* If /?, make following component optional. */ + else if (dir[1] == '?') + do_optional (str_list_ptr, elt, dir - elt + 1, dir + 2); +#endif + /* No special stuff at this slash. Keep going. */ + } + + dir++; + } + + if (!found_special) + /* When we reach the end of ELT, it will be a normal filename. */ + checked_dir_list_add (str_list_ptr, elt); +} + +/* Here is the entry point. Returns directory list for ELT. */ + +str_llist_type * +kpse_element_dirs P1C(const_string, elt) +{ + str_llist_type *ret; + + /* If given nothing, return nothing. */ + if (!elt) + return NULL; + + /* If we've already cached the answer for ELT, return it. */ + ret = cached (elt); + if (ret) + return ret; + + /* We're going to have a real directory list to return. */ + ret = XTALLOC1 (str_llist_type); + *ret = NULL; + + /* If ELT is the empty string, just return cwd. */ + if (*elt == 0) + { /* Some old compilers do not support aggregate initialization. */ + char cwd[3]; + cwd[0] = '.'; + cwd[1] = DIR_SEP; + cwd[2] = 0; + + checked_dir_list_add (ret, cwd); + } + + /* OK, so much for the trivial cases. We handle the hard case in + a subroutine. */ + else + expand_elt (ret, elt, 0); + + /* Remember the directory list we just found, in case future calls are + made with the same ELT. */ + cache (elt, ret); + +#ifdef DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_EXPAND)) + { + DEBUGF1 ("path element %s =>", elt); + if (ret) + { + str_llist_elt_type *e; + for (e = *ret; e; e = STR_LLIST_NEXT (*e)) + fprintf (stderr, " %s", STR_LLIST (*e)); + } + putc ('\n', stderr); + fflush (stderr); + } +#endif + + return ret; +} + +#ifdef TEST + +void +print_element_dirs (const_string elt) +{ + str_llist_type *dirs; + + printf ("Directories of %s:\t", elt ? elt : "(null)"); + fflush (stdout); + + dirs = kpse_element_dirs (elt); + + if (!dirs) + printf ("(null)"); + else + { + str_llist_elt_type *dir; + for (dir = *dirs; dir; dir = STR_LLIST_NEXT (*dir)) + { + string d = STR_LLIST (*dir); + printf ("%s ", *d ? d : "`'"); + } + } + + putchar ('\n'); +} + +int +main () +{ + /* DEBUG_SET (DEBUG_STAT); */ + + /* All lists end with NULL. */ + print_element_dirs (NULL); /* */ + print_element_dirs (""); /* ./ */ + print_element_dirs ("/k"); /* */ + print_element_dirs (".//"); /* ./ ./archive/ */ + print_element_dirs (".//archive"); /* ./ ./archive/ */ + print_element_dirs ("/tmp/fonts//"); /* no need to stat anything */ + print_element_dirs ("/usr/local/lib/tex/fonts//"); /* lots */ + print_element_dirs ("/usr/local/lib/tex/fonts//times"); /* just one */ + print_element_dirs ("/usr/local/lib/tex/fonts//"); /* lots again [cache] */ + print_element_dirs ("~karl"); /* tilde expansion */ + print_element_dirs ("$karl"); /* variable expansion */ + print_element_dirs ("~${LOGNAME}"); /* both */ + + return 0; +} + +#endif /* TEST */ + + +/* +Local variables: +test-compile-command: "gcc -g -I. -I.. -DTEST elt-dirs.c kpathsea.a" +End: +*/
new file mode 100644 --- /dev/null +++ b/kpathsea/expand.c @@ -0,0 +1,69 @@ +/* expand.c: general expansion. + +Copyright (C) 1993, 94 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-pathch.h> +#include <kpathsea/expand.h> +#include <kpathsea/pathsearch.h> +#include <kpathsea/tilde.h> +#include <kpathsea/variable.h> + + +/* Do variable expansion so ~${USER} will work. (Besides, it's what the + shells do.) */ + +string +kpse_expand P1C(const_string, s) +{ + string var_expansion = kpse_var_expand (s); + string tilde_expansion = kpse_tilde_expand (var_expansion); + + /* `kpse_var_expand' always gives us new memory; `kpse_tilde_expand' + doesn't, necessarily. So be careful that we don't free what we are + about to return. */ + if (tilde_expansion != var_expansion) + free (var_expansion); + + return tilde_expansion; +} + + +/* Be careful to not waste all the memory we allocate for each element. */ + +string +kpse_path_expand P1C(const_string, path) +{ + string elt; + string ret = xmalloc (1); /* so we can free it */ + *ret = 0; + + for (elt = kpse_path_element (path); elt; elt = kpse_path_element (NULL)) + { + string save_ret = ret; + string elt_exp = kpse_expand (elt); + ret = concat3 (ret, elt_exp, ENV_SEP_STRING); + free (elt_exp); + free (save_ret); + } + + /* Waste the last byte. */ + ret[strlen (ret) - 1] = 0; + + return ret; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/extend-fname.c @@ -0,0 +1,34 @@ +/* extend-fname.c: give a filename a suffix, if necessary. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + + +/* We may or may not return NAME. It's up to the caller not to assume + the return value is modifiable. */ + +string +extend_filename P2C(const_string, name, const_string, default_suffix) +{ + string new_s; + const_string suffix = find_suffix (name); + + new_s = suffix == NULL ? concat3 (name, ".", default_suffix) + : (string) name; + return new_s; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/file-p.c @@ -0,0 +1,37 @@ +/* file-p.c: file predicates. + +Copyright (C) 1992, 93, 94 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/xstat.h> + + +/* Test whether FILENAME1 and FILENAME2 are actually the same file. If + stat fails on either of the names, we return false, without error. */ + +boolean +same_file_p P2C(const_string, filename1, const_string, filename2) +{ + struct stat sb1, sb2; + /* These are put in variables only so the results can be inspected + under gdb. */ + int r1 = stat (filename1, &sb1); + int r2 = stat (filename2, &sb2); + + return r1 == 0 && r2 == 0 ? SAME_FILE_P (sb1, sb2) : false; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/find-suffix.c @@ -0,0 +1,43 @@ +/* find-suffix.c: return the stuff after a dot. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-pathch.h> + + +/* 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. */ + +string +find_suffix P1C(const_string, name) +{ + const_string slash_pos; + string dot_pos = strrchr (name, '.'); + + if (dot_pos == NULL) + return NULL; + + for (slash_pos = name + strlen (name) + 1; + slash_pos > dot_pos && !IS_DIR_SEP (*slash_pos); + slash_pos--) + ; + + return slash_pos > dot_pos ? NULL : dot_pos + 1; +} +
new file mode 100644 --- /dev/null +++ b/kpathsea/fn.c @@ -0,0 +1,119 @@ +/* fn.c: arbitrarily long filenames (or just strings). + +Copyright (C) 1993 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/fn.h> + + +/* /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 P1H(void) +{ + fn_type ret; + + FN_ALLOCATED (ret) = FN_LENGTH (ret) = 0; + FN_STRING (ret) = NULL; + + return ret; +} + + +fn_type +fn_copy0 P2C(const_string, s, unsigned, len) +{ + fn_type ret; + + FN_ALLOCATED (ret) = CHUNK_SIZE > len ? CHUNK_SIZE : len + 1; + FN_STRING (ret) = 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 P1C(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 P2C(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 P2C(fn_type *, f, char, c) +{ + grow (f, 1); + FN_STRING (*f)[FN_LENGTH (*f)] = c; + FN_LENGTH (*f)++; +} + + +void +fn_grow P3C(fn_type *, f, address, source, unsigned, len) +{ + grow (f, len); + strncpy (FN_STRING (*f) + FN_LENGTH (*f), source, len); + FN_LENGTH (*f) += len; +} + + +void +fn_str_grow P2C(fn_type *, f, const_string, s) +{ + unsigned more_len = strlen (s); + grow (f, more_len); + strcat (FN_STRING (*f), s); + FN_LENGTH (*f) += more_len; +} + + +void +fn_shrink_to P2C(fn_type *, f, unsigned, loc) +{ + assert (FN_LENGTH (*f) > loc); + FN_STRING (*f)[loc] = 0; + FN_LENGTH (*f) = loc + 1; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/fontmap.c @@ -0,0 +1,133 @@ +/* fontmap.c: read a file for additional font names. + +Copyright (C) 1993, 94 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-fopen.h> +#include <kpathsea/fontmap.h> +#include <kpathsea/hash.h> +#include <kpathsea/line.h> +#include <kpathsea/pathsearch.h> +#include <kpathsea/str-list.h> + + +/* Look up KEY in MAP; if it's not found, remove any suffix from KEY and + try again. */ + +string * +map_lookup P2C(hash_table_type, map, const_string, key) +{ + string suffix = find_suffix (key); + string *ret = hash_lookup (map, key); + + if (!ret) + { + /* OK, the original KEY didn't work. Let's check for the KEY without + an extension -- perhaps they gave foobar.tfm, but the mapping only + defines `foobar'. */ + if (suffix) + { + string base_key = remove_suffix (key); + + ret = hash_lookup (map, base_key); + + free (base_key); + } + } + + /* Append the original suffix, if we had one. */ + if (ret && suffix) + while (*ret) + { + *ret = extend_filename (*ret, suffix); + ret++; + } + + return ret; +} + +/* Open and read the mapping file MAP_FILENAME, putting its entries into + MAP. Comments begin with % and continue to the end of the line. Each + line of the file defines an entry: the first word is the real + filename (e.g., `ptmr'), the second word is the alias (e.g., + `Times-Roman'), and any subsequent words are ignored. .tfm is added + if either the filename or the alias have no extension. This is the + same order as in Dvips' psfonts.map; unfortunately, we can't have TeX + read that same file, since most of the real filenames start with an + `r', because of the virtual fonts Dvips uses. */ + +static void +map_file_parse P2C(hash_table_type *, map, const_string, map_filename) +{ + extern FILE *xfopen (); /* In xfopen.c. */ + char *l; + unsigned map_lineno = 0; + FILE *f = xfopen (map_filename, FOPEN_R_MODE); + + while ((l = read_line (f)) != NULL) + { + string filename; + string comment_loc = strchr (l, '%'); + + map_lineno++; + + /* Ignore anything after a %. */ + if (comment_loc) + *comment_loc = 0; + + /* If we don't have any filename, that's ok, the line is blank. */ + filename = strtok (l, " \t"); + if (filename) + { + string alias = strtok (NULL, " \t"); + + /* But if we have a filename and no alias, something's wrong. */ + if (alias == NULL || *alias == 0) + fprintf (stderr, "%s:%u: Alias missing for filename `%s'.\n", + map_filename, map_lineno, filename); + else + { /* We've got everything. Insert the new entry. */ + hash_insert (map, xstrdup (alias), xstrdup (filename)); + } + } + + free (l); + } + + xfclose (f, map_filename); +} + +/* Search for all the MAP_NAME's in PATH. */ + +#define MAP_NAME "texfonts.map" + +hash_table_type +map_create P1C(const_string, path) +{ + string *filenames = kpse_all_path_search (path, MAP_NAME); + hash_table_type map; /* some old compilers ... */ + map = hash_create (751); + + while (*filenames) + { + map_file_parse (&map, *filenames); + filenames++; + } + + return map; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/getopt.c @@ -0,0 +1,748 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 + Free Software Foundation, Inc. + + 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, 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, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. + Ditto for AIX 3.2 and <stdlib.h>. */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include <stdlib.h> +#endif /* GNU C library. */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include <string.h> +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (optstring) + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0) + optstring = _getopt_initialize (optstring); + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0')) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if (nameend - nextchar == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); + else + fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c); + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */
new file mode 100644 --- /dev/null +++ b/kpathsea/getopt1.c @@ -0,0 +1,180 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993, 1994 + Free Software Foundation, Inc. + + 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, 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, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "getopt.h" + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include <stdlib.h> +#else +char *getenv (); +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +#include <stdio.h> + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */
new file mode 100644 --- /dev/null +++ b/kpathsea/hash.c @@ -0,0 +1,171 @@ +/* hash.c: hash table operations. + +Copyright (C) 1994 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/hash.h> +#include <kpathsea/str-list.h> + + +/* The hash function. We go for simplicity here. */ + +static unsigned +hash P2C(hash_table_type, table, const_string, 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 + *key++) % table.size; + + return n; +} + +hash_table_type +hash_create P1C(unsigned, size) +{ + unsigned b; + hash_table_type ret; + ret.buckets = XTALLOC (size, hash_element_type *); + ret.size = size; + + /* calloc's zeroes aren't necessarily NULL, according to ANSI, so be + safe. (Not that I know of any exceptions in reality.) */ + 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 P3C(hash_table_type *, table, const_string, key, + const_string, 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 DEBUG +/* Print the hash values as integers if this is nonzero. */ +boolean kpse_debug_hash_lookup_int = false; +#endif + +string * +hash_lookup P2C(hash_table_type, table, const_string, 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 (STREQ (key, p->key)) + /* Cast because the general str_list_type shouldn't force const data. */ + str_list_add (&ret, (string) p->value); + + /* If we found anything, mark end of list with null. */ + if (STR_LIST (ret)) + str_list_add (&ret, NULL); + +#ifdef DEBUG + if (KPSE_DEBUG_P (KPSE_DEBUG_HASH)) + { + DEBUGF1 ("hash_lookup(%s) =>", key); + if (!STR_LIST (ret)) + fputs (" (null)\n", stderr); + else + { + string *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 P1C(hash_table_type, table) +{ + 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++; + printf ("%4d ", b); + + for (tb = bucket->next; tb != NULL; tb = tb->next) + len++; + printf (":%-5d", len); + total_elements += len; + + for (tb = bucket; tb != NULL; tb = tb->next) + printf (" %s=>%s", tb->key, tb->value); + + putchar ('\n'); + } + } + + printf ("%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); +}
new file mode 100644 --- /dev/null +++ b/kpathsea/itoa.c @@ -0,0 +1,29 @@ +/* itoa.c: convert an int to a string. + +Copyright (C) 1993 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + + +string +itoa P1C(int, i) +{ + char a[MAX_INT_LENGTH]; + + sprintf (a, "%d", i); + return xstrdup (a); +}
new file mode 100644 --- /dev/null +++ b/kpathsea/kdefault.c @@ -0,0 +1,120 @@ +/* kdefault.c: Expand extra colons. + (This is not named default.c because then the OSF/1 make tries to + make a program `default' from it, since we have a target `default'; + and OSF/1 make doesn't understand .PHONY.) + +Copyright (C) 1993, 94 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-pathch.h> +#include <kpathsea/default.h> + + +/* Check for leading colon first, then trailing, then doubled, since + that is fastest. Usually it will be leading or trailing. */ + +string +kpse_expand_default P2C(const_string, path, const_string, fallback) +{ + unsigned path_length; + string expansion; + + /* The default path better not be null. */ + assert (fallback); + + if (path == NULL) + expansion = (string) fallback; + + /* Solitary or leading :? */ + else if (IS_ENV_SEP (*path)) + { + expansion = path[1] == 0 ? (string) fallback : concat (fallback, path); + } + + /* Sorry about the assignment in the middle of the expression, but + conventions were made to be flouted and all that. I don't see the + point of calling strlen twice or complicating the logic just to + avoid the assignment (especially now that I've pointed it out at + such great length). */ + else if (path[(path_length = strlen (path)) - 1] == ENV_SEP) + expansion = concat (path, fallback); + + /* OK, not leading or trailing. Check for doubled. */ + else + { + const_string loc; + + /* What we'll return if we find none. */ + expansion = (string) path; + + for (loc = path; *loc && expansion == path; loc++) + { + if (IS_ENV_SEP (loc[0]) && IS_ENV_SEP (loc[1])) + { /* We have a doubled colon. */ + expansion = xmalloc (path_length + strlen (fallback) + 1); + + /* Copy stuff up to and including the first colon. */ + strncpy (expansion, path, loc - path + 1); + expansion[loc - path + 1] = 0; + + /* Copy in FALLBACK, and then the rest of PATH. */ + strcat (expansion, fallback); + strcat (expansion, loc + 1); + } + } + } + + return expansion; +} + +#ifdef TEST + +void +test_expand_default (const_string path, const_string def) +{ + string answer; + + printf ("Expansion of `%s':\t", path ? path : "(null)"); + answer = kpse_expand_default (path, def); + puts (answer); +} + +int +main () +{ + string default_path = "default"; + + test_expand_default (NULL, default_path); + test_expand_default ("", default_path); + test_expand_default ("none", default_path); + test_expand_default (":", default_path); + test_expand_default (":first", default_path); + test_expand_default ("last:", default_path); + test_expand_default ("middle::elddim", default_path); + + return 0; +} + +#endif /* TEST */ + + +/* +Local variables: +standalone-compile-command: "gcc -g -I. -I.. -DTEST default.c kpathsea.a" +End: +*/
new file mode 100644 --- /dev/null +++ b/kpathsea/kpsewhich.c @@ -0,0 +1,258 @@ +/* kpsewhich -- standalone path lookup and variable expansion for Kpathsea. + Ideas from Tom Esser and Pierre MacKay. + +Copyright (C) 1995 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> +#include <kpathsea/getopt.h> +#include <kpathsea/line.h> +#include <kpathsea/proginit.h> +#include <kpathsea/progname.h> +#include <kpathsea/tex-file.h> +#include <kpathsea/tex-glyph.h> +#include <kpathsea/variable.h> + + +/* Base resolution. (-D, -dpi) */ +unsigned dpi = 300; + +/* A construct to do variable expansion on. (-expand) */ +string expand = NULL; + +/* The file type for lookups. (-format) */ +kpse_file_format_type format = kpse_last_format; + +/* Ask interactively? (-interactive) */ +boolean interactive = false; + +/* The device name, for $MAKETEX_MODE. (-mode) */ +string mode = NULL; + +/* The program name, for `.program' in texmf.cnf. (-program) */ +string progname = NULL; + +/* Return the <number> substring in `<name>.<number><stuff>', if S has + that form. If it doesn't, return 0. */ + +static unsigned +find_dpi P1C(string, s) +{ + unsigned dpi_number = 0; + string extension = find_suffix (s); + + if (extension != NULL) + sscanf (extension, "%u", &dpi_number); + + return dpi_number; +} + +/* Use the -format if specified, else guess dynamically from NAME. */ + +/* We could partially initialize this array from `kpse_format_info', + but it doesn't seem worth the trouble. */ +static const_string suffix[] + = { "gf", "pk", "", ".base", ".bib", ".bst", ".cnf", ".fmt", ".mf", + ".pool", ".eps", ".tex", ".pool", ".tfm", ".vf", ".ps", ".pfa" }; + +static kpse_file_format_type +find_format P1C(string, name) +{ + kpse_file_format_type ret; + + if (format != kpse_last_format) + ret = format; + else + { + unsigned name_len = strlen (name); + for (ret = 0; ret < kpse_last_format; ret++) + { + const_string suffix_try = suffix[ret]; + unsigned try_len = strlen (suffix_try); + if (try_len && try_len < name_len + && STREQ (suffix_try, name + name_len - try_len)) + break; + } + if (ret == kpse_last_format) + { + fprintf (stderr, + "kpsewhich: Can't guess format for %s, using tex.\n", name); + ret = kpse_tex_format; + } + } + + return ret; +} + +/* Look up a single filename NAME. Return 0 if success, 1 if failure. */ + +static unsigned +lookup P1C(string, name) +{ + string ret; + unsigned local_dpi; + kpse_glyph_file_type glyph_ret; + kpse_file_format_type fmt = find_format (name); + + switch (fmt) + { + case kpse_pk_format: + case kpse_gf_format: + case kpse_any_glyph_format: + /* Try to extract the resolution from the name. */ + local_dpi = find_dpi (name); + if (!local_dpi) + local_dpi = dpi; + ret = kpse_find_glyph (remove_suffix (name), local_dpi, fmt, &glyph_ret); + break; + default: + ret = kpse_find_file (name, fmt, false); + } + + if (ret) + puts (ret); + + return ret == NULL; +} + +/* Reading the options. */ + +#define USAGE "Options:\n\ + You can use `--' or `-' to start an option.\n\ + You can use any unambiguous abbreviation for an option name.\n\ + You can separate option names and values with `=' or ` '.\n\ +debug <integer>: set debugging flags.\n\ +D, dpi <unsigned>: use a base resolution of <unsigned>; default 300.\n\ +expand <string>: do variable expansion on <string>.\n\ +help: print this message and exit.\n\ +interactive: ask for additional filenames to look up.\n\ +mode <string>: set device name for $MAKETEX_MODE to <string>; no default.\n\ +version: print version number and exit.\n\ +" + +/* Test whether getopt found an option ``A''. + Assumes the option index is in the variable `option_index', and the + option table in a variable `long_options'. */ +#define ARGUMENT_IS(a) STREQ (long_options[option_index].name, a) + +/* SunOS cc can't initialize automatic structs. */ +static struct option long_options[] + = { { "debug", 1, 0, 0 }, + { "dpi", 1, 0, 0 }, + { "D", 1, 0, 0 }, + { "expand", 1, 0, 0 }, + { "format", 1, 0, 0 }, + { "help", 0, 0, 0 }, + { "interactive", 0, (int *) &interactive, 1 }, + { "mode", 1, 0, 0 }, + { "program", 1, 0, 0 }, + { "version", 0, 0, 0 }, + { 0, 0, 0, 0 } }; + + +/* We return the name of the font to process. */ + +static void +read_command_line P2C(int, argc, string *, argv) +{ + int g; /* `getopt' return code. */ + int option_index; + + while (true) + { + g = getopt_long_only (argc, argv, "", long_options, &option_index); + + if (g == EOF) + break; + + if (g == '?') + exit (1); /* Unknown option. */ + + assert (g == 0); /* We have no short option names. */ + + if (ARGUMENT_IS ("debug")) + kpathsea_debug |= atoi (optarg); + + else if (ARGUMENT_IS ("dpi") || ARGUMENT_IS ("D")) + dpi = atoi (optarg); + + else if (ARGUMENT_IS ("expand")) + expand = optarg; + + else if (ARGUMENT_IS ("format")) + format = atoi (optarg); + + else if (ARGUMENT_IS ("help")) + { + printf ("Usage: %s [options] [filenames].\n", argv[0]); + fputs (USAGE, stdout); + exit (0); + } + + else if (ARGUMENT_IS ("mode")) + mode = optarg; + + else if (ARGUMENT_IS ("program")) + progname = optarg; + + else if (ARGUMENT_IS ("version")) + { + extern char *kpathsea_version_string; /* from version.c */ + printf ("%s.\n", kpathsea_version_string); + exit (0); + } + + /* Else it was just a flag; getopt has already done the assignment. */ + } +} + +int +main P2C(int, argc, string *, argv) +{ + unsigned unfound = 0; + + read_command_line (argc, argv); + + if (!progname) + progname = (string) ""; + + kpse_set_progname (progname); + + /* false for no MakeTeXPK, NULL for no fallback font. */ + kpse_init_prog (uppercasify (progname), dpi, mode, false, NULL); + + /* No extra frills on expansion output, to make it easy to use. */ + if (expand) + puts (kpse_var_expand (expand)); + + for (; optind < argc; optind++) + { + unfound += lookup (argv[optind]); + } + + if (interactive) + { + for (;;) + { + string name = read_line (stdin); + if (!name || STREQ (name, "q") || STREQ (name, "quit")) break; + unfound += lookup (name); + free (name); + } + } + + exit (unfound); +}
new file mode 100644 --- /dev/null +++ b/kpathsea/kpsexpand.c @@ -0,0 +1,44 @@ +/* kpsexpand -- do Kpathsea variable expansion, pretending to be a given + program. For example, + prompt$ kpsexpand latex2e '$TEXINPUTS' + expands $TEXINPUTS for latex2e. + + Based on code from te@informatik.uni-hannover.de. */ + +/* Not worth chance of collisions to #include anything but this. */ +#include <stdio.h> /* for stderr */ + +#include "tex-file.h" +extern char *kpse_var_expand (); +extern void kpse_set_progname (); +extern char *kpathsea_version_string; + +int +main (argc, argv) + int argc; + char *argv[]; +{ + if (argc < 3) + { + fprintf (stderr, "Usage: %s progname string [filename]\n", argv[0]); + fprintf (stderr, "%s\n", kpathsea_version_string); + fputs ("Sets the program name to `progname',\n", stderr); + fputs ("then prints the variable expansion of `string'.\n", stderr); + fputs ("If `filename' is present, does lookups of several types.\n", + stderr); + fputs ("Example: kpsexpand latex2e '$TEXFINPUTS'.\n", stderr); + exit (1); + } + + kpse_set_progname (argv[1]); + printf ("%s\n", kpse_var_expand (argv[2])); + + if (argc == 4) + { + printf ("PICT: %s\n", kpse_find_pict (argv[3])); + printf ("TEX: %s\n", kpse_find_tex (argv[3])); + printf ("TFM: %s\n", kpse_find_tfm (argv[3])); + printf ("VF: %s\n", kpse_find_vf (argv[3])); + } + return 0; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/line.c @@ -0,0 +1,71 @@ +/* line.c: return the next line from a file, or NULL. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Don't include config.h or all our other usual includes, since + it's useful to just throw this file into other programs. */ + +#include <stdio.h> +extern void free (); + +/* From xmalloc.c and xrealloc.c. This saves having to include config.h. */ +extern void *xmalloc (), *xrealloc (); + + +/* Allocate in increments of this size. */ +#define BLOCK_SIZE 40 + +char * +read_line (f) + FILE *f; +{ + int c; + unsigned limit = BLOCK_SIZE; + unsigned loc = 0; + char *line = xmalloc (limit); + + while ((c = getc (f)) != EOF && c != '\n') + { + line[loc] = c; + loc++; + + /* By testing after the assignment, we guarantee that we'll always + have space for the null we append below. We know we always + have room for the first char, since we start with BLOCK_SIZE. */ + if (loc == limit) + { + limit += BLOCK_SIZE; + line = xrealloc (line, limit); + } + } + + /* If we read anything, return it. This can't represent a last + ``line'' which doesn't end in a newline, but so what. */ + if (c != EOF) + { + /* Terminate the string. We can't represent nulls in the file, + either. Again, it doesn't matter. */ + line[loc] = 0; + } + else /* At end of file. */ + { + free (line); + line = NULL; + } + + return line; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/magstep.c @@ -0,0 +1,100 @@ +/* magstep.c: fix up fixed-point vs. floating-point. + +Copyright (C) 1994 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/magstep.h> + + +/* Return true magstep N, where the lsb of N means ``half'' (see + magstep.h) for resolution BDPI. From Tom Rokicki's dvips. */ + +static int +magstep P2C(int, n, int, bdpi) +{ + double t; + int step; + int neg = 0; + + if (n < 0) + { + neg = 1; + n = -n; + } + + if (n & 1) + { + n &= ~1; + t = 1.095445115; + } + else + t = 1.0; + + while (n > 8) + { + n -= 8; + t = t * 2.0736; + } + + while (n > 0) + { + n -= 2; + t = t * 1.2; + } + + step = 0.5 + (neg ? bdpi / t : bdpi * t); + return step; +} + +/* This is adapted from code written by Tom Rokicki for dvips. It's + part of Kpathsea now so all the drivers can use it. The idea is to + return the true dpi corresponding to DPI with a base resolution of + BDPI. If M_RET is non-null, we also set that to the mag value. */ + +/* Don't bother trying to use fabs or some other ``standard'' routine + which can only cause trouble; just roll our own simple-minded + absolute-value function that is all we need. */ +#undef ABS /* be safe */ +#define ABS(expr) ((expr) < 0 ? -(expr) : (expr)) + +#define MAGSTEP_MAX 40 + +unsigned +kpse_magstep_fix P3C(unsigned, dpi, unsigned, bdpi, int *, m_ret) +{ + int m; + int mdpi = -1; + unsigned real_dpi = 0; + int sign = dpi < bdpi ? -1 : 1; /* negative or positive magsteps? */ + + for (m = 0; !real_dpi && m < MAGSTEP_MAX; m++) /* don't go forever */ + { + mdpi = magstep (m * sign, bdpi); + if (ABS (mdpi - (int) dpi) <= 1) /* if this magstep matches, quit */ + real_dpi = mdpi; + else if ((mdpi - (int) dpi) * sign > 0) /* if gone too far, quit */ + real_dpi = dpi; + } + + /* If requested, return the encoded magstep (the loop went one too far). */ + if (m_ret) + *m_ret = real_dpi == mdpi ? (m - 1) * sign : 0; + + /* Always return the true dpi found. */ + return real_dpi ? real_dpi : dpi; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/make-suffix.c @@ -0,0 +1,44 @@ +/* make_suffix.c: unconditionally add a filename suffix. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + + +/* Return a new string: S suffixed with SUFFIX, regardless of what it + was before. This returns a newly allocated string. */ + +string +make_suffix P2C(const_string, s, const_string, suffix) +{ + string new_s; + const_string dot_pos = strrchr (s, '.'); + const_string slash_pos = strrchr (s, '/'); + + if (dot_pos == NULL || dot_pos < slash_pos) + new_s = concat3 (s, ".", suffix); + else + { + unsigned past_dot_index = dot_pos + 1 - s; + + new_s = (string) xmalloc (past_dot_index + strlen (suffix) + 1); + strncpy (new_s, s, dot_pos + 1 - s); + strcpy (new_s + past_dot_index, suffix); + } + + return new_s; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/path-elt.c @@ -0,0 +1,133 @@ +/* path-elt.c: Return the stuff between colons. + +Copyright (C) 1993 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-pathch.h> +#include <kpathsea/pathsearch.h> + + +/* The static (but dynamically allocated) area we return the answer in, + and how much we've currently allocated for it. */ +static string elt = NULL; +static unsigned elt_alloc = 0; + +/* The path we're currently working on. */ +static const_string path = NULL; + + +/* Upon entry, the static `path' is at the first (and perhaps last) + character of the return value, or else NULL if we're at the end (or + haven't been called). I make no provision for caching the results; + thus, we parse the same path over and over, on every lookup. If that + turns out to be a significant lose, it can be fixed, but I'm guessing + disk accesses overwhelm everything else. */ + +string +kpse_path_element P1C(const_string, passed_path) +{ + const_string p; + string ret; + + if (passed_path) + path = passed_path; + + /* Check if called with NULL, and no previous path (perhaps we reached + the end). */ + else if (!path) + return NULL; + + /* OK, we have a non-null `path' if we get here. */ + assert (path); + p = path; + + /* Find the next colon (or the end of the path). */ + while (*p != 0 && !IS_ENV_SEP (*p)) + p++; + + /* If there were no separators, return the whole thing this time, and + return NULL next time. */ + if (*p == 0) + { + ret = (string) path; + path = NULL; + } + + /* Otherwise, return the substring starting at `path'. */ + else + { + unsigned len = p - path; + + /* Make sure we have enough space (including the null byte). */ + if (len + 1 > elt_alloc) + { + elt_alloc = len + 1; + elt = xrealloc (elt, elt_alloc); + } + + strncpy (elt, path, len); + elt[len] = 0; + ret = elt; + + path += len + 1; + } + + return ret; +} + +#ifdef TEST + +void +print_path_elements (const_string path) +{ + string elt; + printf ("Elements of `%s':", path ? path : "(null)"); + + for (elt = kpse_path_element (path); elt != NULL; + elt = kpse_path_element (NULL)) + { + printf (" %s", *elt ? elt : "`'"); + } + + puts ("."); +} + +int +main () +{ + /* All lists end with NULL. */ + print_path_elements (NULL); /* */ + print_path_elements (""); /* "" */ + print_path_elements ("a"); /* a */ + print_path_elements (":"); /* "", "" */ + print_path_elements ("::"); /* "", "", "" */ + print_path_elements ("a:"); /* a, "" */ + print_path_elements (":b"); /* "", b */ + print_path_elements ("a:b"); /* a, b */ + + return 0; +} + +#endif /* TEST */ + + +/* +Local variables: +standalone-compile-command: "gcc -g -I. -I.. -DTEST path-elt.c kpathsea.a" +End: +*/
new file mode 100644 --- /dev/null +++ b/kpathsea/pathsearch.c @@ -0,0 +1,405 @@ +/* pathsearch.c: look up a filename in a path. + +Copyright (C) 1993, 94 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.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' */ + +/* This function is called after every search to record the filename(s) + found in $TEXMFLOG or if debugging. */ + +static void +log_search P1C(str_list_type, filenames) +{ + static FILE *log_file = NULL; + static boolean first_time = true; + + if (first_time) + { + string log_name = kpse_var_expand ("$TEXMFLOG"); + first_time = false; + /* Get name from either envvar or config file. */ + if (log_name && *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, "%u %s\n", 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)) + 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 = 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 = 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; +} + +/* 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; +} + +/* If DB_DIR is a prefix of PATH_ELT, return true; otherwise false. + That is, the question is whether to try the db for a file looked up + in PATH_ELT. If PATH_ELT == ".", for example, the answer is no. If + PATH_ELT == "/usr/local/lib/texmf/fonts//tfm", the answer is yes. + + In practice, ls-R is only needed for lengthy subdirectory + comparisons, but there's no gain to checking PATH_ELT to see if it is + a subdir match, since the only way to do that is to do a string + search in it, which is all we do anyway. + + In fact, we do a simple string compare, ignoring // complications, + since in practice I believe //'s will always be after `kpse_db_dir', + i.e., we would never want to find ls-R in /usr//texmf. */ + +static boolean +elt_in_db P1C(const_string, path_elt) +{ + boolean found = false; + + /* If `kpse_db_dir' is not set, we're being called from `read_files' + for the very first time -- for cnf file initialization. We can't + use ls-R for that. */ + if (kpse_db_dir) + { + string db_temp = kpse_db_dir; + + while (!found && *db_temp++ == *path_elt++) + { /* If we've matched the entire db directory, it's good. */ + if (*db_temp == 0) + found = true; + /* If we've reached the end of PATH_ELT, but not the end of the db + directory, it's no good. */ + else if (*path_elt == 0) + break; + } + } + + return found; +} + + +/* This is the hard case -- look for NAME in PATH. If + ALL is false, just 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)) + { + boolean try_db; + boolean allow_disk_search = true; + str_list_type *found = NULL; + + if (*elt == '!' && *(elt + 1) == '!') + { /* Magic leading chars in a path element means don't search the + disk regardless. And move past the magic to get to the name. */ + allow_disk_search = false; + elt += 2; + } + + /* Try the prebuilt db only if it's relevant to this path element. */ + try_db = elt_in_db (elt); + found = try_db ? kpse_db_search (name, elt, all) : NULL; + + /* Search the filesystem if (1) the path spec allows it, and either + (2a) the db was irrelevant to ELT (try_db == false); or + (2b) no db exists (kpse_db_search returns NULL); or + (3) NAME was not in the db (kpse_db_search returns an empty list) + and MUST_EXIST. + In (2a) and (2b), `found' will be NULL. */ + if (allow_disk_search && (!found || (!STR_LIST (*found) && must_exist))) + { + 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) + 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; + + /* Make a leading ~ count as an absolute filename, and expand $FOO's. */ + string name = kpse_expand (original_name); + + /* If the first name is absolute or explicitly relative, no need to + consider PATH at all. */ + boolean absolute_p = kpse_absolute_p (name, true); + + if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) + DEBUGF4 ("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); + + /* 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); + + 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); + return *ret_list; +} + + +/* 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; +} + +#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 : "(null)"); + + 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:.", "kpathsea.texi"); + test_path_search ("/k:.", "/etc/fstab"); + test_path_search (".:" TEXFONTS "//", "cmr10.tfm"); + test_path_search (".:" TEXFONTS "//", "logo10.tfm"); + test_path_search (TEXFONTS "//times:.::", "ptmr.vf"); + test_path_search (TEXFONTS "/adobe//:" + "/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:.", "$KPATHSEA.texi"); + test_path_search ("/k:.", "${KPATHSEA}.texi"); + test_path_search ("$KPATHSEA:.", "README"); + test_path_search (".:$KPATHSEA", "README"); + + return 0; +} + +#endif /* TEST */ + + +/* +Local variables: +test-compile-command: "gcc -posix -g -I. -I.. -DTEST pathsearch.c kpathsea.a" +End: +*/
new file mode 100644 --- /dev/null +++ b/kpathsea/proginit.c @@ -0,0 +1,63 @@ +/* proginit.c: useful initializations for DVI drivers. + +Copyright (C) 1994 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> +#include <kpathsea/c-pathch.h> +#include <kpathsea/proginit.h> +#include <kpathsea/progname.h> +#include <kpathsea/tex-file.h> + + +/* These initializations were common to all the drivers modified for + kpathsea, so a single routine seemed in order. Kind of a bollixed-up + mess, but still better than repeating the code. */ + +void +kpse_init_prog P5C(const_string, prefix, unsigned, dpi, const_string, mode, + boolean, make_tex_pk, const_string, fallback) +{ + string makepk_var = concat (prefix, "MAKEPK"); + string font_var = concat (prefix, "FONTS"); + string size_var = concat (prefix, "SIZES"); + + /* Do both `pk_format' and `any_glyph_format' for the sake of xdvi; in + general, MakeTeXPK might apply to either, and the program will ask + for the one it wants. */ + + /* Might have a program-specific name for MakeTeXPK itself. */ + kpse_format_info[kpse_pk_format].program + = kpse_format_info[kpse_any_glyph_format].program + = getenv (makepk_var); + + /* If we did, we want to enable the program, I think. */ + kpse_format_info[kpse_pk_format].program_enabled_p + = kpse_format_info[kpse_any_glyph_format].program_enabled_p + = getenv (makepk_var) || make_tex_pk; + + kpse_font_override_path = getenv (font_var); + kpse_init_fallback_resolutions (size_var); + xputenv_int ("MAKETEX_BASE_DPI", dpi); + kpse_fallback_font = fallback; + + /* See comments in kpse_make_tex in kpathsea/tex-make.c. */ + xputenv ("MAKETEX_MODE", mode ? mode : DIR_SEP_STRING); + + free (makepk_var); + free (font_var); + free (size_var); +}
new file mode 100644 --- /dev/null +++ b/kpathsea/progname.c @@ -0,0 +1,43 @@ +/* progname.c: the executable name we were invoked as; general initialization. + +Copyright (C) 1994 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/progname.h> + + +#ifndef HAVE_PROGRAM_INVOCATION_NAME +/* Don't redefine the variables if glibc already has. */ +string program_invocation_name; +string program_invocation_short_name; +#endif + + +void +kpse_set_progname P1C(const_string, progname) +{ +#ifndef HAVE_PROGRAM_INVOCATION_NAME + program_invocation_name = xstrdup (progname); + program_invocation_short_name = (string) basename (program_invocation_name); +#endif + { + string s = getenv ("KPATHSEA_DEBUG"); + if (s) + kpathsea_debug |= atoi (s); + } +}
new file mode 100644 --- /dev/null +++ b/kpathsea/putenv.c @@ -0,0 +1,118 @@ +/****************************************************************/ +/* */ +/* putenv(3) */ +/* */ +/* Change or add an environment entry */ +/* */ +/****************************************************************/ +/* origination 1987-Oct-7 T. Holm */ +/* (slightly modified by karl@cs.umb.edu for kpathsea.) */ +/****************************************************************/ + +/* for HAVE_PUTENV and const -- need nothing else. */ +#include <kpathsea/c-auto.h> + +#ifndef HAVE_PUTENV /* whole file */ + +/* +Path: hoptoad!pacbell!ames!ll-xn!mit-eddie!uw-beaver!ssc-vax!uvicctr!tholm +From: tholm@uvicctr.UUCP (Terrence W. Holm) +Newsgroups: comp.os.minix +Subject: putenv(3) +Message-ID: <395@uvicctr.UUCP> +Date: 5 May 88 06:40:52 GMT +Organization: University of Victoria, Victoria B.C. Canada + +EFTH Minix report #2 - May 1988 - putenv(3) + +This is an implementation of putenv(3) that we +wrote for Minix. Please consider this a public +domain program. +*/ + +#define NULL 0 +#define PSIZE sizeof(char *) + +extern char **environ; + +char *strchr(); +char *malloc(); + +/****************************************************************/ +/* */ +/* int */ +/* putenv( entry ) */ +/* */ +/* The "entry" should follow the form */ +/* "NAME=VALUE". This routine will search the */ +/* user environment for "NAME" and replace its */ +/* value with "VALUE". */ +/* */ +/* Note that "entry" is not copied, it is used */ +/* as the environment entry. This means that it */ +/* must not be unallocated or otherwise modifed */ +/* by the caller, unless it is replaced by a */ +/* subsequent putenv(). */ +/* */ +/* If the name is not found in the environment, */ +/* then a new vector of pointers is allocated, */ +/* "entry" is put at the end and the global */ +/* variable "environ" is updated. */ +/* */ +/* This function normally returns 0, but -1 */ +/* is returned if it can not allocate enough */ +/* space using malloc(3), or "entry" does not */ +/* contain a '='. */ +/* */ +/****************************************************************/ + + +int +putenv( entry ) + char *entry; +{ + unsigned length; + unsigned size; + char *temp; + char **p; + char **new_environ; + + /* Find the length of the "NAME=" */ + + temp = strchr(entry,'='); + if ( temp == 0 ) + return( -1 ); + + length = (unsigned) (temp - entry + 1); + + + /* Scan through the environment looking for "NAME=" */ + + for ( p=environ; *p != 0 ; p++ ) + if ( strncmp( entry, *p, length ) == 0 ) + { + *p = entry; + return( 0 ); + } + + + /* The name was not found, build a bigger environment */ + + size = p - environ; + + new_environ = (char **) malloc( (size+2)*PSIZE ); + + if ( new_environ == (char **) NULL ) + return( -1 ); + + memcpy ((char *) new_environ, (const char *) environ, size*PSIZE ); + + new_environ[size] = entry; + new_environ[size+1] = NULL; + + environ = new_environ; + + return(0); +} + +#endif /* not HAVE_PUTENV */
new file mode 100644 --- /dev/null +++ b/kpathsea/readable.c @@ -0,0 +1,71 @@ +/* readable.c: check if a filename is a readable regular file. + +Copyright (C) 1993 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-stat.h> +#include <kpathsea/readable.h> +#include <kpathsea/truncate.h> + + +/* If access can read FN, run stat (assigning to stat buffer ST) and + check that fn is a regular file. */ + +#define READABLE(fn, st) \ + (access (fn, R_OK) == 0 && stat (fn, &(st)) == 0 && S_ISREG (st.st_mode)) + + +/* 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. */ + +string +kpse_readable_file P1C(const_string, name) +{ + struct stat st; + string ret; + + if (READABLE (name, st)) + ret = (string) 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 + + else /* Some other error. */ + { + if (errno == EACCES) /* Warn them if permissions are bad. */ + perror (name); + ret = NULL; + } + + return ret; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/rm-suffix.c @@ -0,0 +1,42 @@ +/* rm-suffix.c: remove any suffix. + +Copyright (C) 1992, 93, 95 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + + +/* Generic const warning -- see extend-fname.c. */ + +string +remove_suffix P1C(const_string, s) +{ + string ret; + const_string suffix = find_suffix (s); + + if (suffix) + { + /* Back up to before the dot. */ + suffix--; + ret = (string) xmalloc (suffix - s + 1); + strncpy (ret, s, suffix - s); + ret[suffix - s] = 0; + } + else + ret = (string) s; + + return ret; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/str-list.c @@ -0,0 +1,75 @@ +/* str-list.c: define routines for string lists. + +Copyright (C) 1993 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/str-list.h> + + +/* See the .h file for comments. */ + + +str_list_type +str_list_init P1H(void) +{ + str_list_type ret; + + STR_LIST_LENGTH (ret) = 0; + STR_LIST (ret) = NULL; + + return ret; +} + + +void +str_list_add P2C(str_list_type *, l, string, s) +{ + STR_LIST_LENGTH (*l)++; + XRETALLOC (STR_LIST (*l), STR_LIST_LENGTH (*l), string); + 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 P2C(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), string); + + 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 P1C(str_list_type *, l) +{ + if (STR_LIST (*l)) + { + free (STR_LIST (*l)); + STR_LIST (*l) = NULL; + } +}
new file mode 100644 --- /dev/null +++ b/kpathsea/str-llist.c @@ -0,0 +1,94 @@ +/* str-llist.c: Implementation of a linked list of strings. + +Copyright (C) 1993 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/str-llist.h> + + +/* Add the new string STR to the end of the list L. */ + +void +str_llist_add P2C(str_llist_type *, l, string, 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) = false; + 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 P2C(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) = true; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/tex-file.c @@ -0,0 +1,344 @@ +/* tex-file.c: stuff for all TeX formats. + +Copyright (C) 1993, 94, 95 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-vararg.h> +#include <kpathsea/cnf.h> +#include <kpathsea/default.h> +#include <kpathsea/expand.h> +#include <kpathsea/paths.h> +#include <kpathsea/pathsearch.h> +#include <kpathsea/tex-file.h> + + +/* See tex-file.h. */ +const_string kpse_fallback_font = NULL; +unsigned *kpse_fallback_resolutions = NULL; +string kpse_font_override_path = NULL; +kpse_format_info_type kpse_format_info[kpse_last_format]; + +/* These are not in the structure + because it's annoying to initialize lists in C. */ +#define KPSE_GF_ENVS "GFFONTS", KPSE_GLYPH_ENVS +#define KPSE_PK_ENVS "PKFONTS", "TEXPKS", KPSE_GLYPH_ENVS +#define KPSE_GLYPH_ENVS "GLYPHFONTS", "TEXFONTS" +#define KPSE_BASE_ENVS "MFBASES" +#define KPSE_BIB_ENVS "BIBINPUTS" +#define KPSE_BST_ENVS "BSTINPUTS" +#define KPSE_CNF_ENVS "TEXMFCNF" +#define KPSE_FMT_ENVS "TEXFORMATS" +#define KPSE_MF_ENVS "MFINPUTS" +#define KPSE_MFPOOL_ENVS "MFPOOL" +#define KPSE_PICT_ENVS "TEXPICTS", KPSE_TEX_ENVS +#define KPSE_TEX_ENVS "TEXINPUTS" +#define KPSE_TEXPOOL_ENVS "TEXPOOL" +#define KPSE_TFM_ENVS "TFMFONTS", "TEXFONTS" +#define KPSE_VF_ENVS "VFFONTS", "TEXFONTS" +#define KPSE_DVIPS_CONFIG_ENVS "TEXCONFIG" +#define KPSE_DVIPS_HEADER_ENVS "DVIPSHEADERS" + +/* The compiled-in default list, DEFAULT_FONT_SIZES, is intended to be + set from the command line (presumably via the Makefile). */ + +#ifndef DEFAULT_FONT_SIZES +#define DEFAULT_FONT_SIZES "" +#endif + +void +kpse_init_fallback_resolutions P1C(string, envvar) +{ + const_string size_var = ENVVAR (envvar, "TEXSIZES"); + string size_str = getenv (size_var); + unsigned *last_resort_sizes = NULL; + unsigned size_count = 0; + string size_list = kpse_expand_default (size_str, DEFAULT_FONT_SIZES); + + /* Initialize the list of last-resort sizes. */ + for (size_str = kpse_path_element (size_list); size_str != NULL; + size_str = kpse_path_element (NULL)) + { + if (! *size_str) + continue; + + size_count++; + XRETALLOC (last_resort_sizes, size_count, unsigned); + last_resort_sizes[size_count - 1] = atoi (size_str); + } + + /* Add a zero to mark the end of the list. */ + size_count++; + XRETALLOC (last_resort_sizes, size_count, unsigned); + last_resort_sizes[size_count - 1] = 0; + + kpse_fallback_resolutions = last_resort_sizes; +} + +/* Find the final search path to use for the format entry INFO, given + FONT_P (whether the font override path applies), the compile-time + default (DEFAULT_PATH), and the environment variables to check (the + remaining arguments, terminated with NULL). We set the `path' and + `path_source' members of INFO. The `client_path' member must already + be set upon entry. */ + +#define EXPAND_DEFAULT(try_path, source_string) \ +if (try_path) \ + { \ + info->raw_path = try_path; \ + info->path = kpse_expand_default (try_path, info->path); \ + info->path_source = source_string; \ + } + +static void +init_path PVAR3C(kpse_format_info_type *, info, boolean, font_p, + const_string, default_path, ap) +{ + string env_name; + string var = NULL; + + info->font_override_p = font_p; + info->default_path = default_path; + + /* First envvar that's set to a nonempty value will exit the loop. If + none are set, we want the first cnf entry that matches. Find the + cnf entries simultaneously, to avoid having to go through envvar + list twice -- because of the PVAR?C macro, that would mean having + to create a str_list and then use it twice. Yuck. */ + while ((env_name = va_arg (ap, string)) != NULL) + { + if (!var) + { + string env_value = getenv (env_name); + if (env_value && *env_value) + var = env_name; + } + + /* If we are initializing the cnf path, don't try to get any + values from the cnf files; that's infinite loop time. */ + if (!info->cnf_path && (!info->suffix || !STREQ (info->suffix, ".cnf"))) + info->cnf_path = kpse_cnf_get (env_name); + + if (var && info->cnf_path) + break; + } + va_end (ap); + + /* Expand any extra :'s. For each level (the font override path down + through the compile-time default), an extra : should be replaced + with the path at the next lower level. For example, an extra : in + a user-set envvar should be replaced with the path from the cnf + file. Things are complicated because none of the levels above the + very bottom are guaranteed to exist. */ + + /* Assume we can reliably start with the compile-time default. */ + info->path = info->raw_path = info->default_path; + info->path_source = "compile-time paths.h"; + + EXPAND_DEFAULT (info->cnf_path, "texmf.cnf"); + EXPAND_DEFAULT (info->client_path, "program config file"); + if (var) + EXPAND_DEFAULT (getenv (var), concat (var, " environment variable")); + if (info->font_override_p) + EXPAND_DEFAULT (kpse_font_override_path, "font override variable"); + info->path = kpse_path_expand (info->path); +}} + + +/* The path spec we are defining, one element of the global array. */ +#define FMT_INFO kpse_format_info[format] + +/* Call `init_path', including appending the trailing NULL to the envvar + list. Also initialize the fields not needed in setting the path. + Don't set `program_enabled' -- it'll be false via the compiler + initialization, and programs may want to enable it beforehand. */ +#define INIT_FORMAT(ext, font_p, default_path, envs) \ + FMT_INFO.suffix = FMT_INFO.type = ext; \ + init_path (&FMT_INFO, font_p, default_path, envs, NULL) + +/* A few file types allow for runtime generation by an external program. */ +#define INIT_MT(prog, args) \ + FMT_INFO.program = prog; FMT_INFO.program_args = args +#define MAKETEXPK_SPEC \ + "$KPATHSEA_DPI $MAKETEX_BASE_DPI $MAKETEX_MAG $MAKETEX_MODE" + +/* Initialize everything for FORMAT. */ + +const_string +kpse_init_format P1C(kpse_file_format_type, format) +{ + /* If we get called twice, don't redo all the work. */ + if (FMT_INFO.path) + return FMT_INFO.path; + + switch (format) + { /* We could avoid this repetition by token pasting, but it doesn't + seem worth it. */ + case kpse_gf_format: + INIT_FORMAT ("gf", true, DEFAULT_GFFONTS, KPSE_GF_ENVS); + FMT_INFO.suffix_search_only = false; + break; + case kpse_pk_format: + INIT_MT ("MakeTeXPK", MAKETEXPK_SPEC); + INIT_FORMAT ("pk", true, DEFAULT_PKFONTS, KPSE_PK_ENVS); + FMT_INFO.suffix_search_only = true; + break; + case kpse_any_glyph_format: + INIT_MT ("MakeTeXPK", MAKETEXPK_SPEC); + INIT_FORMAT ("bitmap", true, DEFAULT_GLYPHFONTS, KPSE_GLYPH_ENVS); + FMT_INFO.suffix_search_only = true; + break; + case kpse_base_format: + INIT_FORMAT (".base", false, DEFAULT_MFBASES, KPSE_BASE_ENVS); + break; + case kpse_bib_format: + INIT_FORMAT (".bib", false, DEFAULT_BIBINPUTS, KPSE_BIB_ENVS); + break; + case kpse_bst_format: + INIT_FORMAT (".bst", false, DEFAULT_BSTINPUTS, KPSE_BST_ENVS); + break; + case kpse_cnf_format: + /* I admit this is ugly, but making another field just for + this one file type seemed a waste. We use this value in cnf.c + as the filename to search for in the path. */ + if (!FMT_INFO.program) + FMT_INFO.program = "texmf.cnf"; + INIT_FORMAT (".cnf", false, DEFAULT_TEXMFCNF, KPSE_CNF_ENVS); + break; + case kpse_fmt_format: + INIT_FORMAT (".fmt", false, DEFAULT_TEXFORMATS, KPSE_FMT_ENVS); + break; + case kpse_mf_format: + INIT_MT ("MakeTeXMF", NULL); + INIT_FORMAT (".mf", false, DEFAULT_MFINPUTS, KPSE_MF_ENVS); + break; + case kpse_mfpool_format: + INIT_FORMAT (".pool", false, DEFAULT_MFPOOL, KPSE_MFPOOL_ENVS); + break; + case kpse_tex_format: + INIT_MT ("MakeTeXTeX", NULL); + INIT_FORMAT (".tex", false, DEFAULT_TEXINPUTS, KPSE_TEX_ENVS); + break; + case kpse_texpool_format: + INIT_FORMAT (".pool", false, DEFAULT_TEXPOOL, KPSE_TEXPOOL_ENVS); + break; + case kpse_pict_format: + INIT_FORMAT (NULL, false, DEFAULT_TEXINPUTS, KPSE_PICT_ENVS); + FMT_INFO.type = "graphic/figure"; + break; + case kpse_tfm_format: + INIT_MT ("MakeTeXTFM", NULL); + INIT_FORMAT (".tfm", false, DEFAULT_TFMFONTS, KPSE_TFM_ENVS); + FMT_INFO.suffix_search_only = true; + break; + case kpse_vf_format: + INIT_FORMAT (".vf", false, DEFAULT_VFFONTS, KPSE_VF_ENVS); + FMT_INFO.suffix_search_only = true; + break; + case kpse_dvips_config_format: + INIT_FORMAT (NULL, false, DEFAULT_TEXCONFIG, KPSE_DVIPS_CONFIG_ENVS); + FMT_INFO.type = "dvips config"; + break; + case kpse_dvips_header_format: + INIT_FORMAT (NULL, false, DEFAULT_DVIPSHEADERS, KPSE_DVIPS_HEADER_ENVS); + FMT_INFO.type = "dvips header/type1 font"; + break; + default: + FATAL1 ("(kpathsea) init_path: Unknown format %d", format); + } + +#ifdef DEBUG +#define MAYBE_NULL(member) (FMT_INFO.member ? FMT_INFO.member : "(none)") + + /* Describe the monster we've created. */ + if (KPSE_DEBUG_P (KPSE_DEBUG_PATHS)) + { + DEBUGF2 ("Search path for %s files (from %s)\n", + FMT_INFO.type, FMT_INFO.path_source); + DEBUGF1 (" = %s\n", FMT_INFO.path); + DEBUGF1 (" before expansion = %s\n", FMT_INFO.raw_path); + DEBUGF1 (" font override var applies = %d\n", FMT_INFO.font_override_p); + DEBUGF1 (" application config file path = %s\n", + MAYBE_NULL (client_path)); + DEBUGF1 (" texmf.cnf path = %s\n", MAYBE_NULL (cnf_path)); + DEBUGF1 (" compile-time path = %s\n", MAYBE_NULL (default_path)); + DEBUGF1 (" suffix = %s\n", MAYBE_NULL (suffix)); + DEBUGF1 (" search only with suffix = %d\n", + FMT_INFO.suffix_search_only); + DEBUGF1 (" runtime generation program = %s\n", MAYBE_NULL (program)); + DEBUGF1 (" extra program args = %s\n", MAYBE_NULL (program_args)); + /* Don't print the `program_enabled_p' member, since it's likely + always false (made true on a per-font basis), and hence it + would just confuse matters. */ + } +#endif /* DEBUG */ + + return FMT_INFO.path; +} + +/* Look up a file NAME of type FORMAT, and the given MUST_EXIST. This + initializes the path spec for FORMAT if it's the first lookup of that + type. Return the filename found, or NULL. */ + +string +kpse_find_file P3C(const_string, name, kpse_file_format_type, format, + boolean, must_exist) +{ + boolean name_has_suffix; + const_string suffix; + string ret = NULL; + + if (FMT_INFO.path == NULL) + kpse_init_format (format); + + suffix = FMT_INFO.suffix; + + /* Does NAME already end in `.suffix'? */ + if (suffix) + { /* Don't do the strlen's at all if no SUFFIX. */ + unsigned suffix_len = strlen (suffix); + unsigned name_len = strlen (name); + name_has_suffix = (name_len > suffix_len + && STREQ (suffix, name + name_len - suffix_len)); + } + else + name_has_suffix = false; + + if (suffix && !name_has_suffix) + { + /* Append `.suffix' and search for it. If we're going to search + for the original name if this fails, then set must_exist=false; + otherwise, we'd be looking on the disk for foo.eps.tex. */ + string full_name = concat (name, suffix); + ret = kpse_path_search (FMT_INFO.path, full_name, + FMT_INFO.suffix_search_only ? must_exist : false); + free (full_name); + } + + /* For example, looking for foo.eps, first we look for foo.eps.tex, + which presumably fails, then we want to look for foo.eps. (But we + want to find foo.tex before foo, so have to try with `suffix' + first.) Another example: tftopl passes in cmr10.tfm, but the + drivers just pass in cmr10. To avoid pounding the disk for `cmr10', + `suffix_search_only' for PK format is true. But then + `name_has_suffix' must override, or tftopl would wind up doing + no searches at all. */ + if (!ret && (name_has_suffix || !FMT_INFO.suffix_search_only)) + ret = kpse_path_search (FMT_INFO.path, name, must_exist); + + return ret; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/tex-glyph.c @@ -0,0 +1,383 @@ +/* tex-glyph.c: Search for GF/PK files. + +Copyright (C) 1993, 94 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/absolute.h> +#include <kpathsea/expand.h> +#include <kpathsea/fontmap.h> +#include <kpathsea/pathsearch.h> +#include <kpathsea/tex-glyph.h> +#include <kpathsea/tex-make.h> +#include <kpathsea/variable.h> + +/* Routines are in bottom-up order. */ + +/* Support both cmr10.300pk and dpi300/cmr10.pk. (Use the latter + instead of dpi300\cmr10.pk since DOS supports /'s, but Unix doesn't + support \'s. */ +#define UNIX_BITMAP_SPEC "$KPATHSEA_NAME.$KPATHSEA_DPI$KPATHSEA_FORMAT" +#define DPI_BITMAP_SPEC "dpi$KPATHSEA_DPI/$KPATHSEA_NAME.$KPATHSEA_FORMAT" + +/* Look up FONT_NAME at resolution DPI in PATH, with filename suffix + EXTENSION. Return file found or NULL. */ + +static string +try_format P3C(const_string, font_name, unsigned, dpi, + kpse_file_format_type, format) +{ + static const_string bitmap_specs[] + = { UNIX_BITMAP_SPEC, DPI_BITMAP_SPEC, NULL }; + const_string *spec; + boolean must_exist; + string ret = NULL; + const_string path = kpse_format_info[format].path; + if (!path) + path = kpse_init_format (format); + + /* Set the suffix on the name we'll be searching for. */ + xputenv ("KPATHSEA_FORMAT", kpse_format_info[format].suffix); + + /* OK, the limits on this for loop are a little hokey, but it saves + having to repeat the body. We want to do it once with `must_exist' + false to avoid looking on the disk for cmr10.600pk if + dpi600/cmr10.pk is in ls-R. (The time spent in the extra variable + expansions and db searches is negligible.) */ + for (must_exist = false; !ret && must_exist <= true; must_exist++) + { + for (spec = bitmap_specs; !ret && *spec; spec++) + { + string name = kpse_var_expand (*spec); + ret = kpse_path_search (path, name, must_exist); + if (name != ret) + free (name); + } + } + + return ret; +} + +/* Look for FONT_NAME at resolution DPI in format FORMAT. Search the + (entire) PK path first, then the GF path, if we're looking for both. + Return any filename found, and (if we succeeded) fill in GLYPH_FILE. */ + +static string +try_size P4C(const_string, font_name, unsigned, dpi, + kpse_file_format_type, format, + kpse_glyph_file_type *, glyph_file) +{ + kpse_file_format_type format_found; + string ret; + boolean try_gf = format == kpse_gf_format || format == kpse_any_glyph_format; + boolean try_pk = format == kpse_pk_format || format == kpse_any_glyph_format; + + xputenv_int ("KPATHSEA_DPI", dpi); + + /* Look for PK first (since it's more likely to be found), then GF. */ + ret = try_pk ? try_format (font_name, dpi, kpse_pk_format) : NULL; + + if (ret != NULL) + format_found = kpse_pk_format; + else + { + if (try_gf) + { + ret = try_format (font_name, dpi, kpse_gf_format); + format_found = kpse_gf_format; + } + } + + if (ret != NULL && glyph_file) + { /* Success. Fill in the return info. Discard const. */ + glyph_file->name = (string) font_name; + glyph_file->dpi = dpi; + glyph_file->format = format_found; + } + + return ret; +} + +/* Look for FONT_NAME at resolution DPI, then at the resolutions within + KPSE_BITMAP_TOLERANCE of DPI. */ + +static string +try_resolution P4C(const_string, font_name, unsigned, dpi, + kpse_file_format_type, format, + kpse_glyph_file_type *, glyph_file) +{ + string ret = try_size (font_name, dpi, format, glyph_file); + + if (!ret) + { + unsigned r; + unsigned tolerance = KPSE_BITMAP_TOLERANCE (dpi); + unsigned lower_bound = (int) (dpi - tolerance) < 0 ? 0 : dpi - tolerance; + unsigned upper_bound = dpi + tolerance; + + /* Prefer scaling up to scaling down, since scaling down can omit + character features (Tom did this in dvips). */ + for (r = lower_bound; !ret && r <= upper_bound; r++) + if (r != dpi) + ret = try_size (font_name, r, format, glyph_file); + } + + return ret; +} + +/* Look up FONT_NAME in format FORMAT at DPI in the texfonts.map files + that we can find, returning the filename found and GLYPH_FILE. */ + +static string +try_fontmap P4C(const_string, font_name, unsigned, dpi, + kpse_file_format_type, format, + kpse_glyph_file_type *, glyph_file) +{ + static hash_table_type fontmap; + string *mapped_names; + string ret = NULL; + + if (fontmap.size == 0) + { /* If we wanted to complicate our lives, we could handle separate + maps for GF and PK ones. I don't see that this has any + practical utility, though, because if someone wants an alias, + most likely the alias should apply to non-glyphs as well as + glyphs (let alone to only GF format or PK format). */ + const_string map_path = kpse_init_format (kpse_any_glyph_format); + fontmap = map_create (map_path); + } + + mapped_names = map_lookup (fontmap, font_name); + if (mapped_names) + { + string mapped_name; + while ((mapped_name = *mapped_names++) && !ret) + { + xputenv ("KPATHSEA_NAME", mapped_name); + ret = try_resolution (mapped_name, dpi, format, glyph_file); + } + } + + return ret; +} + +/* Look for FONT_NAME in `kpse_fallback_resolutions', omitting DPI if we + happen across it. Return NULL if nothing found. Pass GLYPH_FILE + along as usual. Assume `kpse_fallback_resolutions' is sorted. */ + +static string +try_fallback_resolutions P4C(const_string, font_name, unsigned, dpi, + kpse_file_format_type, format, + kpse_glyph_file_type *, glyph_file) +{ + unsigned s; + int loc, max_loc; + int lower_loc, upper_loc; + unsigned lower_diff, upper_diff; + unsigned closest_diff = UINT_MAX; + string ret = NULL; /* In case the only fallback resolution is DPI. */ + + /* First find the fallback size closest to DPI. */ + for (s = 0; kpse_fallback_resolutions[s] != 0; s++) + { + unsigned this_diff = abs (kpse_fallback_resolutions[s] - dpi); + if (this_diff < closest_diff) + { + closest_diff = this_diff; + loc = s; + } + } + if (s == 0) + return ret; /* If nothing in list, quit now. */ + + max_loc = s; + lower_loc = loc - 1; + upper_loc = loc + 1; + + for (;;) + { + unsigned fallback = kpse_fallback_resolutions[loc]; + /* Don't bother to try DPI itself again. */ + if (fallback != dpi) + { + ret = try_resolution (font_name, fallback, format, glyph_file); + if (ret) + break; + } + + /* That didn't work. How far away are the locs above or below? */ + lower_diff = lower_loc > -1 + ? dpi - kpse_fallback_resolutions[lower_loc] : INT_MAX; + upper_diff = upper_loc < max_loc + ? kpse_fallback_resolutions[upper_loc] - dpi : INT_MAX; + + /* But if we're at the end in both directions, quit. */ + if (lower_diff == INT_MAX && upper_diff == INT_MAX) + break; + + /* Go in whichever direction is closest. */ + if (lower_diff < upper_diff) + { + loc = lower_loc; + lower_loc--; + } + else + { + loc = upper_loc; + upper_loc++; + } + } + + return ret; +} + +/* See the .h file for description. This is the entry point. */ + +string +kpse_find_glyph P4C(const_string, font_name, unsigned, dpi, + kpse_file_format_type, format, + kpse_glyph_file_type *, glyph_file) +{ + string ret; + kpse_glyph_source_type source; + + /* Start the search: try the name we're given. */ + source = kpse_glyph_source_normal; + xputenv ("KPATHSEA_NAME", font_name); + ret = try_resolution (font_name, dpi, format, glyph_file); + + /* Try all the various possibilities in order of preference. */ + if (!ret) + { + /* Maybe FONT_NAME was an alias. */ + source = kpse_glyph_source_alias; + ret = try_fontmap (font_name, dpi, format, glyph_file); + + /* If not an alias, try creating it on the fly with MakeTeXPK, + unless FONT_NAME is absolute or explicitly relative. */ + if (!ret && !kpse_absolute_p (font_name, true)) + { + source = kpse_glyph_source_maketex; + /* `try_resolution' leaves the envvar set randomly. */ + xputenv_int ("KPATHSEA_DPI", dpi); + ret = kpse_make_tex (format, font_name); + } + + /* If MakeTeX... succeeded, set return struct. Doesn't make sense for + `kpse_make_tex' to set it, since it can only succeed or fail, + unlike the other routines. */ + if (ret) + { + KPSE_GLYPH_FILE_DPI (*glyph_file) = dpi; + /* Have to discard const here. */ + KPSE_GLYPH_FILE_NAME (*glyph_file) = (string) font_name; + } + + /* If MakeTeX... failed, try any fallback resolutions. */ + else + { + if (kpse_fallback_resolutions) + ret = try_fallback_resolutions(font_name, dpi, format, glyph_file); + + /* We're down to the font of last resort. */ + if (!ret && kpse_fallback_font) + { + const_string name = kpse_fallback_font; + source = kpse_glyph_source_fallback; + xputenv ("KPATHSEA_NAME", name); + + /* As before, first try it at the given size. */ + ret = try_resolution (name, dpi, format, glyph_file); + + /* The fallback font at the fallback resolutions. */ + if (!ret && kpse_fallback_resolutions) + ret = try_fallback_resolutions (name, dpi, format, glyph_file); + } + } + } + + /* If RET is null, then the caller is not supposed to look at GLYPH_FILE, + so it doesn't matter if we assign something incorrect. */ + KPSE_GLYPH_FILE_SOURCE (*glyph_file) = source; + + return ret; +} + +/* The tolerances change whether we base things on DPI1 or DPI2. */ + +boolean +kpse_bitmap_tolerance P2C(double, dpi1, double, dpi2) +{ + unsigned tolerance = KPSE_BITMAP_TOLERANCE (dpi2); + unsigned lower_bound = (int) (dpi2 - tolerance) < 0 ? 0 : dpi2 - tolerance; + unsigned upper_bound = dpi2 + tolerance; + + return lower_bound <= dpi1 && dpi1 <= upper_bound; +} + +#ifdef TEST + +void +test_find_glyph (const_string font_name, unsigned dpi) +{ + string answer; + kpse_glyph_file_type ret; + + printf ("\nSearch for %s@%u:\n\t", font_name, dpi); + + answer = kpse_find_glyph_format (font_name, dpi, + kpse_any_glyph_format, &ret); + if (answer) + { + string format = ret.format == kpse_pk_format ? "pk" : "gf"; + if (!ret.name) + ret.name = "(null)"; + printf ("%s\n\t(%s@%u, %s)\n", answer, ret.name, ret.dpi, format); + } + else + puts ("(null)"); +} + + +int +main () +{ + test_find_glyph ("/usr/local/lib/tex/fonts/cm/cmr10", 300); /* absolute */ + test_find_glyph ("cmr10", 300); /* normal */ + test_find_glyph ("logo10", 300); /* find gf */ + test_find_glyph ("cmr10", 299); /* find 300 */ + test_find_glyph ("circle10", 300); /* in fontmap */ + test_find_glyph ("none", 300); /* do not find */ + kpse_fallback_font = "cmr10"; + test_find_glyph ("fallback", 300); /* find fallback font cmr10 */ + kpse_init_fallback_resolutions ("KPATHSEA_TEST_SIZES"); + test_find_glyph ("fallbackdpi", 759); /* find fallback font cmr10@300 */ + + xputenv ("GFFONTS", "."); + test_find_glyph ("cmr10", 300); /* different GFFONTS/TEXFONTS */ + + return 0; +} + +#endif /* TEST */ + + +/* +Local variables: +test-compile-command: "gcc -g -I. -I.. -DTEST tex-glyph.c kpathsea.a" +End: +*/
new file mode 100644 --- /dev/null +++ b/kpathsea/tex-make.c @@ -0,0 +1,301 @@ +/* tex-make.c: Run external programs to make TeX-related files. + +Copyright (C) 1993, 94 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-fopen.h> +#include <kpathsea/c-pathch.h> +#include <kpathsea/concatn.h> +#include <kpathsea/db.h> +#include <kpathsea/fn.h> +#include <kpathsea/magstep.h> +#include <kpathsea/readable.h> +#include <kpathsea/tex-make.h> +#include <kpathsea/variable.h> + + +/* We never throw away stdout, since that is supposed to be the filename + found, if all is successful. This variable controls whether stderr + is thrown away. */ +boolean kpse_make_tex_discard_errors = false; + +/* We set the envvar MAKETEX_MAG, which is part of the default spec for + MakeTeXPK above, based on KPATHSEA_DPI and MAKETEX_BASE_DPI. */ + +static void +set_maketex_mag P1H(void) +{ + char q[MAX_INT_LENGTH * 3 + 3]; + int m; + string dpi_str = getenv ("KPATHSEA_DPI"); + string bdpi_str = getenv ("MAKETEX_BASE_DPI"); + unsigned dpi = dpi_str ? atoi (dpi_str) : 0; + unsigned bdpi = bdpi_str ? atoi (bdpi_str) : 0; + + /* If the environment variables aren't set, it's a bug. */ + assert (dpi != 0 && bdpi != 0); + + /* Fix up for roundoff error. Hopefully the driver has already fixed + up DPI, but may as well be safe, and also get the magstep number. */ + (void) kpse_magstep_fix (dpi, bdpi, &m); + + /* Have to do something different for DOS? */ + if (m == 0) + sprintf (q, "%d+%d/%d", dpi / bdpi, dpi % bdpi, bdpi); + else + { /* m is encoded with LSB being a ``half'' bit (see magstep.h). Are + we making an assumption here about two's complement? Probably. + In any case, if m is negative, we have to put in the sign + explicitly, since m/2==0 if m==-1. */ + const_string sign = ""; + if (m < 0) + { + m *= -1; + sign = "-"; + } + sprintf (q, "magstep\\(%s%d.%d\\)", sign, m / 2, (m & 1) * 5); + } + xputenv ("MAKETEX_MAG", q); +} + +/* This MakeTeX... program was disabled, or the script failed. If this + was a font creation (according to FORMAT), append CMD + to a file missfont.log in the current directory. */ + +static void +misstex P2C(kpse_file_format_type, format, const_string, cmd) +{ + static FILE *missfont = NULL; + + /* If we weren't trying to make a font, do nothing. Maybe should + allow people to specify what they want recorded? */ + if (format > kpse_any_glyph_format && format != kpse_tfm_format + && format != kpse_vf_format) + return; + + /* If this is the first time, have to open the log file. */ + if (!missfont) + { + const_string missfont_name = "missfont.log"; + missfont = fopen (missfont_name, FOPEN_A_MODE); + if (!missfont && getenv ("TEXMFOUTPUT")) + { + missfont_name = concat3 (getenv ("TEXMFOUTPUT"), DIR_SEP_STRING, + missfont_name); + missfont = fopen (missfont_name, FOPEN_A_MODE); + } + + /* Should we really be unconditionally shouting this message? */ + if (missfont) + fprintf (stderr, "kpathsea: Appending font creation commands to %s.\n", + missfont_name); + } + + /* Write the command if we have a log file. */ + if (missfont) + { + fputs (cmd, missfont); + putc ('\n', missfont); + } +} + + +/* Assume the script outputs the filename it creates (and nothing + else) on standard output; hence, we run the script with `popen'. */ + +static string +maketex P2C(kpse_file_format_type, format, const_string, cmd) +{ + string ret; + FILE *f; + + /* Tell the user we are running the script, so they have a clue as to + what's going on if something messes up. */ + fprintf (stderr, "kpathsea: Running %s\n", cmd); + + /* Run the script. */ + f = popen (cmd, FOPEN_R_MODE); + + if (f) + { + int c; + string fn; /* The final filename. */ + unsigned len; /* And its length. */ + fn_type output; + output = fn_init (); /* Collect the script output. */ + + /* Read all the output and terminate with a null. */ + while ((c = getc (f)) != EOF) + fn_1grow (&output, c); + fn_1grow (&output, 0); + + /* Maybe should check for `EXIT_SUCCESS' status before even + looking at the output? */ + if (pclose (f) == -1) + FATAL_PERROR (cmd); + + len = FN_LENGTH (output); + fn = FN_STRING (output); + + /* Remove trailing newlines and returns. */ + while (len > 1 && (fn[len - 2] == '\n' || fn[len - 2] == '\r')) + { + fn[len - 2] = 0; + len--; + } + + /* If no output from script, return NULL. Otherwise check + what it output. */ + ret = len == 1 ? NULL : kpse_readable_file (fn); + + /* Free the name if we're not returning it. */ + if (fn != ret) + free (fn); + } + else + /* popen failed. Maybe should give error (optionally), but for + now be silent, to avoid annoying people who purposefully + don't have the script installed. */ + ret = NULL; + + if (ret == NULL) + misstex (format, cmd); + else + db_insert (ret); + + return ret; +} + + +/* Create BASE in FORMAT and return the generated filename, or + return NULL. */ + +string +kpse_make_tex P2C(kpse_file_format_type, format, const_string, base) +{ + kpse_format_info_type spec; /* some compilers lack struct initialization */ + string ret = NULL; + + spec = kpse_format_info[format]; + + if (spec.program) + { + /* See the documentation for the envvars we're dealing with here. */ + string args, cmd; + const_string prog = spec.program; /* MakeTeXPK */ + string PROG = uppercasify (prog); /* MAKETEXPK */ + string progenv = getenv (PROG); /* ENV{"MAKETEXPK"} */ + const_string arg_spec = progenv ? progenv : spec.program_args; + string mode = getenv ("MAKETEX_MODE"); + boolean unset_mode = false; + + set_maketex_mag (); + + /* Here's an awful kludge: if the mode is `/', unset it for the + call and then reset it. We could ignore a mode of / in + MakeTeXPK, but then everyone's MakeTeXPK would have to handle + that special case, which seems too onerous. `kpse_prog_init' + sets it to this in the first place when no mode is otherwise + specified; this is so when the user defines a resolution, they + don't also have to specify a mode; instead, MakeTeXPK's guesses + will take over. They use / for the value because then when it + is expanded as part of the PKFONTS et al. path values, we'll + wind up searching all the pk directories. We put $MAKETEX_MODE + in the path values in the first place so that sites with two + different devices with the same resolution can find the right + fonts; but such sites are uncommon, so they shouldn't make + things harder for everyone else. */ + if (mode && STREQ (mode, DIR_SEP_STRING)) + { + xputenv ("MAKETEX_MODE", ""); + unset_mode = true; + } + args = arg_spec ? kpse_var_expand (arg_spec) : (string) ""; + if (unset_mode) + xputenv ("MAKETEX_MODE", DIR_SEP_STRING); + + /* The command is the program name plus the arguments. */ + cmd = concatn (prog, " ", base, " ", args, NULL); + + if (spec.program_enabled_p) + { + /* Only way to discard errors is redirect stderr inside another + shell; otherwise, if the MakeTeX... script doesn't exist, we + will see the `sh: MakeTeX...: not found' error. No point in + doing this if we're not actually going to run anything. */ + if (kpse_make_tex_discard_errors) + { + string old_cmd = cmd; + cmd = concat3 ("sh -c \"", cmd, "\" 2>/dev/null"); + free (old_cmd); + } + + ret = maketex (format, cmd); + } + else + misstex (format, cmd); + + free (PROG); + free (cmd); + if (*args) + free (args); + } + + return ret; +} + +#ifdef TEST + +void +test_make_tex (kpse_file_format_type fmt, const_string base) +{ + string answer; + + printf ("\nAttempting %s in format %d:\n", base, fmt); + + answer = kpse_make_tex (fmt, base); + puts (answer ? answer : "(null)"); +} + + +int +main () +{ + xputenv ("KPATHSEA_DPI", "781"); /* call MakeTeXPK */ + xputenv ("MAKETEX_BASE_DPI", "300"); /* call MakeTeXPK */ + KPSE_MAKE_SPEC_ENABLED (kpse_make_specs[kpse_pk_format]) = true; + test_make_tex (kpse_pk_format, "cmr10"); + + /* Fail with MakeTeXTFM. */ + KPSE_MAKE_SPEC_ENABLED (kpse_make_specs[kpse_tfm_format]) = true; + test_make_tex (kpse_tfm_format, "foozler99"); + + /* Call something disabled. */ + test_make_tex (kpse_bst_format, "no-way"); + + return 0; +} + +#endif /* TEST */ + + +/* +Local variables: +test-compile-command: "gcc -g -I. -I.. -DTEST tex-make.c kpathsea.a" +End: +*/
new file mode 100644 --- /dev/null +++ b/kpathsea/tilde.c @@ -0,0 +1,122 @@ +/* tilde.c: Expand user's home directories. + +Copyright (C) 1993 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-pathch.h> +#include <kpathsea/tilde.h> + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif + + +/* If NAME has a leading ~ or ~user, Unix-style, expand it to the user's + home directory, and return a new malloced string. If no ~, or no + <pwd.h>, just return NAME. */ + +string +kpse_tilde_expand P1C(const_string, name) +{ +#ifdef HAVE_PWD_H + const_string expansion; + const_string home; + + assert (name); + + /* If no leading tilde, do nothing. */ + if (*name != '~') + expansion = name; + + /* If `~' or `~/', use $HOME if it exists, or `.' if it doesn't. */ + else if (IS_DIR_SEP (name[1]) || name[1] == 0) + { + home = getenv ("HOME"); + if (!home) + home = "."; + + expansion = name[1] == 0 + ? xstrdup (home) : concat3 (home, DIR_SEP_STRING, name + 2); + } + + /* If `~user' or `~user/', look up user in the passwd database. */ + else + { + struct passwd *p; + string user; + unsigned c = 2; + while (!IS_DIR_SEP (name[c]) && name[c] != 0) + c++; + + user = (string) xmalloc (c); + strncpy (user, name + 1, c - 1); + user[c - 1] = 0; + + /* We only need the cast here for those (deficient) systems + which do not declare `getpwnam' in <pwd.h>. */ + p = (struct passwd *) getpwnam (user); + free (user); + + /* If no such user, just use `.'. */ + home = p == NULL ? "." : p->pw_dir; + + expansion = name[c] == 0 ? xstrdup (home) : concat (home, name + c); + } + + /* We may return the same thing as the original, and then we might not + be returning a malloc-ed string. */ + return (string) expansion; +#else + return name; +#endif /* not HAVE_PWD_H */ +} + +#ifdef TEST + +void +test_expand_tilde (const_string filename) +{ + string answer; + + printf ("Tilde expansion of `%s':\t", filename ? filename : "(null)"); + answer = kpse_expand_tilde (filename); + puts (answer); +} + +int +main () +{ + string tilde_path = "tilde"; + + test_expand_tilde (""); + test_expand_tilde ("none"); + test_expand_tilde ("~root"); + test_expand_tilde ("~"); + test_expand_tilde ("foo~bar"); + + return 0; +} + +#endif /* TEST */ + + +/* +Local variables: +standalone-compile-command: "gcc -g -I. -I.. -DTEST tilde.c kpathsea.a" +End: +*/
new file mode 100644 --- /dev/null +++ b/kpathsea/truncate.c @@ -0,0 +1,57 @@ +/* truncate.c: truncate too-long components in a filename. + +Copyright (C) 1993 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-namemx.h> +#include <kpathsea/c-pathch.h> +#include <kpathsea/c-pathmx.h> +#include <kpathsea/truncate.h> + + +/* Truncate any too-long components in NAME, returning the result. It's + too bad this is necessary. See comments in readable.c for why. */ + +string +kpse_truncate_filename P1C(const_string, name) +{ + unsigned c_len = 0; /* Length of current component. */ + unsigned ret_len = 0; /* Length of constructed result. */ + + /* Allocate enough space. */ + string ret = (string) xmalloc (strlen (name) + 1); + + for (; *name; name++) + { + if (IS_DIR_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; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/uppercasify.c @@ -0,0 +1,36 @@ +/* uppercasify.c: change all lowercase letters to uppercase. + +Copyright (C) 1993 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-ctype.h> + + +string +uppercasify P1C(const_string, s) +{ + string target; + string ret = xstrdup (s); + + for (target = ret; *target; target++) + { + *target = TOUPPER (*target); + } + + return ret; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/variable.c @@ -0,0 +1,185 @@ +/* variable.c: variable expansion. + +Copyright (C) 1993, 94 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-ctype.h> +#include <kpathsea/cnf.h> +#include <kpathsea/fn.h> +#include <kpathsea/variable.h> + + +/* 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. */ + +static void +expand P3C(fn_type *, expansion, const_string, start, const_string, end) +{ + string value; + unsigned len = end - start + 1; + string var = xmalloc (len + 1); + strncpy (var, start, len); + var[len] = 0; + + /* First check the environment variable. */ + value = getenv (var); + + /* If no envvar, check the config files. */ + if (!value) + value = kpse_cnf_get (var); + + if (value) + { + value = kpse_var_expand (value); + 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 generalize to allow some or all of the various shell + ${...} constructs, especially ${var-value}. */ + +string +kpse_var_expand P1C(const_string, src) +{ + const_string s; + string 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_string 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_string var_end = ++s; + + while (*var_end && !IS_VAR_END_DELIMITER (*var_end)) + var_end++; + + if (! *var_end) + { + WARNING1 ("%s: No matching right brace 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; +} + +#ifdef TEST + +static void +test_var (string test, string right_answer) +{ + string result = kpse_var_expand (test); + + printf ("expansion of `%s'\t=> %s", test, result); + if (!STREQ (result, right_answer)) + printf (" [should be `%s']", right_answer); + putchar ('\n'); +} + + +int +main () +{ + test_var ("a", "a"); + test_var ("$foo", ""); + test_var ("a$foo", "a"); + test_var ("$foo a", " a"); + test_var ("a$foo b", "a b"); + + xputenv ("FOO", "foo value"); + test_var ("a$FOO", "afoo value"); + + xputenv ("Dollar", "$"); + test_var ("$Dollar a", "$ a"); + + test_var ("a${FOO}b", "afoo valueb"); + test_var ("a${}b", "ab"); + + test_var ("$$", ""); /* and error */ + test_var ("a${oops", "a"); /* and error */ + + return 0; +} + +#endif /* TEST */ + + +/* +Local variables: +standalone-compile-command: "gcc -g -I. -I.. -DTEST variable.c kpathsea.a" +End: +*/
new file mode 100644 --- /dev/null +++ b/kpathsea/version.c @@ -0,0 +1,1 @@ +char *kpathsea_version_string = (char *) "kpathsea version 2.6";
new file mode 100644 --- /dev/null +++ b/kpathsea/xcalloc.c @@ -0,0 +1,35 @@ +/* xcalloc.c: calloc with error checking. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + + +address +xcalloc P2C(unsigned, nelem, unsigned, elsize) +{ + address new_mem = (address) calloc (nelem, elsize); + + if (new_mem == NULL) + { + fprintf (stderr, "xcalloc: request for %u elements of size %u failed.\n", + nelem, elsize); + abort (); + } + + return new_mem; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/xfopen.c @@ -0,0 +1,44 @@ +/* xfopen.c: fopen and fclose with error checking. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + + +/* These routines just check the return status from standard library + routines and abort if an error happens. */ + +FILE * +xfopen P2C(const_string, filename, const_string, mode) +{ + FILE *f = fopen (filename, mode); + + if (f == NULL) + FATAL_PERROR (filename); + + return f; +} + + +void +xfclose P2C(FILE *, f, const_string, filename) +{ + if (fclose (f) == EOF) + FATAL_PERROR (filename); +} + +
new file mode 100644 --- /dev/null +++ b/kpathsea/xmalloc.c @@ -0,0 +1,41 @@ +/* xmalloc.c: malloc with error checking. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Don't include config.h or all our other usual includes, since + it's useful to just throw this file into other programs. */ + +#include <stdio.h> +extern char *malloc (); + + +void * +xmalloc (size) + 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; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/xopendir.c @@ -0,0 +1,47 @@ +/* xopendir.c: opendir and closedir with error checking. + +Copyright (C) 1992, 93, 94 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/xopendir.h> + + +DIR * +xopendir P1C(string, dirname) +{ + DIR *d = opendir (dirname); + + if (d == NULL) + FATAL_PERROR (dirname); + + return d; +} + + +void +xclosedir P1C(DIR *, d) +{ +#ifdef VOID_CLOSEDIR + closedir (d); +#else + int ret = closedir (d); + + if (ret != 0) + FATAL ("closedir failed"); +#endif +}
new file mode 100644 --- /dev/null +++ b/kpathsea/xputenv.c @@ -0,0 +1,107 @@ +/* xputenv.c: set an environment variable without return. + +Copyright (C) 1993, 94 Karl Berry. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +/* Avoid implicit declaration warning. */ +extern int putenv (); + +/* This `x' function is different from the others in that it takes + different parameters than the standard function; but I find it much + more convenient to pass the variable and the value separately. Also, + this way we can guarantee that the environment value won't become + garbage. Also, putenv just overwrites old entries with + the new, and we want to reclaim that space -- this may be called + hundreds of times on a run. + + But naturally, some systems do it differently. In this case, it's + net2 that is smart and does its own saving/freeing. If you can write + a test for configure to check this, please send it to me. Until then, + you'll have to define SMART_PUTENV yourself. */ + +void +xputenv P2C(const_string, var_name, const_string, value) +{ + static const_string *saved_env_items = NULL; + static unsigned saved_len; + string old_item = NULL; + string new_item = concat3 (var_name, "=", value); + +#ifndef SMART_PUTENV + /* Check if we have saved anything yet. */ + if (!saved_env_items) + { + saved_env_items = XTALLOC1 (const_string); + saved_env_items[0] = var_name; + saved_len = 1; + } + else + { + /* Check if we've assigned VAR_NAME before. */ + unsigned i; + unsigned len = strlen (var_name); + for (i = 0; i < saved_len && !old_item; i++) + { + if (STREQ (saved_env_items[i], var_name)) + { + old_item = getenv (var_name); + assert (old_item); + /* Back up to the `NAME=' in the environment before the + value that getenv returns. */ + old_item -= (len + 1); + } + } + + if (!old_item) + { + /* If we haven't seen VAR_NAME before, save it. Assume it is + in safe storage. */ + saved_len++; + XRETALLOC (saved_env_items, saved_len, const_string); + saved_env_items[saved_len - 1] = var_name; + } + } +#endif /* not SMART_PUTENV */ + + /* As far as I can see there's no way to distinguish between the + various errors; putenv doesn't have errno values. */ + if (putenv (new_item) < 0) + FATAL1 ("putenv (%s) failed", new_item); + +#ifndef SMART_PUTENV + /* Can't free `new_item' because its contained value is now in + `environ', but we can free `old_item', since it's been replaced. */ + if (old_item) + free (old_item); +#endif /* not SMART_PUTENV */ +} + + +/* A special case for setting a variable to a numeric value + (specifically, KPATHSEA_DPI). We don't need to dynamically allocate + and free the string for the number, since it's saved as part of the + environment value. */ + +void +xputenv_int P2C(const_string, var_name, int, num) +{ + char str[MAX_INT_LENGTH]; + sprintf (str, "%d", num); + + xputenv (var_name, str); +}
new file mode 100644 --- /dev/null +++ b/kpathsea/xrealloc.c @@ -0,0 +1,54 @@ +/* xrealloc.c: realloc with error checking. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Don't include config.h or all our other usual includes, since + it's useful to just throw this file into other programs. */ + +#include <stdio.h> +extern char *realloc (); + + +extern void *xmalloc (); + +void * +xrealloc (old_ptr, size) + 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; +}
new file mode 100644 --- /dev/null +++ b/kpathsea/xstat.c @@ -0,0 +1,52 @@ +/* xstat.c: stat and (maybe) lstat with error checking. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + +#include <kpathsea/xstat.h> + + +struct stat +xstat P1C(const_string, path) +{ + struct stat s; + + if (stat (path, &s) != 0) + FATAL_PERROR (path); + + return s; +} + + +/* If we don't have symbolic links, lstat is the same as stat, and + a #define is made in the include file. We declare lstat to avoid an + implicit declaration warning for development; sigh. */ + +#ifdef S_ISLNK +extern int lstat (); +struct stat +xlstat P1C(const_string, path) +{ + struct stat s; + + if (lstat (path, &s) != 0) + FATAL_PERROR (path); + + return s; +} +#endif
new file mode 100644 --- /dev/null +++ b/kpathsea/xstrdup.c @@ -0,0 +1,29 @@ +/* xstrdup.c: strdup with error checking. + +Copyright (C) 1992, 93 Free Software Foundation, Inc. + +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, 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <kpathsea/config.h> + + +/* Return a copy of S in new storage. */ + +string +xstrdup P1C(const_string, s) +{ + string new_string = (string) xmalloc (strlen (s) + 1); + return strcpy (new_string, s); +}