diff options
Diffstat (limited to 'Build/source/texk/kpathsea/kpsewhich.c')
-rw-r--r-- | Build/source/texk/kpathsea/kpsewhich.c | 941 |
1 files changed, 941 insertions, 0 deletions
diff --git a/Build/source/texk/kpathsea/kpsewhich.c b/Build/source/texk/kpathsea/kpsewhich.c new file mode 100644 index 00000000000..76f956a2480 --- /dev/null +++ b/Build/source/texk/kpathsea/kpsewhich.c @@ -0,0 +1,941 @@ +/* kpsewhich -- standalone path lookup and variable expansion for Kpathsea. + Ideas from Thomas Esser, Pierre MacKay, and many others. + + Copyright 1995-2021 Karl Berry & Olaf Weber. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, see <http://www.gnu.org/licenses/>. */ + +#include <kpathsea/config.h> +#include <kpathsea/c-ctype.h> +#include <kpathsea/c-pathch.h> +#include <kpathsea/cnf.h> +#include <kpathsea/expand.h> +#include <kpathsea/getopt.h> +#include <kpathsea/line.h> +#include <kpathsea/pathsearch.h> +#include <kpathsea/proginit.h> +#include <kpathsea/str-list.h> +#include <kpathsea/tex-file.h> +#include <kpathsea/tex-glyph.h> +#include <kpathsea/variable.h> +#include <kpathsea/version.h> + +/* For variable and path expansion. (-expand-var, -expand-path, + -show-path, etc.) */ +string var_to_expand = NULL; +string braces_to_expand = NULL; +string path_to_expand = NULL; +string path_to_show = NULL; +string var_to_value = NULL; +string var_to_brace_value = NULL; + +/* Array/count of cnf lines from the command line. (--cnf-line) */ +static string *user_cnf_lines = NULL; +static unsigned user_cnf_nlines = 0; + +/* Base resolution. (-D, -dpi) */ +unsigned dpi = 600; + +/* The engine name, for `$engine' construct in texmf.cnf. (-engine) */ +string engine = NULL; + +/* Interactively ask for names to look up? (-interactive) */ +boolean interactive = false; + +/* The device name, for $MAKETEX_MODE. (-mode) */ +string mode = NULL; + +/* Search the disk as well as ls-R? (-must-exist, -mktex) */ +boolean must_exist = false; + +/* The program name, for `.PROG' construct in texmf.cnf. (-program) */ +string progname = NULL; + +/* Safe input and output names to check. (-safe-in-name and -safe-out-name) */ +string safe_in_name = NULL; +string safe_out_name = NULL; + +/* Return all matches, not just the first one? (-all) */ +boolean show_all = false; + +/* Only match files in given subdirs. (-subdir) */ +str_list_type subdir_paths; + +/* The file type and path for lookups. (-format, -path) */ +kpse_file_format_type user_format = kpse_last_format; +string user_format_string; +string user_path; + + + +/* Define one-word abbreviations for those format types which + can otherwise only be specified by strings containing spaces. */ + +typedef struct +{ + const_string abbr; + kpse_file_format_type format; +} format_abbr_type; + +static format_abbr_type format_abbrs[] + = { { "bitmapfont", kpse_any_glyph_format }, + { "mpsupport", kpse_mpsupport_format }, + { "doc", kpse_texdoc_format }, + { "source", kpse_texsource_format }, + { "trofffont", kpse_troff_font_format }, + { "dvipsconfig", kpse_dvips_config_format }, + { "web2c", kpse_web2c_format }, + { "othertext", kpse_program_text_format }, + { "otherbin", kpse_program_binary_format }, + { "miscfont", kpse_miscfonts_format }, + { "cmap", kpse_cmap_format }, + { "pdftexconfig", kpse_pdftex_config_format }, + { NULL, kpse_last_format } }; + +/* The function to look up STR in the abbr table above. + This is called only on a user-specified format string. + Return `kpse_last_format' if no match. */ + +static kpse_file_format_type +format_abbr (const_string str) +{ + kpse_file_format_type ret = kpse_last_format; + unsigned a = 0; + + while (format_abbrs[a].abbr != NULL) { + if (STREQ (str, format_abbrs[a].abbr)) { + ret = format_abbrs[a].format; + break; + } + a++; + } + + return ret; +} + + + +/* Return the <number> substring in `<name>.<number><stuff>', if S has + that form. If it doesn't, return 0. */ + +static unsigned +find_dpi (string s) +{ + unsigned dpi_number = 0; + const_string extension = find_suffix (s); + + if (extension != NULL) + sscanf (extension, "%u", &dpi_number); + + return dpi_number; +} + + + +/* Return true if FTRY (the candidate suffix) matches NAME. If + IS_FILENAME is true, the check is simply that FTRY is a suffix of + NAME. If false (that is, NAME is a format), then FTRY and NAME must + be entirely equal. */ + +static boolean +try_suffix (boolean is_filename, string name, unsigned name_len, + const_string ftry) +{ + unsigned try_len; + + if (!ftry || ! *ftry) { + return false; + } + + try_len = strlen (ftry); + if (try_len > name_len) { + /* Candidate is longer than what we're looking for. */ + return false; + } + if (!is_filename && try_len < name_len) { + /* We're doing format names, not file names, and candidate is + shorter than what we're looking for. E.g., do not find `lua' + when looking for `clua'. */ + return false; + } + + if (FILESTRCASEEQ (name + name_len - try_len, ftry)) { + return true; + } + + return false; +} + + + +/* Use the file type from -format if that was previously determined + (i.e., the user_format global variable), else guess dynamically from + NAME. Return kpse_last_format if undeterminable. This function is + also used to parse the -format string, a case we distinguish via + is_filename being false. + + A few filenames have been hard-coded for format types that + differ from what would be inferred from their extensions. */ + +static kpse_file_format_type +find_format (kpathsea kpse, string name, boolean is_filename) +{ + kpse_file_format_type ret = kpse_last_format; + + if (is_filename && user_format != kpse_last_format) { + ret = user_format; /* just return what we already computed */ + + } else if (FILESTRCASEEQ (name, "config.ps")) { + ret = kpse_dvips_config_format; + } else if (FILESTRCASEEQ (name, "fmtutil.cnf")) { + ret = kpse_web2c_format; + } else if (FILESTRCASEEQ (name, "glyphlist.txt")) { + ret = kpse_fontmap_format; + } else if (FILESTRCASEEQ (name, "mktex.cnf")) { + ret = kpse_web2c_format; + } else if (FILESTRCASEEQ (name, "pdfglyphlist.txt")) { + ret = kpse_fontmap_format; + } else if (FILESTRCASEEQ (name, "pdftex.cfg")) { + ret = kpse_pdftex_config_format; + } else if (FILESTRCASEEQ (name, "texglyphlist.txt")) { + ret = kpse_fontmap_format; + } else if (FILESTRCASEEQ (name, "texmf.cnf")) { + ret = kpse_cnf_format; + } else if (FILESTRCASEEQ (name, "updmap.cfg")) { + ret = kpse_web2c_format; + } else if (FILESTRCASEEQ (name, "XDvi")) { + ret = kpse_program_text_format; + } else { + if (!is_filename) { + /* Look for kpsewhich-specific format abbreviations. */ + ret = format_abbr (name); + } + + if (ret == kpse_last_format) { + int f = 0; /* kpse_file_format_type */ + unsigned name_len = strlen (name); + + while (f != kpse_last_format) { + const_string *ext; + const_string ftry; + boolean found = false; + + if (!kpse->format_info[f].type) + kpathsea_init_format (kpse, (kpse_file_format_type) f); + +/* Just to abbreviate this lengthy call. */ +#define TRY_SUFFIX(ftry) try_suffix (is_filename, name, name_len, (ftry)) + + if (!is_filename) { + /* Allow the long name, but only in the format options. We don't + want a filename confused with a format name. */ + ftry = kpse->format_info[f].type; + found = TRY_SUFFIX (ftry); + } + for (ext = kpse->format_info[f].suffix; !found && ext && *ext; ext++) { + found = TRY_SUFFIX (*ext); + } + for (ext=kpse->format_info[f].alt_suffix; !found && ext && *ext;ext++){ + found = TRY_SUFFIX (*ext); + } + + if (found) + break; + f++; + } + ret = f; + } + } + return ret; +} + + + +/* Return newly-allocated NULL-terminated list of strings from MATCHES + that are prefixed with any of the subdirectories in SUBDIRS. That + is, for a string S in MATCHES, its dirname must end with one of the + elements in SUBDIRS. For instance, if subdir=foo/bar, that will + match a string foo/bar/baz or /some/texmf/foo/bar/baz. + + We don't reallocate the actual strings, just the list elements. + Perhaps later we will implement wildcards or // or something. */ + +#if defined(WIN32) +static string * +kpathsea_subdir_match (kpathsea kpse, str_list_type subdirs, string *matches) +{ + string *ret = XTALLOC1 (string); + unsigned len = 1; + unsigned e; + unsigned m; + string p; + + for (e = 0; e < STR_LIST_LENGTH (subdirs); e++) { + for (p = STR_LIST_ELT (subdirs, e); *p; p++) { + if (*p == '\\') + *p = '/'; + else if (kpathsea_IS_KANJI(kpse, p)) + p++; + } + } + + for (m = 0; matches[m]; m++) { + unsigned loc; + string s = xstrdup (matches[m]); + for (loc = strlen (s); loc > 0 && !IS_DIR_SEP_CH (s[loc-1]); loc--) + ; + while (loc > 0 && IS_DIR_SEP_CH (s[loc-1])) { + loc--; + } + s[loc] = 0; /* wipe out basename */ + + for (e = 0; e < STR_LIST_LENGTH (subdirs); e++) { + string subdir = STR_LIST_ELT (subdirs, e); + unsigned subdir_len = strlen (subdir); + while (subdir_len > 0 && IS_DIR_SEP_CH (subdir[subdir_len-1])) { + subdir_len--; + subdir[subdir_len] = 0; /* remove trailing slashes from subdir spec */ + } + if (FILESTRCASEEQ (subdir, s + loc - subdir_len)) { + /* matched, save this one. */ + XRETALLOC (ret, len + 1, string); + ret[len-1] = matches[m]; + len++; + } + } + free (s); + } + ret[len-1] = NULL; + return ret; +} + +#else /* WIN32 */ +static string * +subdir_match (str_list_type subdirs, string *matches) +{ + string *ret = XTALLOC1 (string); + unsigned len = 1; + unsigned e; + unsigned m; + + for (m = 0; matches[m]; m++) { + unsigned loc; + string s = xstrdup (matches[m]); + for (loc = strlen (s); loc > 0 && !IS_DIR_SEP_CH (s[loc-1]); loc--) + ; + while (loc > 0 && IS_DIR_SEP_CH (s[loc-1])) { + loc--; + } + s[loc] = 0; /* wipe out basename */ + + for (e = 0; e < STR_LIST_LENGTH (subdirs); e++) { + string subdir = STR_LIST_ELT (subdirs, e); + unsigned subdir_len = strlen (subdir); + while (subdir_len > 0 && IS_DIR_SEP_CH (subdir[subdir_len-1])) { + subdir_len--; + subdir[subdir_len] = 0; /* remove trailing slashes from subdir spec */ + } + if (FILESTRCASEEQ (subdir, s + loc - subdir_len)) { + /* matched, save this one. */ + XRETALLOC (ret, len + 1, string); + ret[len-1] = matches[m]; + len++; + } + } + free (s); + } + ret[len-1] = NULL; + return ret; +} +#endif /* WIN32 */ + + +/* Look up a single filename NAME, filtering by given subdirectories if + -subdir was specified. Print all matches if global `show_all' is + true, else just the first match. Return 0 if success, 1 if failure. */ + +static unsigned +lookup (kpathsea kpse, string name) +{ + int i; + string ret = NULL; + string *ret_list = NULL; + + if (user_path) { + /* Translate ; to : if that's our ENV_SEP. See cnf.c. */ + if (IS_ENV_SEP (':')) { + string loc; + for (loc = user_path; *loc; loc++) { + if (*loc == ';') + *loc = ':'; + } + } + user_path = kpathsea_path_expand (kpse, user_path); + if (show_all) { + ret_list = kpathsea_all_path_search (kpse, user_path, name); + } else { + ret = kpathsea_path_search (kpse, user_path, name, must_exist); + } + + } else { + /* No user-specified search path, check user format or guess from NAME. */ + kpse_file_format_type fmt = find_format (kpse, name, true); + + switch (fmt) { + case kpse_pk_format: + case kpse_gf_format: + case kpse_any_glyph_format: + { + kpse_glyph_file_type glyph_ret; + string temp = remove_suffix (name); + /* Try to extract the resolution from the name. */ + unsigned local_dpi = find_dpi (name); + if (!local_dpi) + local_dpi = dpi; + if (show_all) { + /* Because the whole glyph lookup thing is too complicated + to try all the alternatives (cmr10.600pk vs. + dpi600/cmr10.pk, just for starters). Patches welcome :). */ + WARNING1 ("kpsewhich: Ignoring --all for bitmap font, sorry: %s", + name); + } + ret = kpathsea_find_glyph (kpse, temp, + local_dpi, fmt, &glyph_ret); + if (temp != name) + free (temp); + } + break; + + case kpse_last_format: + /* If the suffix isn't recognized, assume it's a tex file. */ + fmt = kpse_tex_format; + /* fall through */ + + default: + if (show_all) { + ret_list = kpathsea_find_file_generic (kpse, name, fmt, + must_exist, true); + } else { + ret = kpathsea_find_file (kpse, name, fmt, must_exist); + } + } + } + + /* Turn single return into a null-terminated list for uniform treatment. */ + if (ret) { + ret_list = XTALLOC (2, string); + ret_list[0] = ret; + ret_list[1] = NULL; + } + + /* Filter by subdirectories, if specified. */ + if (!STR_LIST_EMPTY (subdir_paths)) { +#if defined(WIN32) + string *new_list = kpathsea_subdir_match (kpse, subdir_paths, ret_list); +#else + string *new_list = subdir_match (subdir_paths, ret_list); +#endif /* WIN32 */ + free (ret_list); + ret_list = new_list; + } + + /* Print output. */ + if (ret_list) { + for (i = 0; ret_list[i]; i++) +#ifdef WIN32 + kpathsea_win32_puts (kpse, ret_list[i]); +#else + puts (ret_list[i]); +#endif + /* Save whether we found anything */ + ret = ret_list[0]; + free (ret_list); + } + + return ret == NULL; +} + +/* Help message. */ + +#define USAGE "\n\ +Standalone path lookup and expansion for the Kpathsea library.\n\ +The default is to look up each FILENAME in turn and report its\n\ +first match (if any) to standard output.\n\ +\n\ +When looking up format (.fmt/.base/.mem) files, it is usually necessary\n\ +to also use -engine, or nothing will be returned; in particular,\n\ +-engine=/ will return matching format files for any engine.\n\ +\n\ +-all output all matches, one per line (no effect with pk/gf).\n\ +[-no]-casefold-search fall back to case-insensitive search if no exact match.\n\ +-cnf-line=STRING parse STRING as a configuration file line.\n\ +-debug=NUM set debugging flags.\n\ +-D, -dpi=NUM use a base resolution of NUM; default 600.\n\ +-engine=STRING set engine name to STRING.\n\ +-expand-braces=STRING output variable and brace expansion of STRING.\n\ +-expand-path=STRING output complete path expansion of STRING.\n\ +-expand-var=STRING output variable expansion of STRING.\n\ +-format=NAME use file type NAME (list shown by -help-formats).\n\ +-help display this message and exit.\n\ +-help-formats display information about all supported file formats.\n\ +-interactive ask for additional filenames to look up.\n\ +[-no]-mktex=FMT disable/enable mktexFMT generation (FMT=pk/mf/tex/tfm).\n\ +-mode=STRING set device name for $MAKETEX_MODE to STRING; no default.\n\ +-must-exist search the disk as well as ls-R if necessary.\n\ +-path=STRING search in the path STRING.\n\ +-progname=STRING set program name to STRING.\n\ +-safe-in-name=STRING check if STRING is ok to open for input.\n\ +-safe-out-name=STRING check if STRING is ok to open for output.\n\ +-show-path=TYPE output search path for file type TYPE\n\ + (list shown by -help-formats).\n\ +-subdir=STRING only output matches whose directory ends with STRING.\n\ +-var-brace-value=STRING output brace-expanded value of variable $STRING.\n\ +-var-value=STRING output variable-expanded value of variable $STRING.\n\ +-version display version information number and exit.\n \ +" + +static void +help_message (kpathsea kpse, string *argv) +{ + printf ("Usage: %s [OPTION]... [FILENAME]...\n", argv[0]); + fputs (USAGE, stdout); + putchar ('\n'); + fputs (kpathsea_bug_address, stdout); + fputs ("Kpathsea home page: https://tug.org/kpathsea/\n", stdout); + exit (0); +} + +static void +help_formats (kpathsea kpse, string *argv) +{ + int f; /* kpse_file_format_type */ + + /* Have to set this for init_format to work. */ + kpathsea_set_program_name (kpse, argv[0], progname); + + puts (kpathsea_version_string); + puts ("\nRecognized Kpathsea format names and their (abbreviations) and suffixes:"); + for (f = 0; f < kpse_last_format; f++) { + const_string *ext; + + const_string envvar_list = + kpathsea_init_format_return_varlist (kpse, (kpse_file_format_type) f); + printf ("%s", kpse->format_info[f].type); + + /* Show abbreviation if we accept one. We repeatedly go through the + abbr list here, but it's so short, it doesn't matter. */ + { + unsigned a = 0; + while (format_abbrs[a].abbr != NULL) { + if (f == format_abbrs[a].format) { + printf (" (%s)", format_abbrs[a].abbr); + break; + } + a++; + } + } + + /* Regular suffixes. */ + putchar (':'); + for (ext = kpse->format_info[f].suffix; ext && *ext; ext++) { + putchar (' '); +#ifdef WIN32 + kpathsea_win32_fputs (kpse, *ext, stdout); +#else + fputs (*ext, stdout); +#endif + } + + if (kpse->format_info[f].alt_suffix) { + /* leave extra space between default and alt suffixes */ + putchar (' '); + } + for (ext = kpse->format_info[f].alt_suffix; ext && *ext; ext++) { + putchar (' '); +#ifdef WIN32 + kpathsea_win32_fputs (kpse, *ext, stdout); +#else + fputs (*ext, stdout); +#endif + } + + printf (" [variables: %s]\n", envvar_list); + + printf (" [original path (from %s) = %s]\n", + kpse->format_info[f].path_source, kpse->format_info[f].raw_path); + } + + fputs ("\nTo see paths after expansion, use --show-path=FMT.\n\n", stdout); + fputs (kpathsea_bug_address, stdout); + exit (0); +} + + +/* Reading the options. */ + +/* This macro tests 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) + +/* If S is NULL or the empty string, abort. */ +#define ENSURE_NONEMPTY_STRING(s) do { \ + if (!(s) || ! *(s)) { \ + fprintf (stderr, "kpsewhich: empty argument for %s\n", \ + long_options[option_index].name); \ + fputs ("Try `kpsewhich --help' for more information.\n", stderr); \ + exit (EXIT_FAILURE); } } while (0) + +/* SunOS cc can't initialize automatic structs. */ +static struct option long_options[] + = { { "D", 1, 0, 0 }, + { "all", 0, (int *) &show_all, 1 }, + { "casefold-search", 0, 0, 0 }, + { "cnf-line", 1, 0, 0 }, + { "debug", 1, 0, 0 }, + { "dpi", 1, 0, 0 }, + { "engine", 1, 0, 0 }, + { "expand-braces", 1, 0, 0 }, + { "expand-path", 1, 0, 0 }, + { "expand-var", 1, 0, 0 }, + { "format", 1, 0, 0 }, + { "help", 0, 0, 0 }, + { "help-formats", 0, 0, 0 }, + { "interactive", 0, (int *) &interactive, 1 }, + { "mktex", 1, 0, 0 }, + { "mode", 1, 0, 0 }, + { "must-exist", 0, (int *) &must_exist, 1 }, + { "path", 1, 0, 0 }, + { "no-casefold-search", 0, 0, 0 }, + { "no-mktex", 1, 0, 0 }, + { "progname", 1, 0, 0 }, + { "safe-in-name", 1, 0, 0 }, + { "safe-out-name", 1, 0, 0 }, + { "subdir", 1, 0, 0 }, + { "show-path", 1, 0, 0 }, + { "var-brace-value", 1, 0, 0 }, + { "var-value", 1, 0, 0 }, + { "version", 0, 0, 0 }, + { 0, 0, 0, 0 } }; + +static void +read_command_line (kpathsea kpse, int argc, string *argv) +{ + int g; /* `getopt' return code. */ + int option_index; + + for (;;) { + g = getopt_long_only (argc, argv, "", long_options, &option_index); + + if (g == -1) + break; + + if (g == '?') + exit (EXIT_FAILURE); /* Unknown option. */ + + assert (g == 0); /* We have no short option names. */ + + if (ARGUMENT_IS ("casefold-search")) { + /* We can't use a boolean for casefold-search because we want to + distinguish it being set with an option vs. leaving the default + (by default). */ + xputenv ("texmf_casefold_search", "1"); + + } else if (ARGUMENT_IS ("cnf-line")) { + if (user_cnf_lines == NULL) { + user_cnf_nlines = 1; + user_cnf_lines = xmalloc (sizeof (const_string)); + } else { + user_cnf_nlines++; + user_cnf_lines = xrealloc (user_cnf_lines, + user_cnf_nlines * sizeof (const_string)); + } + user_cnf_lines[user_cnf_nlines-1] = xstrdup (optarg); + + } else if (ARGUMENT_IS ("debug")) { + kpse->debug |= atoi (optarg); + + } else if (ARGUMENT_IS ("dpi") || ARGUMENT_IS ("D")) { + dpi = atoi (optarg); + + } else if (ARGUMENT_IS ("engine")) { + engine = optarg; + + } else if (ARGUMENT_IS ("expand-braces")) { + braces_to_expand = optarg; + + } else if (ARGUMENT_IS ("expand-path")) { + path_to_expand = optarg; + + } else if (ARGUMENT_IS ("expand-var")) { + var_to_expand = optarg; + + } else if (ARGUMENT_IS ("format")) { + ENSURE_NONEMPTY_STRING (optarg); + user_format_string = optarg; + + } else if (ARGUMENT_IS ("help")) { + help_message (kpse, argv); + + } else if (ARGUMENT_IS ("help-formats")) { + help_formats (kpse, argv); + + } else if (ARGUMENT_IS ("mktex")) { + kpathsea_maketex_option (kpse, optarg, true); + must_exist = 1; /* otherwise it never gets called */ + + } else if (ARGUMENT_IS ("mode")) { + mode = optarg; + + } else if (ARGUMENT_IS ("no-casefold-search")) { + xputenv ("texmf_casefold_search", "0"); + + } else if (ARGUMENT_IS ("no-mktex")) { + kpathsea_maketex_option (kpse, optarg, false); + must_exist = 0; + + } else if (ARGUMENT_IS ("path")) { + user_path = optarg; + + } else if (ARGUMENT_IS ("progname")) { + progname = optarg; + + } else if (ARGUMENT_IS ("safe-in-name")) { + ENSURE_NONEMPTY_STRING (optarg); + safe_in_name = optarg; + + } else if (ARGUMENT_IS ("safe-out-name")) { + ENSURE_NONEMPTY_STRING (optarg); + safe_out_name = optarg; + + } else if (ARGUMENT_IS ("show-path")) { + path_to_show = optarg; + user_format_string = optarg; + + } else if (ARGUMENT_IS ("subdir")) { + str_list_add (&subdir_paths, optarg); + + } else if (ARGUMENT_IS ("var-brace-value")) { + ENSURE_NONEMPTY_STRING (optarg); + var_to_brace_value = optarg; + + } else if (ARGUMENT_IS ("var-value")) { + ENSURE_NONEMPTY_STRING (optarg); + var_to_value = optarg; + + } else if (ARGUMENT_IS ("version")) { + puts (kpathsea_version_string); + puts ("Copyright 2021 Karl Berry & Olaf Weber.\n\ +License LGPLv2.1+: GNU Lesser GPL version 2.1 or later <https://gnu.org/licenses/lgpl.html>\n\ +This is free software: you are free to change and redistribute it.\n\ +There is NO WARRANTY, to the extent permitted by law.\n"); + exit (0); + } + + /* Else it was just a flag; getopt has already done the assignment. */ + } + + if (user_path && user_format_string) { + fprintf (stderr, + "kpsewhich: -path (%s) and -format (%s) are mutually exclusive.\n", + user_path, user_format_string); + fputs ("Try `kpsewhich --help' for more information.\n", stderr); + exit (EXIT_FAILURE); + } + + if (optind == argc + && !var_to_expand && !braces_to_expand && !path_to_expand + && !path_to_show && !var_to_value && !var_to_brace_value + && !safe_in_name && !safe_out_name) { + fputs ("Missing argument. Try `kpsewhich --help' for more information.\n", + stderr); + exit (EXIT_FAILURE); + } +} + + + +/* Initializations that may depend on the options. */ + +static void +init_more (kpathsea kpse) +{ + if (engine) + kpathsea_xputenv (kpse, "engine", engine); + + /* We want config lines from the command line to override config files. */ + if (user_cnf_lines) { + unsigned i; + for (i = 0; i < user_cnf_nlines; i++) { + kpathsea_cnf_line_env_progname (kpse, user_cnf_lines[i]); + free (user_cnf_lines[i]); + } + } + + /* Disable all mktex programs unless they were explicitly enabled on our + command line. */ +#define DISABLE_MKTEX(fmt) \ +kpathsea_set_program_enabled (kpse, fmt, false, kpse_src_cmdline - 1) + DISABLE_MKTEX (kpse_pk_format); + DISABLE_MKTEX (kpse_mf_format); + DISABLE_MKTEX (kpse_tex_format); + DISABLE_MKTEX (kpse_tfm_format); + DISABLE_MKTEX (kpse_fmt_format); + DISABLE_MKTEX (kpse_ofm_format); + DISABLE_MKTEX (kpse_ocp_format); + + /* NULL for no fallback font. */ + kpathsea_init_prog (kpse, uppercasify (kpse->program_name), dpi, mode, NULL); + + /* Have to do this after setting the program name. */ + if (user_format_string) { + user_format = find_format (kpse, user_format_string, false); + if (user_format == kpse_last_format) { + WARNING1 ("kpsewhich: Ignoring unknown file type `%s'", + user_format_string); + } + } +} + + + +int +main (int argc, string *argv) +{ +#ifdef WIN32 +#define puts(s) kpathsea_win32_puts(kpse, (s)) + string *av, enc; + int ac; +#endif /* WIN32 */ + unsigned unfound = 0; + kpathsea kpse = kpathsea_new (); + + /* Read options, then dependent initializations. */ + read_command_line (kpse, argc, argv); + + kpathsea_set_program_name (kpse, argv[0], progname); +#ifdef WIN32 + if(strstr(kpse->program_name,"xetex") || strstr(kpse->program_name,"xelatex") + || strstr(kpse->program_name,"uptex") || strstr(kpse->program_name,"uplatex") + || strstr(kpse->program_name,"pdftex") || strstr(kpse->program_name,"pdflatex") + || strstr(kpse->program_name,"dvipdfm") || strstr(kpse->program_name,"extractbb") + || strstr(kpse->program_name,"xbb") || strstr(kpse->program_name,"ebb") + || strstr(kpse->program_name,"dvips") || strstr(kpse->program_name,"upmendex")) + { + if (strstr(kpse->program_name,"upmendex")) + enc = "utf-8"; + else + enc = kpathsea_var_value (kpse, "command_line_encoding"); + if (kpathsea_get_command_line_args_utf8(kpse, enc, &ac, &av)) { + optind = 0; + read_command_line (kpse, ac, av); + argv = av; + argc = ac; + } + } +#endif /* WIN32 */ + init_more (kpse); + + + /* Perform actions. */ + + /* Variable expansion. */ + if (var_to_expand) + puts (kpathsea_var_expand (kpse, var_to_expand)); + + /* Brace expansion. */ + if (braces_to_expand) + puts (kpathsea_brace_expand (kpse, braces_to_expand)); + + /* Path expansion. */ + if (path_to_expand) + puts (kpathsea_path_expand (kpse, path_to_expand)); + + /* Show a search path. */ + if (path_to_show) { + if (user_format != kpse_last_format) { + if (!kpse->format_info[user_format].type) { + /* needed if arg was numeric */ + kpathsea_init_format (kpse, user_format); + } + puts (kpse->format_info[user_format].path); + } else { + WARNING1 ("kpsewhich: Unknown file type, cannot show path: %s", + path_to_show); + } + } + + /* Var to value. */ + if (var_to_value) { + const_string value = kpathsea_var_value (kpse, var_to_value); + if (!value) { + unfound++; + value = ""; + } + puts (value); + } + + /* Var to brace-expanded value. This is separate from --var-value for + compatibility; people use --var-value for non-path values, where + changing commas to colons, which brace expansion does, is not right. */ + if (var_to_brace_value) { + const_string value = kpathsea_var_value (kpse, var_to_brace_value); + if (!value) { + unfound++; + value = ""; + } else { + /* Sometimes users want the fully-expanded (as a string, no + filesystem checks) value. We can't call brace_expand as part of + kpathsea_var_value, though, because unfortunately it is not + reentrant. We use var_value in lots of places in the source, + and it clobbers the static buffer in the kpse structure. */ + value = kpathsea_brace_expand (kpse, value); + } + puts (value); + } + + if (safe_in_name) { + if (!kpathsea_in_name_ok_silent (kpse, safe_in_name)) + unfound++; + } + + if (safe_out_name) { + if (!kpathsea_out_name_ok_silent (kpse, safe_out_name)) + unfound++; + } + + /* --subdir must imply --all, since we filter here after doing the + search, rather than inside the search itself. */ + if (!STR_LIST_EMPTY (subdir_paths)) { + show_all = 1; + } + + /* Usual case: look up each given filename. */ + for (; optind < argc; optind++) { + unfound += lookup (kpse, argv[optind]); + } + + if (interactive) { + for (;;) { + string name = read_line (stdin); + if (!name || STREQ (name, "q") || STREQ (name, "quit")) + break; + unfound += lookup (kpse, name); + free (name); + } + } + + kpathsea_finish (kpse); + return unfound > 255 ? 1 : unfound; +} |