1 /* kpsewhich -- standalone path lookup and variable expansion for Kpathsea.
2 Ideas from Thomas Esser, Pierre MacKay, and many others.
4 Copyright (C) 1995-2008 Karl Berry & Olaf Weber.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with this library; if not, see <http://www.gnu.org/licenses/>. */
19 #include <kpathsea/config.h>
20 #include <kpathsea/c-ctype.h>
21 #include <kpathsea/c-pathch.h>
22 #include <kpathsea/expand.h>
23 #include <kpathsea/getopt.h>
24 #include <kpathsea/line.h>
25 #include <kpathsea/pathsearch.h>
26 #include <kpathsea/proginit.h>
27 #include <kpathsea/str-list.h>
28 #include <kpathsea/tex-file.h>
29 #include <kpathsea/tex-glyph.h>
30 #include <kpathsea/variable.h>
31 #include <kpathsea/progname.h>
34 /* Base resolution. (-D, -dpi) */
37 /* For variable and path expansion. (-expand-var, -expand-path,
38 -show-path, -separator) */
39 string var_to_expand = NULL;
40 string braces_to_expand = NULL;
41 string path_to_expand = NULL;
42 string path_to_show = NULL;
43 string var_to_value = NULL;
45 /* Only match files in given subdirs. (-subdir) */
46 str_list_type subdir_paths;
48 /* The file type and path for lookups. (-format, -path) */
49 kpse_file_format_type user_format = kpse_last_format;
50 string user_format_string;
53 /* Interactively ask for names to look up? (-interactive) */
54 boolean interactive = false;
56 /* Search the disk as well as ls-R? (-must-exist) */
57 boolean must_exist = false;
59 /* Return all matches, not just the first one? (-all) */
60 boolean show_all = false;
62 /* The device name, for $MAKETEX_MODE. (-mode) */
65 /* The program name, for `.PROG' construct in texmf.cnf. (-program) */
66 string progname = NULL;
68 /* The engine name, for '$engine' construct in texmf.cnf. (-engine) */
71 /* Return the <number> substring in `<name>.<number><stuff>', if S has
72 that form. If it doesn't, return 0. */
75 find_dpi P1C(string, s)
77 unsigned dpi_number = 0;
78 string extension = find_suffix (s);
80 if (extension != NULL)
81 sscanf (extension, "%u", &dpi_number);
86 /* Use the file type from -format if that was specified, else guess
87 dynamically from NAME. Return kpse_last_format if undeterminable.
88 This function is also used to parse the -format string, a case which
89 we distinguish by setting is_filename to false.
91 Note that a few filenames have been hard-coded for format types that
92 differ from what would be inferred from their extensions. */
94 static kpse_file_format_type
95 find_format P2C(string, name, boolean, is_filename)
97 kpse_file_format_type ret;
99 if (is_filename && user_format != kpse_last_format) {
101 } else if (FILESTRCASEEQ (name, "pdftex.cfg")) {
102 ret = kpse_pdftex_config_format;
103 } else if (FILESTRCASEEQ (name, "config.ps")) {
104 ret = kpse_dvips_config_format;
106 int f; /* kpse_file_format_type */
107 unsigned name_len = strlen (name);
109 /* Have to rely on `try_len' being declared here, since we can't assume
110 GNU C and statement expressions. */
111 #define TRY_SUFFIX(ftry) (\
112 try_len = (ftry) ? strlen (ftry) : 0, \
113 (ftry) && try_len <= name_len \
114 && FILESTRCASEEQ (ftry, name + name_len - try_len))
117 while (f != kpse_last_format) {
121 boolean found = false;
123 if (!kpse_format_info[f].type)
124 kpse_init_format ((kpse_file_format_type)f);
127 /* Allow the long name, but only in the -format option. We don't
128 want a filename confused with a format name. */
129 ftry = kpse_format_info[f].type;
130 found = TRY_SUFFIX (ftry);
132 for (ext = kpse_format_info[f].suffix; !found && ext && *ext; ext++){
133 found = TRY_SUFFIX (*ext);
135 for (ext = kpse_format_info[f].alt_suffix; !found && ext && *ext; ext++){
136 found = TRY_SUFFIX (*ext);
142 /* Some trickery here: the extensions for kpse_fmt_format can
143 * clash with other extensions in use, and we prefer for those
144 * others to be preferred. And we don't want to change the
145 * integer value of kpse_fmt_format. So skip it when first
146 * enountered, then use it when we've done everything else,
147 * and use it as the end-guard.
149 if (f == kpse_fmt_format) {
150 f = kpse_last_format;
151 } else if (++f == kpse_fmt_format) {
153 } else if (f == kpse_last_format) {
158 /* If there was a match, f will be one past the correct value. */
167 /* Return newly-allocated NULL-terminated list of strings from MATCHES
168 that are prefixed with any of the subdirectories in SUBDIRS. That
169 is, for a string S in MATCHES, its dirname must end with one of the
170 elements in SUBDIRS. For instance, if subdir=foo/bar, that will
171 match a string foo/bar/baz or /some/texmf/foo/bar/baz.
173 We don't reallocate the actual strings, just the list elements.
174 Perhaps later we will implement wildcards or // or something. */
177 subdir_match P2C(str_list_type, subdirs, string *, matches)
179 string *ret = XTALLOC1 (string);
183 for (m = 0; matches[m]; m++) {
186 string s = xstrdup (matches[m]);
187 for (loc = strlen (s); loc > 0 && !IS_DIR_SEP (s[loc-1]); loc--)
189 while (loc > 0 && IS_DIR_SEP (s[loc-1])) {
192 s[loc] = 0; /* wipe out basename */
194 for (e = 0; e < STR_LIST_LENGTH (subdirs); e++) {
195 string subdir = STR_LIST_ELT (subdirs, e);
196 unsigned subdir_len = strlen (subdir);
197 while (subdir_len > 0 && IS_DIR_SEP (subdir[subdir_len-1])) {
199 subdir[subdir_len] = 0; /* remove trailing slashes from subdir spec */
201 if (FILESTRCASEEQ (subdir, s + loc - subdir_len)) {
202 /* matched, save this one. */
203 XRETALLOC (ret, len + 1, string);
204 ret[len-1] = matches[m];
216 /* Look up a single filename NAME. Return 0 if success, 1 if failure. */
219 lookup P1C(string, name)
223 string *ret_list = NULL;
227 ret_list = kpse_all_path_search (user_path, name);
229 ret = kpse_path_search (user_path, name, must_exist);
233 /* No user-specified search path, check user format or guess from NAME. */
234 kpse_file_format_type fmt = find_format (name, true);
239 case kpse_any_glyph_format:
241 kpse_glyph_file_type glyph_ret;
242 /* Try to extract the resolution from the name. */
243 unsigned local_dpi = find_dpi (name);
246 ret = kpse_find_glyph (remove_suffix (name), local_dpi, fmt,
251 case kpse_last_format:
252 /* If the suffix isn't recognized, assume it's a tex file. */
253 fmt = kpse_tex_format;
258 ret_list = kpse_find_file_generic (name, fmt, must_exist, true);
260 ret = kpse_find_file (name, fmt, must_exist);
265 /* Turn single return into a null-terminated list for uniform treatment. */
267 ret_list = XTALLOC (2, string);
272 /* Filter by subdirectories, if specified. */
273 if (STR_LIST_LENGTH (subdir_paths) > 0) {
274 string *new_list = subdir_match (subdir_paths, ret_list);
281 for (i = 0; ret_list[i]; i++)
283 /* Save whether we found anything */
291 /* Reading the options. */
294 Standalone path lookup and expansion for Kpathsea.\n\
296 -all output all matches (one per line), not just the first.\n\
297 -debug=NUM set debugging flags.\n\
298 -D, -dpi=NUM use a base resolution of NUM; default 600.\n\
299 -engine=STRING set engine name to STRING.\n\
300 -expand-braces=STRING output variable and brace expansion of STRING.\n\
301 -expand-path=STRING output complete path expansion of STRING.\n\
302 -expand-var=STRING output variable expansion of STRING.\n\
303 -format=NAME use file type NAME (see list below).\n\
304 -help print this message and exit.\n\
305 -interactive ask for additional filenames to look up.\n\
306 [-no]-mktex=FMT disable/enable mktexFMT generation (FMT=pk/mf/tex/tfm).\n\
307 -mode=STRING set device name for $MAKETEX_MODE to STRING; no default.\n\
308 -must-exist search the disk as well as ls-R if necessary.\n\
309 -path=STRING search in the path STRING.\n\
310 -progname=STRING set program name to STRING.\n\
311 -show-path=NAME output search path for file type NAME (see list below).\n\
312 -subdir=STRING only output matches whose directory part ends with STRING.\n\
313 -var-value=STRING output the value of variable $STRING.\n\
314 -version print version number and exit.\n \
317 /* Test whether getopt found an option ``A''.
318 Assumes the option index is in the variable `option_index', and the
319 option table in a variable `long_options'. */
320 #define ARGUMENT_IS(a) STREQ (long_options[option_index].name, a)
322 /* SunOS cc can't initialize automatic structs. */
323 static struct option long_options[]
324 = { { "D", 1, 0, 0 },
325 { "all", 0, (int *) &show_all, 1 },
326 { "debug", 1, 0, 0 },
328 { "engine", 1, 0, 0 },
329 { "expand-braces", 1, 0, 0 },
330 { "expand-path", 1, 0, 0 },
331 { "expand-var", 1, 0, 0 },
332 { "format", 1, 0, 0 },
334 { "interactive", 0, (int *) &interactive, 1 },
335 { "mktex", 1, 0, 0 },
337 { "must-exist", 0, (int *) &must_exist, 1 },
339 { "no-mktex", 1, 0, 0 },
340 { "progname", 1, 0, 0 },
341 { "separator", 1, 0, 0 },
342 { "subdir", 1, 0, 0 },
343 { "show-path", 1, 0, 0 },
344 { "var-value", 1, 0, 0 },
345 { "version", 0, 0, 0 },
349 read_command_line P2C(int, argc, string *, argv)
351 int g; /* `getopt' return code. */
355 g = getopt_long_only (argc, argv, "", long_options, &option_index);
361 exit (1); /* Unknown option. */
363 assert (g == 0); /* We have no short option names. */
365 if (ARGUMENT_IS ("debug")) {
366 kpathsea_debug |= atoi (optarg);
368 } else if (ARGUMENT_IS ("dpi") || ARGUMENT_IS ("D")) {
371 } else if (ARGUMENT_IS ("engine")) {
374 } else if (ARGUMENT_IS ("expand-braces")) {
375 braces_to_expand = optarg;
377 } else if (ARGUMENT_IS ("expand-path")) {
378 path_to_expand = optarg;
380 } else if (ARGUMENT_IS ("expand-var")) {
381 var_to_expand = optarg;
383 } else if (ARGUMENT_IS ("format")) {
384 user_format_string = optarg;
386 } else if (ARGUMENT_IS ("help")) {
387 int f; /* kpse_file_format_type */
388 extern KPSEDLL char *kpse_bug_address; /* from version.c */
390 printf ("Usage: %s [OPTION]... [FILENAME]...\n", argv[0]);
391 fputs (USAGE, stdout);
393 fputs (kpse_bug_address, stdout);
395 /* Have to set this for init_format to work. */
396 kpse_set_program_name (argv[0], progname);
398 puts ("\nRecognized format names and their suffixes:");
399 for (f = 0; f < kpse_last_format; f++) {
401 kpse_init_format ((kpse_file_format_type)f);
402 printf ("%s:", kpse_format_info[f].type);
403 for (ext = kpse_format_info[f].suffix; ext && *ext; ext++) {
405 fputs (*ext, stdout);
407 for (ext = kpse_format_info[f].alt_suffix; ext && *ext; ext++) {
409 fputs (*ext, stdout);
416 } else if (ARGUMENT_IS ("mktex")) {
417 kpse_maketex_option (optarg, true);
419 } else if (ARGUMENT_IS ("mode")) {
422 } else if (ARGUMENT_IS ("no-mktex")) {
423 kpse_maketex_option (optarg, false);
425 } else if (ARGUMENT_IS ("path")) {
428 } else if (ARGUMENT_IS ("progname")) {
431 } else if (ARGUMENT_IS ("show-path")) {
432 path_to_show = optarg;
433 user_format_string = optarg;
435 } else if (ARGUMENT_IS ("subdir")) {
436 str_list_add (&subdir_paths, optarg);
438 } else if (ARGUMENT_IS ("var-value")) {
439 var_to_value = optarg;
441 } else if (ARGUMENT_IS ("version")) {
442 extern KPSEDLL char *kpathsea_version_string; /* from version.c */
443 puts (kpathsea_version_string);
444 puts ("Copyright 2008 Karl Berry & Olaf Weber.\n\
445 License LGPLv2.1+: GNU Lesser GPL version 2.1 or later <http://gnu.org/licenses/lgpl.html>\n\
446 This is free software: you are free to change and redistribute it.\n\
447 There is NO WARRANTY, to the extent permitted by law.\n");
451 /* Else it was just a flag; getopt has already done the assignment. */
454 if (user_path && user_format_string) {
455 fprintf (stderr, "-path (%s) and -format (%s) are mutually exclusive.\n",
456 user_path, user_format_string);
457 fputs ("Try `kpsewhich --help' for more information.\n", stderr);
461 if (optind == argc && !var_to_expand && !braces_to_expand && !path_to_expand
462 && !path_to_show && !var_to_value) {
463 fputs ("Missing argument. Try `kpsewhich --help' for more information.\n",
470 main P2C(int, argc, string *, argv)
472 unsigned unfound = 0;
474 read_command_line (argc, argv);
476 kpse_set_program_name (argv[0], progname);
479 xputenv("engine", engine);
481 /* NULL for no fallback font. */
482 kpse_init_prog (uppercasify (kpse_program_name), dpi, mode, NULL);
484 /* Have to do this after setting the program name. */
485 if (user_format_string)
486 user_format = find_format (user_format_string, false);
488 /* Variable expansion. */
490 puts (kpse_var_expand (var_to_expand));
492 /* Brace expansion. */
493 if (braces_to_expand)
494 puts (kpse_brace_expand (braces_to_expand));
496 /* Path expansion. */
498 puts (kpse_path_expand (path_to_expand));
500 /* Show a search path. */
502 if (user_format != kpse_last_format) {
503 if (!kpse_format_info[user_format].type) /* needed if arg was numeric */
504 kpse_init_format (user_format);
505 puts (kpse_format_info[user_format].path);
507 WARNING1 ("kpsewhich: Ignoring unknown file type `%s'", path_to_show);
513 string value = kpse_var_value (var_to_value);
521 /* --subdir must imply --all, since we filter here after doing the
522 search, rather than inside the search itself. */
523 if (STR_LIST_LENGTH (subdir_paths) > 0) {
527 for (; optind < argc; optind++) {
528 unfound += lookup (argv[optind]);
533 string name = read_line (stdin);
534 if (!name || STREQ (name, "q") || STREQ (name, "quit")) break;
535 unfound += lookup (name);
540 return unfound > 255 ? 1 : unfound;