1 /* kpsewhich -- standalone path lookup and variable expansion for Kpathsea.
2    Ideas from Thomas Esser, Pierre MacKay, and many others.
3 
4    Copyright 1995-2013 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/version.h>
32 
33 #ifdef WIN32
34 #undef fputs
35 #undef puts
36 #define fputs win32_fputs
37 #define puts  win32_puts
38 #endif
39 
40 /* For variable and path expansion.  (-expand-var, -expand-path,
41    -show-path) */
42 string var_to_expand = NULL;
43 string braces_to_expand = NULL;
44 string path_to_expand = NULL;
45 string path_to_show = NULL;
46 string var_to_value = NULL;
47 
48 /* Base resolution. (-D, -dpi) */
49 unsigned dpi = 600;
50 
51 /* The engine name, for '$engine' construct in texmf.cnf.  (-engine) */
52 string engine = NULL;
53 
54 /* Interactively ask for names to look up?  (-interactive) */
55 boolean interactive = false;
56 
57 /* The device name, for $MAKETEX_MODE.  (-mode) */
58 string mode = NULL;
59 
60 /* Search the disk as well as ls-R?  (-must-exist, -mktex) */
61 boolean must_exist = false;
62 
63 /* The program name, for `.PROG' construct in texmf.cnf.  (-program) */
64 string progname = NULL;
65 
66 /* Safe input and output names to check.  (-safe-in-name and -safe-out-name) */
67 string safe_in_name = NULL;
68 string safe_out_name = NULL;
69 
70 /* Return all matches, not just the first one?  (-all) */
71 boolean show_all = false;
72 
73 /* Only match files in given subdirs.  (-subdir) */
74 str_list_type subdir_paths;
75 
76 /* The file type and path for lookups.  (-format, -path) */
77 kpse_file_format_type user_format = kpse_last_format;
78 string user_format_string;
79 string user_path;
80 
81 
82 
83 /* Define one-word abbreviations for those format types which
84    can otherwise only be specified by strings containing spaces.  */
85 
86 typedef struct
87 {
88   const_string abbr;
89   kpse_file_format_type format;
90 } format_abbr_type;
91 
92 static format_abbr_type format_abbrs[]
93   = { { "bitmapfont", kpse_any_glyph_format },
94       { "mpsupport", kpse_mpsupport_format },
95       { "doc", kpse_texdoc_format },
96       { "source", kpse_texsource_format },
97       { "trofffont", kpse_troff_font_format },
98       { "dvipsconfig", kpse_dvips_config_format },
99       { "web2c", kpse_web2c_format },
100       { "othertext", kpse_program_text_format },
101       { "otherbin", kpse_program_binary_format },
102       { "miscfont", kpse_miscfonts_format },
103       { "cmap", kpse_cmap_format },
104       { "pdftexconfig", kpse_pdftex_config_format },
105       { NULL, kpse_last_format } };
106 
107 /* The function to look up STR in the abbr table above.
108    This is called only on a user-specified format string.
109    Return `kpse_last_format' if no match.  */
110 
111 static kpse_file_format_type
format_abbr(const_string str)112 format_abbr (const_string str)
113 {
114   kpse_file_format_type ret = kpse_last_format;
115   unsigned a = 0;
116 
117   while (format_abbrs[a].abbr != NULL) {
118     if (STREQ (str, format_abbrs[a].abbr)) {
119       ret = format_abbrs[a].format;
120       break;
121     }
122     a++;
123   }
124 
125   return ret;
126 }
127 
128 
129 
130 /* Return the <number> substring in `<name>.<number><stuff>', if S has
131    that form.  If it doesn't, return 0.  */
132 
133 static unsigned
find_dpi(string s)134 find_dpi (string s)
135 {
136   unsigned dpi_number = 0;
137   const_string extension = find_suffix (s);
138 
139   if (extension != NULL)
140     sscanf (extension, "%u", &dpi_number);
141 
142   return dpi_number;
143 }
144 
145 
146 
147 /* Return true if FTRY (the candidate suffix) matches NAME.  If
148    IS_FILENAME is true, the check is simply that FTRY is a suffix of
149    NAME.  If false (that is, NAME is a format), then FTRY and NAME must
150    be entirely equal.  */
151 
152 static boolean
try_suffix(boolean is_filename,string name,unsigned name_len,const_string ftry)153 try_suffix (boolean is_filename, string name, unsigned name_len,
154             const_string ftry)
155 {
156   unsigned try_len;
157 
158   if (!ftry || ! *ftry) {
159     return false;
160   }
161 
162   try_len = strlen (ftry);
163   if (try_len > name_len) {
164     /* Candidate is longer than what we're looking for.  */
165     return false;
166   }
167   if (!is_filename && try_len < name_len) {
168     /* We're doing format names, not file names, and candidate is
169        shorter than what we're looking for.  E.g., do not find `lua'
170        when looking for `clua'.  */
171     return false;
172   }
173 
174   if (FILESTRCASEEQ (name + name_len - try_len, ftry)) {
175     return true;
176   }
177 
178   return false;
179 }
180 
181 
182 
183 /* Use the file type from -format if that was previously determined
184    (i.e., the user_format global variable), else guess dynamically from
185    NAME.  Return kpse_last_format if undeterminable.  This function is
186    also used to parse the -format string, a case we distinguish via
187    is_filename being false.
188 
189    A few filenames have been hard-coded for format types that
190    differ from what would be inferred from their extensions. */
191 
192 static kpse_file_format_type
find_format(kpathsea kpse,string name,boolean is_filename)193 find_format (kpathsea kpse, string name, boolean is_filename)
194 {
195   kpse_file_format_type ret = kpse_last_format;
196 
197   if (is_filename && user_format != kpse_last_format) {
198     ret = user_format; /* just return what we already computed */
199 
200   } else if (FILESTRCASEEQ (name, "config.ps")) {
201     ret = kpse_dvips_config_format;
202   } else if (FILESTRCASEEQ (name, "fmtutil.cnf")) {
203     ret = kpse_web2c_format;
204   } else if (FILESTRCASEEQ (name, "glyphlist.txt")) {
205     ret = kpse_fontmap_format;
206   } else if (FILESTRCASEEQ (name, "mktex.cnf")) {
207     ret = kpse_web2c_format;
208   } else if (FILESTRCASEEQ (name, "pdfglyphlist.txt")) {
209     ret = kpse_fontmap_format;
210   } else if (FILESTRCASEEQ (name, "pdftex.cfg")) {
211     ret = kpse_pdftex_config_format;
212   } else if (FILESTRCASEEQ (name, "texglyphlist.txt")) {
213     ret = kpse_fontmap_format;
214   } else if (FILESTRCASEEQ (name, "texmf.cnf")) {
215     ret = kpse_cnf_format;
216   } else if (FILESTRCASEEQ (name, "updmap.cfg")) {
217     ret = kpse_web2c_format;
218   } else if (FILESTRCASEEQ (name, "XDvi")) {
219     ret = kpse_program_text_format;
220   } else {
221     if (!is_filename) {
222       /* Look for kpsewhich-specific format abbreviations.  */
223       ret = format_abbr (name);
224     }
225 
226     if (ret == kpse_last_format) {
227       int f = 0;  /* kpse_file_format_type */
228       unsigned name_len = strlen (name);
229 
230       while (f != kpse_last_format) {
231         const_string *ext;
232         const_string ftry;
233         boolean found = false;
234 
235         if (!kpse->format_info[f].type)
236           kpathsea_init_format (kpse, (kpse_file_format_type) f);
237 
238 /* Just to abbreviate this lengthy call.  */
239 #define TRY_SUFFIX(ftry) try_suffix (is_filename, name, name_len, (ftry))
240 
241         if (!is_filename) {
242           /* Allow the long name, but only in the format options.  We don't
243              want a filename confused with a format name.  */
244           ftry = kpse->format_info[f].type;
245           found = TRY_SUFFIX (ftry);
246         }
247         for (ext = kpse->format_info[f].suffix; !found && ext && *ext; ext++) {
248           found = TRY_SUFFIX (*ext);
249         }
250         for (ext=kpse->format_info[f].alt_suffix; !found && ext && *ext;ext++){
251           found = TRY_SUFFIX (*ext);
252         }
253 
254         if (found)
255           break;
256         f++;
257       }
258       ret = f;
259     }
260   }
261   return ret;
262 }
263 
264 
265 
266 /* Return newly-allocated NULL-terminated list of strings from MATCHES
267    that are prefixed with any of the subdirectories in SUBDIRS.  That
268    is, for a string S in MATCHES, its dirname must end with one of the
269    elements in SUBDIRS.  For instance, if subdir=foo/bar, that will
270    match a string foo/bar/baz or /some/texmf/foo/bar/baz.
271 
272    We don't reallocate the actual strings, just the list elements.
273    Perhaps later we will implement wildcards or // or something.  */
274 
275 static string *
subdir_match(str_list_type subdirs,string * matches)276 subdir_match (str_list_type subdirs,  string *matches)
277 {
278   string *ret = XTALLOC1 (string);
279   unsigned len = 1;
280   unsigned e;
281   unsigned m;
282 #if defined(WIN32)
283   string p;
284 
285   for (e = 0; e < STR_LIST_LENGTH (subdirs); e++) {
286     for (p = STR_LIST_ELT (subdirs, e); *p; p++) {
287       if (*p == '\\')
288         *p = '/';
289       else if (IS_KANJI(p))
290         p++;
291     }
292   }
293 #endif
294 
295   for (m = 0; matches[m]; m++) {
296     unsigned loc;
297     string s = xstrdup (matches[m]);
298     for (loc = strlen (s); loc > 0 && !IS_DIR_SEP_CH (s[loc-1]); loc--)
299       ;
300     while (loc > 0 && IS_DIR_SEP_CH (s[loc-1])) {
301       loc--;
302     }
303     s[loc] = 0;  /* wipe out basename */
304 
305     for (e = 0; e < STR_LIST_LENGTH (subdirs); e++) {
306       string subdir = STR_LIST_ELT (subdirs, e);
307       unsigned subdir_len = strlen (subdir);
308       while (subdir_len > 0 && IS_DIR_SEP_CH (subdir[subdir_len-1])) {
309         subdir_len--;
310         subdir[subdir_len] = 0; /* remove trailing slashes from subdir spec */
311       }
312       if (FILESTRCASEEQ (subdir, s + loc - subdir_len)) {
313         /* matched, save this one.  */
314         XRETALLOC (ret, len + 1, string);
315         ret[len-1] = matches[m];
316         len++;
317       }
318     }
319     free (s);
320   }
321   ret[len-1] = NULL;
322   return ret;
323 }
324 
325 
326 
327 /* Look up a single filename NAME.  Return 0 if success, 1 if failure.  */
328 
329 static unsigned
lookup(kpathsea kpse,string name)330 lookup (kpathsea kpse, string name)
331 {
332   int i;
333   string ret = NULL;
334   string *ret_list = NULL;
335 
336   if (user_path) {
337     /* Translate ; to : if that's our ENV_SEP.  See cnf.c.  */
338     if (IS_ENV_SEP (':')) {
339       string loc;
340       for (loc = user_path; *loc; loc++) {
341         if (*loc == ';')
342           *loc = ':';
343       }
344     }
345     user_path = kpathsea_path_expand (kpse, user_path);
346     if (show_all) {
347       ret_list = kpathsea_all_path_search (kpse, user_path, name);
348     } else {
349       ret = kpathsea_path_search (kpse, user_path, name, must_exist);
350     }
351 
352   } else {
353     /* No user-specified search path, check user format or guess from NAME.  */
354     kpse_file_format_type fmt = find_format (kpse, name, true);
355 
356     switch (fmt) {
357       case kpse_pk_format:
358       case kpse_gf_format:
359       case kpse_any_glyph_format:
360         {
361           kpse_glyph_file_type glyph_ret;
362           string temp = remove_suffix (name);
363           /* Try to extract the resolution from the name.  */
364           unsigned local_dpi = find_dpi (name);
365           if (!local_dpi)
366             local_dpi = dpi;
367           ret = kpathsea_find_glyph (kpse, temp,
368                                      local_dpi, fmt, &glyph_ret);
369           if (temp != name)
370             free (temp);
371         }
372         break;
373 
374       case kpse_last_format:
375         /* If the suffix isn't recognized, assume it's a tex file. */
376         fmt = kpse_tex_format;
377         /* fall through */
378 
379       default:
380         if (show_all) {
381           ret_list = kpathsea_find_file_generic (kpse, name, fmt,
382                                                  must_exist, true);
383         } else {
384           ret = kpathsea_find_file (kpse, name, fmt, must_exist);
385         }
386     }
387   }
388 
389   /* Turn single return into a null-terminated list for uniform treatment.  */
390   if (ret) {
391     ret_list = XTALLOC (2, string);
392     ret_list[0] = ret;
393     ret_list[1] = NULL;
394   }
395 
396   /* Filter by subdirectories, if specified.  */
397   if (STR_LIST_LENGTH (subdir_paths) > 0) {
398     string *new_list = subdir_match (subdir_paths, ret_list);
399     free (ret_list);
400     ret_list = new_list;
401   }
402 
403   /* Print output.  */
404   if (ret_list) {
405     for (i = 0; ret_list[i]; i++)
406       puts (ret_list[i]);
407     /* Save whether we found anything */
408     ret = ret_list[0];
409     free (ret_list);
410   }
411 
412   return ret == NULL;
413 }
414 
415 /* Help message.  */
416 
417 #define USAGE "\n\
418 Standalone path lookup and expansion for Kpathsea.\n\
419 The default is to look up each FILENAME in turn and report its\n\
420 first match (if any) to standard output.\n\
421 \n\
422 When looking up format (.fmt/.base/.mem) files, it is usually necessary\n\
423 to also use -engine, or nothing will be returned; in particular,\n\
424 -engine=/ will return matching format files for any engine.\n\
425 \n\
426 -all                   output all matches, one per line.\n\
427 -debug=NUM             set debugging flags.\n\
428 -D, -dpi=NUM           use a base resolution of NUM; default 600.\n\
429 -engine=STRING         set engine name to STRING.\n\
430 -expand-braces=STRING  output variable and brace expansion of STRING.\n\
431 -expand-path=STRING    output complete path expansion of STRING.\n\
432 -expand-var=STRING     output variable expansion of STRING.\n\
433 -format=NAME           use file type NAME (see list below).\n\
434 -help                  print this message and exit.\n\
435 -interactive           ask for additional filenames to look up.\n\
436 [-no]-mktex=FMT        disable/enable mktexFMT generation (FMT=pk/mf/tex/tfm).\n\
437 -mode=STRING           set device name for $MAKETEX_MODE to STRING; no default.\n\
438 -must-exist            search the disk as well as ls-R if necessary.\n\
439 -path=STRING           search in the path STRING.\n\
440 -progname=STRING       set program name to STRING.\n\
441 -safe-in-name=STRING   check if STRING is ok to open for input.\n\
442 -safe-out-name=STRING  check if STRING is ok to open for output.\n\
443 -show-path=NAME        output search path for file type NAME (list below).\n\
444 -subdir=STRING         only output matches whose directory ends with STRING.\n\
445 -var-value=STRING      output the value of variable $STRING.\n\
446 -version               print version number and exit.\n \
447 "
448 
449 static void
help_message(kpathsea kpse,string * argv)450 help_message (kpathsea kpse, string *argv)
451 {
452   int f; /* kpse_file_format_type */
453 
454   printf ("Usage: %s [OPTION]... [FILENAME]...\n", argv[0]);
455   fputs (USAGE, stdout);
456   putchar ('\n');
457   fputs (kpathsea_bug_address, stdout);
458 
459   /* Have to set this for init_format to work.  */
460   kpathsea_set_program_name (kpse, argv[0], progname);
461 
462   puts ("\nRecognized format names and their (abbreviations) and suffixes:");
463   for (f = 0; f < kpse_last_format; f++) {
464     const_string *ext;
465     kpathsea_init_format (kpse, (kpse_file_format_type)f);
466     printf ("%s", kpse->format_info[f].type);
467 
468     /* Show abbreviation if we accept one.  We repeatedly go through the
469        abbr list here, but it's so short, it doesn't matter.  */
470     {
471        unsigned a = 0;
472        while (format_abbrs[a].abbr != NULL) {
473          if (f == format_abbrs[a].format) {
474            printf (" (%s)", format_abbrs[a].abbr);
475            break;
476          }
477          a++;
478        }
479     }
480 
481     /* Regular suffixes.  */
482     putchar (':');
483     for (ext = kpse->format_info[f].suffix; ext && *ext; ext++) {
484       putchar (' ');
485       fputs (*ext, stdout);
486     }
487 
488     if (kpse->format_info[f].alt_suffix) {
489       /* leave extra space between default and alt suffixes */
490       putchar (' ');
491     }
492     for (ext = kpse->format_info[f].alt_suffix; ext && *ext; ext++) {
493       putchar (' ');
494       fputs (*ext, stdout);
495     }
496 
497     putchar ('\n');
498   }
499 
500   exit (0);
501 }
502 
503 
504 /* Reading the options.  */
505 
506 /* This macro tests whether getopt found an option ``A''.
507    Assumes the option index is in the variable `option_index', and the
508    option table in a variable `long_options'.  */
509 #define ARGUMENT_IS(a) STREQ (long_options[option_index].name, a)
510 
511 /* SunOS cc can't initialize automatic structs.  */
512 static struct option long_options[]
513   = { { "D",                    1, 0, 0 },
514       { "all",                  0, (int *) &show_all, 1 },
515       { "debug",                1, 0, 0 },
516       { "dpi",                  1, 0, 0 },
517       { "engine",               1, 0, 0 },
518       { "expand-braces",        1, 0, 0 },
519       { "expand-path",          1, 0, 0 },
520       { "expand-var",           1, 0, 0 },
521       { "format",               1, 0, 0 },
522       { "help",                 0, 0, 0 },
523       { "interactive",          0, (int *) &interactive, 1 },
524       { "mktex",                1, 0, 0 },
525       { "mode",                 1, 0, 0 },
526       { "must-exist",           0, (int *) &must_exist, 1 },
527       { "path",                 1, 0, 0 },
528       { "no-mktex",             1, 0, 0 },
529       { "progname",             1, 0, 0 },
530       { "safe-in-name",         1, 0, 0 },
531       { "safe-out-name",        1, 0, 0 },
532       { "subdir",               1, 0, 0 },
533       { "show-path",            1, 0, 0 },
534       { "var-value",            1, 0, 0 },
535       { "version",              0, 0, 0 },
536       { 0, 0, 0, 0 } };
537 
538 static void
read_command_line(kpathsea kpse,int argc,string * argv)539 read_command_line (kpathsea kpse, int argc, string *argv)
540 {
541   int g;   /* `getopt' return code.  */
542   int option_index;
543 
544   for (;;) {
545     g = getopt_long_only (argc, argv, "", long_options, &option_index);
546 
547     if (g == -1)
548       break;
549 
550     if (g == '?')
551       exit (1);  /* Unknown option.  */
552 
553     assert (g == 0); /* We have no short option names.  */
554 
555     if (ARGUMENT_IS ("debug")) {
556       kpse->debug |= atoi (optarg);
557 
558     } else if (ARGUMENT_IS ("dpi") || ARGUMENT_IS ("D")) {
559       dpi = atoi (optarg);
560 
561     } else if (ARGUMENT_IS ("engine")) {
562       engine = optarg;
563 
564     } else if (ARGUMENT_IS ("expand-braces")) {
565       braces_to_expand = optarg;
566 
567     } else if (ARGUMENT_IS ("expand-path")) {
568       path_to_expand = optarg;
569 
570     } else if (ARGUMENT_IS ("expand-var")) {
571       var_to_expand = optarg;
572 
573     } else if (ARGUMENT_IS ("format")) {
574       user_format_string = optarg;
575 
576     } else if (ARGUMENT_IS ("help")) {
577       help_message (kpse, argv);
578 
579     } else if (ARGUMENT_IS ("mktex")) {
580       kpathsea_maketex_option (kpse, optarg, true);
581       must_exist = 1;  /* otherwise it never gets called */
582 
583     } else if (ARGUMENT_IS ("mode")) {
584       mode = optarg;
585 
586     } else if (ARGUMENT_IS ("no-mktex")) {
587       kpathsea_maketex_option (kpse, optarg, false);
588       must_exist = 0;
589 
590     } else if (ARGUMENT_IS ("path")) {
591       user_path = optarg;
592 
593     } else if (ARGUMENT_IS ("progname")) {
594       progname = optarg;
595 
596     } else if (ARGUMENT_IS ("safe-in-name")) {
597       safe_in_name = optarg;
598 
599     } else if (ARGUMENT_IS ("safe-out-name")) {
600       safe_out_name = optarg;
601 
602     } else if (ARGUMENT_IS ("show-path")) {
603       path_to_show = optarg;
604       user_format_string = optarg;
605 
606     } else if (ARGUMENT_IS ("subdir")) {
607       str_list_add (&subdir_paths, optarg);
608 
609     } else if (ARGUMENT_IS ("var-value")) {
610       var_to_value = optarg;
611 
612     } else if (ARGUMENT_IS ("version")) {
613       puts (kpathsea_version_string);
614       puts ("Copyright 2013 Karl Berry & Olaf Weber.\n\
615 License LGPLv2.1+: GNU Lesser GPL version 2.1 or later <http://gnu.org/licenses/lgpl.html>\n\
616 This is free software: you are free to change and redistribute it.\n\
617 There is NO WARRANTY, to the extent permitted by law.\n");
618       exit (0);
619     }
620 
621     /* Else it was just a flag; getopt has already done the assignment.  */
622   }
623 
624   if (user_path && user_format_string) {
625     fprintf (stderr, "-path (%s) and -format (%s) are mutually exclusive.\n",
626              user_path, user_format_string);
627     fputs ("Try `kpsewhich --help' for more information.\n", stderr);
628     exit (1);
629   }
630 
631   if (optind == argc
632       && !var_to_expand && !braces_to_expand && !path_to_expand
633       && !path_to_show && !var_to_value
634       && !safe_in_name && !safe_out_name) {
635     fputs ("Missing argument. Try `kpsewhich --help' for more information.\n",
636            stderr);
637     exit (1);
638   }
639 }
640 
641 
642 
643 /* Initializations that may depend on the options.  */
644 
645 static void
init_more(kpathsea kpse)646 init_more (kpathsea kpse)
647 {
648   if (engine)
649     kpathsea_xputenv (kpse, "engine", engine);
650 
651   /* Disable all mktex programs unless they were explicitly enabled on our
652      command line.  */
653 #define DISABLE_MKTEX(fmt) \
654 kpathsea_set_program_enabled (kpse, fmt, false, kpse_src_cmdline - 1)
655   DISABLE_MKTEX (kpse_pk_format);
656   DISABLE_MKTEX (kpse_mf_format);
657   DISABLE_MKTEX (kpse_tex_format);
658   DISABLE_MKTEX (kpse_tfm_format);
659   DISABLE_MKTEX (kpse_fmt_format);
660   DISABLE_MKTEX (kpse_ofm_format);
661   DISABLE_MKTEX (kpse_ocp_format);
662 
663   /* NULL for no fallback font.  */
664   kpathsea_init_prog (kpse, uppercasify (kpse->program_name), dpi, mode, NULL);
665 
666   /* Have to do this after setting the program name.  */
667   if (user_format_string) {
668     user_format = find_format (kpse, user_format_string, false);
669     if (user_format == kpse_last_format) {
670       WARNING1 ("kpsewhich: Ignoring unknown file type `%s'",
671                 user_format_string);
672     }
673   }
674 }
675 
676 
677 
678 int
main(int argc,string * argv)679 main (int argc,  string *argv)
680 {
681 #ifdef WIN32
682   string *av, enc;
683   int ac;
684 #endif
685   unsigned unfound = 0;
686   kpathsea kpse = kpathsea_new();
687 
688   /* Read options, then dependent initializations.  */
689   read_command_line (kpse, argc, argv);
690 
691   kpathsea_set_program_name (kpse, argv[0], progname);
692 #ifdef WIN32
693   if(strstr(kpse->program_name,"xetex") || strstr(kpse->program_name,"xelatex")
694      || strstr(kpse->program_name,"uptex") || strstr(kpse->program_name,"uplatex")
695      || strstr(kpse->program_name,"dvipdfm") || strstr(kpse->program_name,"extractbb")
696      || strstr(kpse->program_name,"xbb") || strstr(kpse->program_name,"ebb")
697      || strstr(kpse->program_name,"dvips"))
698   {
699     enc = kpathsea_var_value (kpse, "command_line_encoding");
700     if (get_command_line_args_utf8(enc, &ac, &av)) {
701       optind = 0;
702       read_command_line (kpse, ac, av);
703       argv = av;
704       argc = ac;
705     }
706   }
707 #endif
708   init_more (kpse);
709 
710 
711   /* Perform actions.  */
712 
713   /* Variable expansion.  */
714   if (var_to_expand)
715     puts (kpathsea_var_expand (kpse, var_to_expand));
716 
717   /* Brace expansion. */
718   if (braces_to_expand)
719     puts (kpathsea_brace_expand (kpse, braces_to_expand));
720 
721   /* Path expansion. */
722   if (path_to_expand)
723     puts (kpathsea_path_expand (kpse, path_to_expand));
724 
725   /* Show a search path. */
726   if (path_to_show) {
727     if (user_format != kpse_last_format) {
728       if (!kpse->format_info[user_format].type) /* needed if arg was numeric */
729         kpathsea_init_format (kpse, user_format);
730       puts (kpse->format_info[user_format].path);
731     } else {
732       WARNING ("kpsewhich: Cannot show path for unknown file type");
733     }
734   }
735 
736   /* Var to value. */
737   if (var_to_value) {
738     const_string value = kpathsea_var_value (kpse, var_to_value);
739     if (!value) {
740       unfound++;
741       value = "";
742     }
743     puts (value);
744   }
745 
746   if (safe_in_name) {
747     if (!kpathsea_in_name_ok_silent (kpse, safe_in_name))
748       unfound++;
749   }
750 
751   if (safe_out_name) {
752     if (!kpathsea_out_name_ok_silent (kpse, safe_out_name))
753       unfound++;
754   }
755 
756   /* --subdir must imply --all, since we filter here after doing the
757      search, rather than inside the search itself.  */
758   if (STR_LIST_LENGTH (subdir_paths) > 0) {
759     show_all = 1;
760   }
761 
762   /* Usual case: look up each given filename.  */
763   for (; optind < argc; optind++) {
764     unfound += lookup (kpse, argv[optind]);
765   }
766 
767   if (interactive) {
768     for (;;) {
769       string name = read_line (stdin);
770       if (!name || STREQ (name, "q") || STREQ (name, "quit"))
771         break;
772       unfound += lookup (kpse, name);
773       free (name);
774     }
775   }
776 
777   kpathsea_finish (kpse);
778   return unfound > 255 ? 1 : unfound;
779 }
780