1 /* kpsewhich -- standalone path lookup and variable expansion for Kpathsea.
2 Ideas from Thomas Esser, Pierre MacKay, and many others.
4 Copyright 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, "config.ps")) {
102 ret = kpse_dvips_config_format;
103 } else if (FILESTRCASEEQ (name, "dvipdfmx.cfg")) {
104 ret = kpse_program_text_format;
105 } else if (FILESTRCASEEQ (name, "fmtutil.cnf")) {
106 ret = kpse_web2c_format;
107 } else if (FILESTRCASEEQ (name, "glyphlist.txt")) {
108 ret = kpse_fontmap_format;
109 } else if (FILESTRCASEEQ (name, "mktex.cnf")) {
110 ret = kpse_web2c_format;
111 } else if (FILESTRCASEEQ (name, "pdfglyphlist.txt")) {
112 ret = kpse_fontmap_format;
113 } else if (FILESTRCASEEQ (name, "pdftex.cfg")) {
114 ret = kpse_pdftex_config_format;
115 } else if (FILESTRCASEEQ (name, "texmf.cnf")) {
116 ret = kpse_cnf_format;
117 } else if (FILESTRCASEEQ (name, "updmap.cfg")) {
118 ret = kpse_web2c_format;
120 int f; /* kpse_file_format_type */
121 unsigned name_len = strlen (name);
123 /* Have to rely on `try_len' being declared here, since we can't assume
124 GNU C and statement expressions. */
125 #define TRY_SUFFIX(ftry) (\
126 try_len = (ftry) ? strlen (ftry) : 0, \
127 (ftry) && try_len <= name_len \
128 && FILESTRCASEEQ (ftry, name + name_len - try_len))
131 while (f != kpse_last_format) {
135 boolean found = false;
137 if (!kpse_format_info[f].type)
138 kpse_init_format ((kpse_file_format_type) f);
141 /* Allow the long name, but only in the -format option. We don't
142 want a filename confused with a format name. */
143 ftry = kpse_format_info[f].type;
144 found = TRY_SUFFIX (ftry);
146 for (ext = kpse_format_info[f].suffix; !found && ext && *ext; ext++){
147 found = TRY_SUFFIX (*ext);
149 for (ext = kpse_format_info[f].alt_suffix; !found && ext && *ext; ext++){
150 found = TRY_SUFFIX (*ext);
156 /* Some trickery here: the extensions for kpse_fmt_format can
157 * clash with other extensions in use, and we prefer for those
158 * others to be preferred. And we don't want to change the
159 * integer value of kpse_fmt_format. So skip it when first
160 * enountered, then use it when we've done everything else,
161 * and use it as the end-guard.
163 if (f == kpse_fmt_format) {
164 f = kpse_last_format;
165 } else if (++f == kpse_fmt_format) {
167 } else if (f == kpse_last_format) {
172 /* If there was a match, f will be one past the correct value. */
181 /* Return newly-allocated NULL-terminated list of strings from MATCHES
182 that are prefixed with any of the subdirectories in SUBDIRS. That
183 is, for a string S in MATCHES, its dirname must end with one of the
184 elements in SUBDIRS. For instance, if subdir=foo/bar, that will
185 match a string foo/bar/baz or /some/texmf/foo/bar/baz.
187 We don't reallocate the actual strings, just the list elements.
188 Perhaps later we will implement wildcards or // or something. */
191 subdir_match P2C(str_list_type, subdirs, string *, matches)
193 string *ret = XTALLOC1 (string);
197 for (m = 0; matches[m]; m++) {
200 string s = xstrdup (matches[m]);
201 for (loc = strlen (s); loc > 0 && !IS_DIR_SEP (s[loc-1]); loc--)
203 while (loc > 0 && IS_DIR_SEP (s[loc-1])) {
206 s[loc] = 0; /* wipe out basename */
208 for (e = 0; e < STR_LIST_LENGTH (subdirs); e++) {
209 string subdir = STR_LIST_ELT (subdirs, e);
210 unsigned subdir_len = strlen (subdir);
211 while (subdir_len > 0 && IS_DIR_SEP (subdir[subdir_len-1])) {
213 subdir[subdir_len] = 0; /* remove trailing slashes from subdir spec */
215 if (FILESTRCASEEQ (subdir, s + loc - subdir_len)) {
216 /* matched, save this one. */
217 XRETALLOC (ret, len + 1, string);
218 ret[len-1] = matches[m];
230 /* Look up a single filename NAME. Return 0 if success, 1 if failure. */
233 lookup P1C(string, name)
237 string *ret_list = NULL;
241 ret_list = kpse_all_path_search (user_path, name);
243 ret = kpse_path_search (user_path, name, must_exist);
247 /* No user-specified search path, check user format or guess from NAME. */
248 kpse_file_format_type fmt = find_format (name, true);
253 case kpse_any_glyph_format:
255 kpse_glyph_file_type glyph_ret;
256 /* Try to extract the resolution from the name. */
257 unsigned local_dpi = find_dpi (name);
260 ret = kpse_find_glyph (remove_suffix (name), local_dpi, fmt,
265 case kpse_last_format:
266 /* If the suffix isn't recognized, assume it's a tex file. */
267 fmt = kpse_tex_format;
272 ret_list = kpse_find_file_generic (name, fmt, must_exist, true);
274 ret = kpse_find_file (name, fmt, must_exist);
279 /* Turn single return into a null-terminated list for uniform treatment. */
281 ret_list = XTALLOC (2, string);
286 /* Filter by subdirectories, if specified. */
287 if (STR_LIST_LENGTH (subdir_paths) > 0) {
288 string *new_list = subdir_match (subdir_paths, ret_list);
295 for (i = 0; ret_list[i]; i++)
297 /* Save whether we found anything */
305 /* Reading the options. */
308 Standalone path lookup and expansion for Kpathsea.\n\
310 -all output all matches (one per line), not just the first.\n\
311 -debug=NUM set debugging flags.\n\
312 -D, -dpi=NUM use a base resolution of NUM; default 600.\n\
313 -engine=STRING set engine name to STRING.\n\
314 -expand-braces=STRING output variable and brace expansion of STRING.\n\
315 -expand-path=STRING output complete path expansion of STRING.\n\
316 -expand-var=STRING output variable expansion of STRING.\n\
317 -format=NAME use file type NAME (see list below).\n\
318 -help print this message and exit.\n\
319 -interactive ask for additional filenames to look up.\n\
320 [-no]-mktex=FMT disable/enable mktexFMT generation (FMT=pk/mf/tex/tfm).\n\
321 -mode=STRING set device name for $MAKETEX_MODE to STRING; no default.\n\
322 -must-exist search the disk as well as ls-R if necessary.\n\
323 -path=STRING search in the path STRING.\n\
324 -progname=STRING set program name to STRING.\n\
325 -show-path=NAME output search path for file type NAME (see list below).\n\
326 -subdir=STRING only output matches whose directory part ends with STRING.\n\
327 -var-value=STRING output the value of variable $STRING.\n\
328 -version print version number and exit.\n \
331 /* Test whether getopt found an option ``A''.
332 Assumes the option index is in the variable `option_index', and the
333 option table in a variable `long_options'. */
334 #define ARGUMENT_IS(a) STREQ (long_options[option_index].name, a)
336 /* SunOS cc can't initialize automatic structs. */
337 static struct option long_options[]
338 = { { "D", 1, 0, 0 },
339 { "all", 0, (int *) &show_all, 1 },
340 { "debug", 1, 0, 0 },
342 { "engine", 1, 0, 0 },
343 { "expand-braces", 1, 0, 0 },
344 { "expand-path", 1, 0, 0 },
345 { "expand-var", 1, 0, 0 },
346 { "format", 1, 0, 0 },
348 { "interactive", 0, (int *) &interactive, 1 },
349 { "mktex", 1, 0, 0 },
351 { "must-exist", 0, (int *) &must_exist, 1 },
353 { "no-mktex", 1, 0, 0 },
354 { "progname", 1, 0, 0 },
355 { "separator", 1, 0, 0 },
356 { "subdir", 1, 0, 0 },
357 { "show-path", 1, 0, 0 },
358 { "var-value", 1, 0, 0 },
359 { "version", 0, 0, 0 },
363 read_command_line P2C(int, argc, string *, argv)
365 int g; /* `getopt' return code. */
369 g = getopt_long_only (argc, argv, "", long_options, &option_index);
375 exit (1); /* Unknown option. */
377 assert (g == 0); /* We have no short option names. */
379 if (ARGUMENT_IS ("debug")) {
380 kpathsea_debug |= atoi (optarg);
382 } else if (ARGUMENT_IS ("dpi") || ARGUMENT_IS ("D")) {
385 } else if (ARGUMENT_IS ("engine")) {
388 } else if (ARGUMENT_IS ("expand-braces")) {
389 braces_to_expand = optarg;
391 } else if (ARGUMENT_IS ("expand-path")) {
392 path_to_expand = optarg;
394 } else if (ARGUMENT_IS ("expand-var")) {
395 var_to_expand = optarg;
397 } else if (ARGUMENT_IS ("format")) {
398 user_format_string = optarg;
400 } else if (ARGUMENT_IS ("help")) {
401 int f; /* kpse_file_format_type */
402 extern KPSEDLL char *kpse_bug_address; /* from version.c */
404 printf ("Usage: %s [OPTION]... [FILENAME]...\n", argv[0]);
405 fputs (USAGE, stdout);
407 fputs (kpse_bug_address, stdout);
409 /* Have to set this for init_format to work. */
410 kpse_set_program_name (argv[0], progname);
412 puts ("\nRecognized format names and their suffixes:");
413 for (f = 0; f < kpse_last_format; f++) {
415 kpse_init_format ((kpse_file_format_type)f);
416 printf ("%s:", kpse_format_info[f].type);
417 for (ext = kpse_format_info[f].suffix; ext && *ext; ext++) {
419 fputs (*ext, stdout);
421 if (kpse_format_info[f].alt_suffix) {
422 /* leave extra space between default and alt suffixes */
425 for (ext = kpse_format_info[f].alt_suffix; ext && *ext; ext++) {
427 fputs (*ext, stdout);
434 } else if (ARGUMENT_IS ("mktex")) {
435 kpse_maketex_option (optarg, true);
437 } else if (ARGUMENT_IS ("mode")) {
440 } else if (ARGUMENT_IS ("no-mktex")) {
441 kpse_maketex_option (optarg, false);
443 } else if (ARGUMENT_IS ("path")) {
446 } else if (ARGUMENT_IS ("progname")) {
449 } else if (ARGUMENT_IS ("show-path")) {
450 path_to_show = optarg;
451 user_format_string = optarg;
453 } else if (ARGUMENT_IS ("subdir")) {
454 str_list_add (&subdir_paths, optarg);
456 } else if (ARGUMENT_IS ("var-value")) {
457 var_to_value = optarg;
459 } else if (ARGUMENT_IS ("version")) {
460 extern KPSEDLL char *kpathsea_version_string; /* from version.c */
461 puts (kpathsea_version_string);
462 puts ("Copyright 2008 Karl Berry & Olaf Weber.\n\
463 License LGPLv2.1+: GNU Lesser GPL version 2.1 or later <http://gnu.org/licenses/lgpl.html>\n\
464 This is free software: you are free to change and redistribute it.\n\
465 There is NO WARRANTY, to the extent permitted by law.\n");
469 /* Else it was just a flag; getopt has already done the assignment. */
472 if (user_path && user_format_string) {
473 fprintf (stderr, "-path (%s) and -format (%s) are mutually exclusive.\n",
474 user_path, user_format_string);
475 fputs ("Try `kpsewhich --help' for more information.\n", stderr);
479 if (optind == argc && !var_to_expand && !braces_to_expand && !path_to_expand
480 && !path_to_show && !var_to_value) {
481 fputs ("Missing argument. Try `kpsewhich --help' for more information.\n",
488 main P2C(int, argc, string *, argv)
490 unsigned unfound = 0;
492 read_command_line (argc, argv);
494 kpse_set_program_name (argv[0], progname);
497 xputenv ("engine", engine);
499 /* NULL for no fallback font. */
500 kpse_init_prog (uppercasify (kpse_program_name), dpi, mode, NULL);
502 /* Have to do this after setting the program name. */
503 if (user_format_string)
504 user_format = find_format (user_format_string, false);
506 /* Variable expansion. */
508 puts (kpse_var_expand (var_to_expand));
510 /* Brace expansion. */
511 if (braces_to_expand)
512 puts (kpse_brace_expand (braces_to_expand));
514 /* Path expansion. */
516 puts (kpse_path_expand (path_to_expand));
518 /* Show a search path. */
520 if (user_format != kpse_last_format) {
521 if (!kpse_format_info[user_format].type) /* needed if arg was numeric */
522 kpse_init_format (user_format);
523 puts (kpse_format_info[user_format].path);
525 WARNING1 ("kpsewhich: Ignoring unknown file type `%s'", path_to_show);
531 const_string value = kpse_var_value (var_to_value);
539 /* --subdir must imply --all, since we filter here after doing the
540 search, rather than inside the search itself. */
541 if (STR_LIST_LENGTH (subdir_paths) > 0) {
545 for (; optind < argc; optind++) {
546 unfound += lookup (argv[optind]);
551 string name = read_line (stdin);
552 if (!name || STREQ (name, "q") || STREQ (name, "quit")) break;
553 unfound += lookup (name);
558 return unfound > 255 ? 1 : unfound;