/* kpsewhich -- standalone path lookup and variable expansion for Kpathsea.
Ideas from Thomas Esser, Pierre MacKay, and many others.
Copyright 1995-2023 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 . */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 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 substring in `.', 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 2023 Karl Berry & Olaf Weber.\n\
License LGPLv2.1+: GNU Lesser GPL version 2.1 or later \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;
}