summaryrefslogtreecommitdiff
path: root/Build/source/texk/kpathsea/kpsewhich.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/kpathsea/kpsewhich.c')
-rw-r--r--Build/source/texk/kpathsea/kpsewhich.c941
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;
+}