dvitomp fix from Akira
[mplib] / src / texk / kpathsea / kpsewhich.c
1 /* kpsewhich -- standalone path lookup and variable expansion for Kpathsea.
2    Ideas from Thomas Esser, Pierre MacKay, and many others.
3
4    Copyright 1995-2008 Karl Berry & Olaf Weber.
5
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.
10
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.
15
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/>.  */
18
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>
32
33
34 /* Base resolution. (-D, -dpi) */
35 unsigned dpi = 600;
36
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;
44
45 /* Only match files in given subdirs.  (-subdir) */
46 str_list_type subdir_paths;
47
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;
51 string user_path;
52
53 /* Interactively ask for names to look up?  (-interactive) */
54 boolean interactive = false;
55
56 /* Search the disk as well as ls-R?  (-must-exist) */
57 boolean must_exist = false;
58
59 /* Return all matches, not just the first one?  (-all) */
60 boolean show_all = false;
61
62 /* The device name, for $MAKETEX_MODE.  (-mode) */
63 string mode = NULL;
64
65 /* The program name, for `.PROG' construct in texmf.cnf.  (-program) */
66 string progname = NULL;
67
68 /* The engine name, for '$engine' construct in texmf.cnf.  (-engine) */
69 string engine = NULL;
70 \f
71 /* Return the <number> substring in `<name>.<number><stuff>', if S has
72    that form.  If it doesn't, return 0.  */
73
74 static unsigned
75 find_dpi P1C(string, s)
76 {
77   unsigned dpi_number = 0;
78   string extension = find_suffix (s);
79   
80   if (extension != NULL)
81     sscanf (extension, "%u", &dpi_number);
82
83   return dpi_number;
84 }
85 \f
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.
90
91    Note that a few filenames have been hard-coded for format types that
92    differ from what would be inferred from their extensions. */
93
94 static kpse_file_format_type
95 find_format P2C(string, name, boolean, is_filename)
96 {
97   kpse_file_format_type ret;
98   
99   if (is_filename && user_format != kpse_last_format) {
100     ret = user_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;
119   } else {
120     int f;  /* kpse_file_format_type */
121     unsigned name_len = strlen (name);
122
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))
129
130     f = 0;
131     while (f != kpse_last_format) {
132       unsigned try_len;
133       const_string *ext;
134       const_string ftry;
135       boolean found = false;
136       
137       if (!kpse_format_info[f].type)
138         kpse_init_format ((kpse_file_format_type) f);
139
140       if (!is_filename) {
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);
145       }
146       for (ext = kpse_format_info[f].suffix; !found && ext && *ext; ext++){
147         found = TRY_SUFFIX (*ext);
148       }      
149       for (ext = kpse_format_info[f].alt_suffix; !found && ext && *ext; ext++){
150         found = TRY_SUFFIX (*ext);
151       }
152
153       if (found)
154         break;
155
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.
162        */
163       if (f == kpse_fmt_format) {
164         f = kpse_last_format;
165       } else if (++f == kpse_fmt_format) {
166         f++;
167       } else if (f == kpse_last_format) {
168         f = kpse_fmt_format;
169       }
170     }
171
172     /* If there was a match, f will be one past the correct value.  */
173     ret = f;
174   }
175   
176   return ret;
177 }
178
179
180 \f
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.
186    
187    We don't reallocate the actual strings, just the list elements.
188    Perhaps later we will implement wildcards or // or something.  */
189
190 static string *
191 subdir_match P2C(str_list_type, subdirs,  string *, matches)
192 {
193   string *ret = XTALLOC1 (string);
194   unsigned len = 1;
195   unsigned m;
196   
197   for (m = 0; matches[m]; m++) {
198     unsigned loc;
199     unsigned e;
200     string s = xstrdup (matches[m]);
201     for (loc = strlen (s); loc > 0 && !IS_DIR_SEP (s[loc-1]); loc--)
202       ;
203     while (loc > 0 && IS_DIR_SEP (s[loc-1])) {
204       loc--;
205     }
206     s[loc] = 0;  /* wipe out basename */
207     
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])) {
212         subdir_len--;
213         subdir[subdir_len] = 0; /* remove trailing slashes from subdir spec */
214       }
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];
219         len++;
220       }
221     }
222     free (s);
223   }
224   ret[len-1] = NULL;
225   return ret;
226 }
227
228
229 \f
230 /* Look up a single filename NAME.  Return 0 if success, 1 if failure.  */
231
232 static unsigned
233 lookup P1C(string, name)
234 {
235   int i;
236   string ret = NULL;
237   string *ret_list = NULL;
238   
239   if (user_path) {
240     if (show_all) {
241       ret_list = kpse_all_path_search (user_path, name);
242     } else {
243        ret = kpse_path_search (user_path, name, must_exist);
244     }
245     
246   } else {
247     /* No user-specified search path, check user format or guess from NAME.  */
248     kpse_file_format_type fmt = find_format (name, true);
249
250     switch (fmt) {
251       case kpse_pk_format:
252       case kpse_gf_format:
253       case kpse_any_glyph_format:
254         {
255           kpse_glyph_file_type glyph_ret;
256           /* Try to extract the resolution from the name.  */
257           unsigned local_dpi = find_dpi (name);
258           if (!local_dpi)
259             local_dpi = dpi;
260           ret = kpse_find_glyph (remove_suffix (name), local_dpi, fmt,
261                                  &glyph_ret);
262         }
263         break;
264
265       case kpse_last_format:
266         /* If the suffix isn't recognized, assume it's a tex file. */
267         fmt = kpse_tex_format;
268         /* fall through */
269
270       default:
271         if (show_all) {
272           ret_list = kpse_find_file_generic (name, fmt, must_exist, true);
273         } else {
274           ret = kpse_find_file (name, fmt, must_exist);
275         }
276     }
277   }
278   
279   /* Turn single return into a null-terminated list for uniform treatment.  */
280   if (ret) {
281     ret_list = XTALLOC (2, string);
282     ret_list[0] = ret;
283     ret_list[1] = NULL;
284   }
285
286   /* Filter by subdirectories, if specified.  */
287   if (STR_LIST_LENGTH (subdir_paths) > 0) {
288     string *new_list = subdir_match (subdir_paths, ret_list);
289     free (ret_list);
290     ret_list = new_list;
291   }
292
293   /* Print output.  */
294   if (ret_list) {
295     for (i = 0; ret_list[i]; i++)
296       puts (ret_list[i]);
297     /* Save whether we found anything */
298     ret = ret_list[0];
299     free (ret_list);
300   }
301   
302   return ret == NULL;
303 }
304 \f
305 /* Reading the options.  */
306
307 #define USAGE "\n\
308 Standalone path lookup and expansion for Kpathsea.\n\
309 \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 \
329 "
330
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)
335
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 },
341       { "dpi",                  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 },
347       { "help",                 0, 0, 0 },
348       { "interactive",          0, (int *) &interactive, 1 },
349       { "mktex",                1, 0, 0 },
350       { "mode",                 1, 0, 0 },
351       { "must-exist",           0, (int *) &must_exist, 1 },
352       { "path",                 1, 0, 0 },
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 },
360       { 0, 0, 0, 0 } };
361
362 static void
363 read_command_line P2C(int, argc,  string *, argv)
364 {
365   int g;   /* `getopt' return code.  */
366   int option_index;
367
368   for (;;) {
369     g = getopt_long_only (argc, argv, "", long_options, &option_index);
370
371     if (g == -1)
372       break;
373
374     if (g == '?')
375       exit (1);  /* Unknown option.  */
376
377     assert (g == 0); /* We have no short option names.  */
378
379     if (ARGUMENT_IS ("debug")) {
380       kpathsea_debug |= atoi (optarg);
381
382     } else if (ARGUMENT_IS ("dpi") || ARGUMENT_IS ("D")) {
383       dpi = atoi (optarg);
384
385     } else if (ARGUMENT_IS ("engine")) {
386       engine = optarg;
387       
388     } else if (ARGUMENT_IS ("expand-braces")) {
389       braces_to_expand = optarg;
390       
391     } else if (ARGUMENT_IS ("expand-path")) {
392       path_to_expand = optarg;
393
394     } else if (ARGUMENT_IS ("expand-var")) {
395       var_to_expand = optarg;
396
397     } else if (ARGUMENT_IS ("format")) {
398       user_format_string = optarg;
399
400     } else if (ARGUMENT_IS ("help")) {
401       int f; /* kpse_file_format_type */
402       extern KPSEDLL char *kpse_bug_address; /* from version.c */
403       
404       printf ("Usage: %s [OPTION]... [FILENAME]...\n", argv[0]);
405       fputs (USAGE, stdout);
406       putchar ('\n');
407       fputs (kpse_bug_address, stdout);
408
409       /* Have to set this for init_format to work.  */
410       kpse_set_program_name (argv[0], progname);
411
412       puts ("\nRecognized format names and their suffixes:");
413       for (f = 0; f < kpse_last_format; f++) {
414         const_string *ext;
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++) {
418           putchar (' ');
419           fputs (*ext, stdout);
420         }
421         if (kpse_format_info[f].alt_suffix) {
422           /* leave extra space between default and alt suffixes */
423           putchar (' ');
424         }
425         for (ext = kpse_format_info[f].alt_suffix; ext && *ext; ext++) {
426           putchar (' ');
427           fputs (*ext, stdout);
428         }
429         putchar ('\n');
430       }
431
432       exit (0);
433
434     } else if (ARGUMENT_IS ("mktex")) {
435       kpse_maketex_option (optarg, true);
436
437     } else if (ARGUMENT_IS ("mode")) {
438       mode = optarg;
439
440     } else if (ARGUMENT_IS ("no-mktex")) {
441       kpse_maketex_option (optarg, false);
442
443     } else if (ARGUMENT_IS ("path")) {
444       user_path = optarg;
445
446     } else if (ARGUMENT_IS ("progname")) {
447       progname = optarg;
448
449     } else if (ARGUMENT_IS ("show-path")) {
450       path_to_show = optarg;
451       user_format_string = optarg;
452
453     } else if (ARGUMENT_IS ("subdir")) {
454       str_list_add (&subdir_paths, optarg);
455
456     } else if (ARGUMENT_IS ("var-value")) {
457       var_to_value = optarg;
458
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");
466       exit (0);
467     }
468
469     /* Else it was just a flag; getopt has already done the assignment.  */
470   }
471   
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);
476     exit (1);
477   }
478
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",
482            stderr);
483     exit (1);
484   }
485 }
486 \f
487 int
488 main P2C(int, argc,  string *, argv)
489 {
490   unsigned unfound = 0;
491
492   read_command_line (argc, argv);
493
494   kpse_set_program_name (argv[0], progname);
495
496   if (engine)
497     xputenv ("engine", engine);
498   
499   /* NULL for no fallback font.  */
500   kpse_init_prog (uppercasify (kpse_program_name), dpi, mode, NULL);
501   
502   /* Have to do this after setting the program name.  */
503   if (user_format_string)
504     user_format = find_format (user_format_string, false);
505   
506   /* Variable expansion.  */
507   if (var_to_expand)
508     puts (kpse_var_expand (var_to_expand));
509
510   /* Brace expansion. */
511   if (braces_to_expand)
512     puts (kpse_brace_expand (braces_to_expand));
513   
514   /* Path expansion. */
515   if (path_to_expand)
516     puts (kpse_path_expand (path_to_expand));
517
518   /* Show a search path. */
519   if (path_to_show) {
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);
524     } else {
525       WARNING1 ("kpsewhich: Ignoring unknown file type `%s'", path_to_show);
526     }
527   }
528
529   /* Var to value. */
530   if (var_to_value) {
531     const_string value = kpse_var_value (var_to_value);
532     if (!value) {
533       unfound++;
534       value="";
535     }
536     puts (value);
537   }
538   
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) {
542     show_all = 1;
543   }
544   
545   for (; optind < argc; optind++) {
546     unfound += lookup (argv[optind]);
547   }
548
549   if (interactive) {
550     for (;;) {
551       string name = read_line (stdin);
552       if (!name || STREQ (name, "q") || STREQ (name, "quit")) break;
553       unfound += lookup (name);
554       free (name);
555     }
556   }
557   
558   return unfound > 255 ? 1 : unfound;
559 }