OSDN Git Service

clean
[putex/putex.git] / src / texsourc / subroute.c
1 /* Copyright 2007 TeX Users Group
2    Copyright 2014 Clerk Ma
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2 of the License, or
6    (at your option) any later version.
7
8    This program is distributed in the hope that it will be useful, but
9    WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11    General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16    02110-1301 USA.  */
17
18 #ifdef _WINDOWS
19   #define NOCOMM
20   #define NOSOUND
21   #define NODRIVERS
22   #define STRICT
23   #pragma warning(disable:4115) // kill rpcasync.h complaint
24   #include <windows.h>
25   #define MYLIBAPI __declspec(dllexport)
26 #endif
27
28 #include "texwin.h"
29
30 #pragma warning(disable:4996)
31 #pragma warning(disable:4131) // old style declarator
32 #pragma warning(disable:4135) // conversion between different integral types 
33 #pragma warning(disable:4127) // conditional expression is constant
34
35 #include <setjmp.h>
36 #include <assert.h>
37
38 #pragma hdrstop
39
40 #define EXTERN extern
41
42 #include "texd.h"
43
44 #include <io.h>    // needed for _finddata_t 
45 #include <ctype.h> // needed for isascii and isalpha
46
47 //////////////////////////////////////////////////////////////////////////////////
48
49 #define NAME_MAX 255      // max size of name component
50
51 #define ISALPHA(c) (isascii (c) && isalpha(c))
52
53 #define PATH_SEP              '/'
54 #define PATH_SEP_STRING       "/"
55 #define PATH_DELIMITER        ';'
56 #define PATH_DELIMITER_STRING ";"
57
58 // default paths to look for things
59
60 #define TEXPATH    "C:/yandy/yandytex/"
61
62 #define TEXFORMATS TEXPATH "fmt"
63 #define TEXPOOL    TEXPATH "pool"
64 #define TEXFONTS   TEXPATH "tfm"
65 #define TEXINPUTS  TEXPATH "tex//;" "C:/tex;" "C:/texinput"
66
67 // structure used by fontmap
68
69 typedef struct map_element_struct
70 {
71   char * key;
72   char * value;
73   struct map_element_struct * next;
74 } map_element_type;
75
76 typedef map_element_type **map_type;
77
78 extern bool usesourcedirectory; /* in local.c */
79
80 extern bool workingdirectory;   /* in local.c */
81
82 bool absolute_p (string filename);
83 string readable (string name);
84 string truncate_pathname (string name);
85 char * file_p(string fn);
86 bool dir_p(string fn);
87 string *find_dir_list (string path);
88 void add_directory (string **dir_list_ptr, unsigned *dir_count_ptr, string dir);
89 void expand_subdir (string **dir_list_ptr, unsigned *dir_count_ptr, string dirname,
90           struct _finddata_t findt, integer recurseflag);
91 void save_dir_list (string path,  string *dir_list);
92 string *initialize_path_list (string env_name,  string default_path);
93 int xfind_path_filename (string buffer, string filename,  string * dir_list);
94 string expand_default (string env_path, string default_path);
95 map_type map_create (string *dir_list);
96 char *map_lookup (map_type map, char *key);
97
98 // the following do *not* use MALLOC
99
100 extern char * xconcat  (char *buffer, char *s1, char *s2);            /* openinou.c */
101 extern char * xconcat3 (char *buffer, char *s1, char *s2, char *s3); /* openinou.c */
102
103 /////////////////////////////////////////////////////////////////////////
104
105 // used only in jump_out in tex0.c, and in texbody in itex.c
106 // and main in texmf.c and a few other abort situations in texmf.c
107 /* texk/web2c/lib/uexit.c */
108 void uexit (int unix_code)
109 {
110   int final_code;
111
112 #ifndef _WINDOWS
113   fflush(stdout);
114 #endif
115
116   if (unix_code == 0)
117     final_code = EXIT_SUCCESS;
118   else if (unix_code == 1)
119     final_code = EXIT_FAILURE;
120   else
121     final_code = unix_code;
122 /*---->*/
123   if (jump_used)
124   {
125     show_line("Jump Buffer already used\n", 1);
126     exit(1);
127   }
128   jump_used++;
129 /*<----*/
130   exit(final_code);
131 }
132
133 // round a double being careful about very large and very small values
134 // used only in tex0.c, a Pascal function
135 /* texk/web2c/lib/zround.c */
136 integer zround (double r)
137 {
138   integer i;
139
140   /* R can be outside the range of an integer if glue is stretching or
141      shrinking a lot.  We can't do any better than returning the largest
142      or smallest integer possible in that case.  It doesn't seem to make
143      any practical difference.  Here is a sample input file which
144      demonstrates the problem, from phil@cs.arizona.edu:
145        \documentstyle{article}
146        \begin{document}
147        \begin{flushleft}
148        $\hbox{} $\hfill 
149        \filbreak
150        \eject
151
152      djb@silverton.berkeley.edu points out we should testing against
153      TeX's largest or smallest integer (32 bits), not the machine's.  So
154      we might as well use a floating-point constant, and avoid potential
155      compiler bugs (also noted by djb, on BSDI).  */
156   if (r > 2147483647.0)
157     i = 2147483647;
158   else if (r < -2147483647.0)
159     i = -2147483647;
160   else if (r >= 0.0)
161     i = (integer)(r + 0.5);
162   else
163     i = (integer)(r - 0.5);
164
165   return i;
166 }
167
168 /****************************************************************************/
169
170 // malloc with error checking and error message
171 /* kpathsea/xmalloc.c */
172 void * xmalloc (unsigned size)
173 {
174   void * new_mem = (void*) malloc (size ? size : 1);
175
176   if (new_mem == NULL) {
177     sprintf(log_line, "malloc: Unable to honor request for %u bytes.\n", size);
178     show_line(log_line, 1);
179     abort();         // ???
180   }
181 #ifdef MYDEBUG
182   if (trace_flag)
183   {
184     sprintf(log_line, "XMALLOC %d\n", size);    /* 1996/Jan/20 */
185     show_line(log_line, 0);
186   }
187 #endif
188   return new_mem;
189 }
190 // calloc with error checking - used by map_create
191 /* kpathsea/xcalloc.c */
192 address xcalloc (unsigned nelem, unsigned elsize)
193 {
194   address new_mem = (address) calloc (nelem, elsize);
195   if (new_mem == NULL)
196   {
197     sprintf(log_line, "Unable to honor request for %u elements of size %u.\n", nelem, elsize);
198     show_line(log_line, 1);
199     abort();
200   }
201   return new_mem;
202 }
203 /* Return a copy of s in new storage. */ /* xmalloc does error checking */
204 /* kpathsea/xstrdup.c */
205 string xstrdup (string s)
206 {
207   string pnew_string = (string) xmalloc (strlen (s) + 1);
208 #ifdef MYDEBUG
209   if (trace_flag)
210   {
211     sprintf(log_line, "XSTRDUP %d %s\n", strlen(s)+1, s);
212     show_line(log_line, 0);
213   }
214 #endif
215   return strcpy (pnew_string, s);
216 }
217 /* only used by line.c (which in turn is only used by  fontmap.c) */
218 /* kpathsea/xrealloc.c */
219 address xrealloc (address old_ptr, unsigned size)
220 {
221   address new_mem;
222
223   if (old_ptr == NULL) new_mem = xmalloc (size);
224   else
225   {
226     new_mem = (address) realloc (old_ptr, size);
227     if (new_mem == NULL)
228     {
229       sprintf(log_line, "Unable to honor request for %u bytes.\n", size);
230       show_line(log_line, 1);
231       abort();
232     }
233   }
234   return new_mem;
235 }
236 // returns newly allocated string
237 /* kpathsea/concat.c */
238 string concat (string s1, string s2)
239 {
240   string answer;
241 #ifdef MYDEBUG
242   if (trace_flag)
243   {
244     sprintf(log_line, "CONCAT %s and %s ", s1, s2);
245     show_line(log_line, 0);
246   }
247 #endif
248   answer = (string) xmalloc (strlen (s1) + strlen (s2) + 1);
249   strcpy (answer, s1);
250   strcat (answer, s2);
251 #ifdef MYDEBUG
252   if (trace_flag)
253   {
254     sprintf(log_line, "=> %s\n", answer);
255     show_line(log_line, 0);
256   }
257 #endif
258   return answer;
259 }
260 // returns newly allocated string
261 /* kpathsea/concat3.c */
262 string concat3 (string s1, string s2, string s3)
263 {
264   string answer;
265 #ifdef MYDEBUG
266   if (trace_flag)
267   {
268     sprintf(log_line, "CONCAT3 %s, %s, and %s ", s1, s2, s3);
269     show_line(log_line, 0);
270   }
271 #endif
272   answer = (string) xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
273   strcpy (answer, s1);
274   strcat (answer, s2);
275   strcat (answer, s3);
276 #ifdef MYDEBUG
277   if (trace_flag)
278   {
279     sprintf(log_line, "=> %s\n", answer);
280     show_line(log_line, 0);
281   }
282 #endif
283   return answer;
284 }
285
286 /***********************************************************************/
287 // following used only in itex.c on pool file
288 /* Return true if we're at the end of FILE, else false.  This implements */
289 /*  Pascal's `eof' builtin.                                              */
290 /* It differs from C feof in that the latter is not true at end of file
291    unless an attempt has actually been made to read past EOF             */
292 /* texk/web2c/lib/eofeoln.c */
293 bool test_eof (FILE * file)
294 {
295   int c;
296 /* Maybe we're already at the end?  */
297   if (!file)
298     return true;
299
300   if (feof (file))
301     return true;
302
303   if ((c = getc (file)) == EOF)
304     return true;
305 /* We weren't at the end.  Back up.  */
306   (void) ungetc (c, file);
307   return false;
308 }
309 /* Return true on end-of-line in FILE or at the end of FILE, else false.  */
310 /* texk/web2c/lib/eofeoln.c */
311 bool eoln (FILE * file)
312 {
313   register int c;
314
315   if (feof (file))
316     return true;
317
318   c = getc (file);
319
320   if (c != EOF)
321     (void) ungetc (c, file);
322
323   return c == '\n' || c == '\r' || c == EOF; // ???
324 /* Type mismatch (return) (int/enum) ? */
325 }
326 /***********************************************************************/
327
328 // following used only by fontmap.c and openinou.c
329
330 // #define FATAL_PERROR(s) do { perrormod (s); exit (errno); } while (0)
331
332 // perrormod puts error message on stdout instead of stderr
333
334 /* These routines just check the return status from standard library
335    routines and abort if an error happens.  */
336
337 // xfopen used in open_input in openinou.c
338 /* kpathsea/xfopen.c */
339 FILE * xfopen (char *filename, char * fmode)
340 {
341   FILE *f;
342
343   assert(filename && mode);
344 /* if share_flag is non-zero and we are opening for reading use fsopen */
345 /* f = fopen (filename, mode); */
346   if (share_flag == 0 || *fmode != 'r')
347     f = fopen (filename, fmode);
348   else
349     f = _fsopen (filename, fmode, share_flag);
350   if (f == NULL)
351   {
352 //    FATAL_PERROR (filename);
353     perrormod(filename);
354     uexit(1);   // ???
355   }
356   return f;
357 }
358 // xfclose not used ...
359 /* kpathsea/xfopen.c */
360 int xfclose (FILE *f, char *filename)
361 {
362   assert(f);
363
364   if (ferror(f) != 0 || fclose(f) != 0)
365   {
366 //    FATAL_PERROR (filename);
367     perrormod(filename);
368     uexit(1);   // ???
369   }
370   return 0;
371 }
372
373 /********************************************************************************/
374 // following used only in map_lookup
375 // return pointer to start of extension --- or NULL if there isn't one
376 /* kpathsea/find-suffix.c */
377 string find_suffix (string name)
378 {
379   string dot_pos;
380   string slash_pos;
381
382   dot_pos = strrchr (name, '.');
383 #ifdef MSDOS
384   if ((slash_pos = strrchr (name, PATH_SEP)) != NULL);
385   else if ((slash_pos = strrchr (name, '\\')) != NULL);  
386   else if ((slash_pos = strrchr (name, ':')) != NULL);
387   else slash_pos = name;
388 #else
389   slash_pos = strrchr (name, PATH_SEP);
390 #endif
391
392 /* If the name is `foo' or `/foo.bar/baz', we have no extension.  */
393   return dot_pos == NULL || dot_pos < slash_pos ? NULL : dot_pos + 1;
394 }
395 // remove extension of file name - returns copy or NULL
396 /* kpathsea/rm-suffix.c */
397 string remove_suffix (string s)
398 {
399   string ret;
400   string suffix = find_suffix (s);
401
402   if (suffix) {
403     suffix--;   /* Back up to before the dot.  */
404     ret = (char *) xmalloc (suffix - s + 1);
405     strncpy (ret, s, suffix - s);
406     ret[suffix - s] = '\0';
407   }
408   else ret = NULL;
409
410   return ret;
411 }
412 // add extension to file name unless it already has one
413 // returns copy or the old one (warning: danger when freeing)
414 /* kpathsea/extend-fname.c */
415 string extend_filename (string name, string default_suffix)
416 {
417   string new_s;
418   string suffix = find_suffix (name);
419
420   new_s = (suffix == NULL ? concat3 (name, ".", default_suffix) : name);
421   return new_s;
422 }
423 /****************************************************************************************/
424 #ifdef MALLOCLINE
425
426 #define BLOCK_SIZE 64
427
428 // this returns newly allocated character string
429
430 char *read_line (FILE * f)
431 {
432   int c;
433   unsigned int limit = BLOCK_SIZE;
434   unsigned int loc = 0;
435   char * line = (char *) xmalloc (limit);
436
437 /*  while ((c = getc (f)) != EOF && c != '\n')  */
438   while ((c = getc (f)) != EOF && c != '\n' && c != '\r')
439   {
440     line[loc] = (char) c;
441     loc++;
442 /* By testing after the assignment, we guarantee that we'll always
443    have space for the null we append below.  We know we always
444    have room for the first char, since we start with BLOCK_SIZE.  */
445     if (loc == limit)
446     {
447       limit += BLOCK_SIZE;
448       line = (char *) xrealloc (line, limit);
449     }
450   }
451
452 /* If we read anything, return it.  This can't represent a last
453    ``line'' which doesn't end in a newline, but so what.  */
454 /* This is Tom Rokicki's mistake -- lets fix it ! 1994/March/18 */
455   if (c != EOF)
456   {
457 /* Terminate the string.  We can't represent nulls in the file,
458    either.  Again, it doesn't matter.  */
459     line[loc] = 0;
460   }
461   else if (loc > 0)
462   { /* c == EOF, but line not empty 1994/March/18 */
463     line[loc] = 0;
464   }
465   else
466   { /* Real EOF --- at end of file.  */
467     free (line);
468     line = NULL;
469   }
470
471   return line;
472 }
473 #endif
474
475 /* Modified version 97/May/17 to avoid malloc for every line read ... */
476 char * read_a_line (FILE *f,  char *line, int limit)
477 {
478   int c;
479   int loc = 0;
480
481 /*  while ((c = getc (f)) != EOF && c != '\n')  */
482   while ((c = getc (f)) != EOF)
483   {
484     if (c == '\n' || c == '\r')
485     {
486       if (loc > 0) break;
487       else continue;        /* ignore \r\n and blank lines */
488     }
489     line[loc] = (char) c;
490     loc++;
491     if (loc == limit - 1)
492     {     /* very unlikely */
493       sprintf(log_line, " ERROR: line too long\n");
494       show_line(log_line, 1);
495       show_line(line, 0);
496       show_line("\n", 0);
497       break;
498     }
499   }
500
501   if (c != EOF || loc > 0)
502   {      /* normal line or EOF at end of line */
503     line[loc] = '\0';       /* terminate */
504     return line;          /* and return */
505   }
506   else return(NULL);          /* true EOF */
507 }
508 /****************************************************************************************/
509
510 /* from ourpaths.c */
511
512 #define BUILDNAMEDIRECT /* avoid malloc for string concat */
513
514 #define CACHEFILENAME   /* cache last full path/file name 96/Nov/16 */
515                         /* to speed up LaTeX 2e which opens files twice */
516
517 /* `path_dirs' is initialized in `set_paths', to a null-terminated array
518    of directories to search for.  */
519
520 static string *path_dirs[LAST_PATH];
521
522 /* This sets up the paths, by either copying from an environment variable
523    or using the default path, which is defined as a preprocessor symbol
524    (with the same name as the environment variable) in `site.h'.  The
525    parameter PATH_BITS is a logical or of the paths we need to set.  */
526 void set_paths (int path_bits)
527 {
528   int n;                          /* 97/Apr/2 */
529   char *s, *t, *u;                /* 94/Jan/6 */
530   char buffer[PATH_MAX];
531
532 /*  eliminated lots of junk not needed for TeX itself 93/Nov/20 bkph */
533
534 /*  added code to look for pool file in format directory also */
535 /*  added code to look for fmt directory in tex directory also */
536 /*  added code to look for tfm directory in tex directory also */
537 /*  added code to look for some PC TeX flavour environment names 94/Jan/6 */
538 /*  which, in case of formats, could lead to I'm stymied errors ... */
539
540   if (path_bits & TEXFORMATPATHBIT) {
541     s = "TEXFORMATS";
542     t = TEXFORMATS;
543     if (grabenv(s) == NULL) {   /* see if env var defined 94/May/19*/
544       strcpy(buffer, texpath);  /* not, see if texpath\fmt is directory */
545       strcat(buffer, PATH_SEP_STRING); 
546       strcat(buffer, "fmt");
547       if (trace_flag) {
548         sprintf(log_line, "Checking `%s' = %s %s %s\n", buffer, texpath, PATH_SEP_STRING, "fmt"); /* 95/Jan/25 */
549         show_line(log_line, 0);
550       }
551       if (dir_p(buffer)) t = xstrdup(buffer); /* 96/Jan/20 */
552       else {
553         s = "TEXFMTS";      /* added PC-TeX version 94/Jan/6 */
554         if (getenv(s) == NULL)  s = "TEXFMT"; /* em-TeX ... */
555       }
556       if (trace_flag) {
557         sprintf(log_line, "\nSetting up %s (default %s) ", "TEXFORMATS", t);
558         show_line(log_line, 0);
559       }
560     }
561 /* path_dirs[TEXFORMATPATH] = initialize_path_list ("TEXFORMATS", TEXFORMATS); */
562 /* path_dirs[TEXFORMATPATH] = initialize_path_list (s, TEXFORMATS); */
563     path_dirs[TEXFORMATPATH] = initialize_path_list (s, t);
564 /* if (t != TEXFORMATS) free (t); */
565   }
566
567   if (path_bits & TEXPOOLPATHBIT) {
568     s = "TEXPOOL";
569     t = TEXPOOL;
570     if (grabenv(s) == NULL) {     /* 1994/May/19 */
571       s = "TEXFORMATS";       /* try in format directory next */
572       if (grabenv(s) == NULL) {   /* see if environment var defined */
573         strcpy(buffer, texpath);  /* no, see if texpath\fmt is direct */
574         strcat(buffer, PATH_SEP_STRING); 
575         strcat(buffer, "fmt");
576         if (trace_flag) {
577           sprintf(log_line, "Checking `%s' = %s %s %s\n", buffer, texpath, PATH_SEP_STRING, "fmt"); /* 95/Jan/25 */
578           show_line(log_line, 0);
579         }
580         if (dir_p(buffer)) t = xstrdup(buffer);   /* 96/Jan/20 */
581         else {
582           s = "TEXFMTS";      /* added PC-TeX version 94/Jan/6 */
583           if (getenv(s) == NULL)  s = "TEXFMT"; /* em-TeX ... */
584         }
585         if (trace_flag) {
586           sprintf(log_line, "\nSetting up %s (default %s) ", "TEXPOOL", t);
587           show_line(log_line, 0);
588         }
589       }
590     }
591 /*    path_dirs[TEXPOOLPATH] = initialize_path_list ("TEXPOOL", TEXPOOL); */
592 /*    path_dirs[TEXPOOLPATH] = initialize_path_list (s, TEXPOOL); */
593     path_dirs[TEXPOOLPATH] = initialize_path_list (s, t);
594 /*    if (t != TEXPOOL) free (t); */
595   }
596
597   if (path_bits & TFMFILEPATHBIT) {
598     s = "TEXFONTS";
599 /* Introduce encoding specific TEXFONTS env variable 97/April/2 */
600     if ((u = grabenv("ENCODING")) != NULL) {  /* get ENCODING=... */
601       encoding_name = u;        /* remember for error mess */
602 /*      sprintf(log_line, "\nENCODING=%s\n", u); */
603 /*      ENCODING is defined, now see if matching env variable */
604       if ((t = grabenv(u)) != NULL) { /* get TEXNANSI=c:\yandy\tfm; ... */
605 /*        sprintf(loglein, "\nset %s=%s\n", u, t); */
606 /*        prevent problems with TEXNANSI=1 and such mistakes! */
607 /*        should have a drive letter and not be a number */
608         if (strchr(t, ':') != NULL &&
609             sscanf(t, "%d", &n) == 0) {
610           s = u;        /* look here instead of TEXFONTS=... */
611 /*          sprintf(log_line, "\nUSE %s\n", u); */
612         }
613       }
614     }
615
616     t = TEXFONTS;         /* #define TEXFONTS TEXPATH "tfm" */
617 /*    if (getenv(s) == NULL) { */
618     if (grabenv(s) == NULL) {   /* 1994/May/19 */
619       strcpy(buffer, texpath);  /* see if texpath\tfm is directory */
620       strcat(buffer, PATH_SEP_STRING); 
621       strcat(buffer, "tfm");
622       if (trace_flag) {
623         sprintf(log_line, "Checking `%s' = %s %s %s\n",
624             buffer, texpath, PATH_SEP_STRING, "tfm"); /* 95/Jan/25 */
625         show_line(log_line, 0);
626       }
627 /*      if (dir_p(buffer)) t = _strdup(buffer); */
628       if (dir_p(buffer)) t = xstrdup(buffer);     /* 96/Jan/20 */
629       else {
630         s = "TEXTFMS";      /* added PC-TeX version 94/Jan/6 */
631         if (getenv(s) == NULL) s = "TEXTFM"; /* em-TeX uses TEXTFM ... */
632       }
633       if (trace_flag) {
634         sprintf(log_line, "\nSetting up %s (default %s) ", "TEXFONTS", t);
635         show_line(log_line, 0);
636       }
637     }
638 /*    path_dirs[TFMFILEPATH] = initialize_path_list ("TEXFONTS", TEXFONTS); */
639 /*    path_dirs[TFMFILEPATH] = initialize_path_list (s, TEXFONTS); */
640     path_dirs[TFMFILEPATH] = initialize_path_list (s, t);
641 /*    if (t != TEXFONTS) free (t); */
642   }
643
644   if (path_bits & TEXINPUTPATHBIT) {
645     if (format_specific) {                /* 1994/Oct/25 */
646       s = format_name;                /* try specific */
647       if (grabenv(s) == NULL) s = "TEXINPUTS";    /* no format specific */
648     }
649     else s = "TEXINPUTS";               /* normal case */
650 /*    if (getenv(s) == NULL) */
651     if (grabenv(s) == NULL) {             /* 1994/May/19 */
652       s = "TEXINPUT"; /* added PC-TeX vers 94/Jan/6 */
653       if (trace_flag) {
654         sprintf(log_line, "\nSetting up %s ", "TEXINPUTS");
655         show_line(log_line, 0);
656       }
657     }
658 /*    path_dirs[TEXINPUTPATH] = initialize_path_list ("TEXINPUTS", TEXINPUTS); */
659     path_dirs[TEXINPUTPATH]  = initialize_path_list (s, TEXINPUTS);
660   }
661 }
662
663 #ifdef CACHEFILENAME
664   char last_filename[PATH_MAX] = "";  /* last full path / file name found C */
665   char last_name[PATH_MAX]     = "";  /* last file name searched for C */
666   int last_path_index          = -1;  /* last path_index */
667 #endif
668
669 /* Look for NAME, a C string (no longer Pascal), in the colon-separated list 
670    of directories given by `path_dirs[PATH_INDEX]'.  If the search is
671    successful, leave the full pathname in NAME (which therefore must
672    have enough room for such a pathname), padded with blanks.
673    Otherwise, or if NAME is an absolute or relative pathname, just leave
674    it alone.  */
675
676 /*  changed to take C string 97/June/5 - used to take Pascal strings */
677 /*  now expects null terminated strings */
678
679 bool test_read_access (unsigned char *name, int path_index)
680
681 #ifdef BUILDNAMEDIRECT
682   char buffer[PATH_MAX];      /* for constructing name 1996/Jan/20 */
683   int foundflag;          /* true if path found */
684 #else
685   string foundname;
686 #endif  
687
688   if (open_trace_flag) {
689     sprintf(log_line, "Test read access for `%s' ", name);  /* C */
690     show_line(log_line, 0);
691   }
692
693   if (*name == '\0') return FALSE;  /* sanity check */
694
695 #ifdef CACHEFILENAME
696 /*  If file name and path_index matches - and saved filename exists */
697 /*  then use cached full path / file name 96/Nov/16 */
698   if (cache_file_flag) {
699     if (path_index == last_path_index &&
700         strcmp((const char *)name, last_name) == 0 && *last_filename != '\0') { 
701       if (open_trace_flag) {
702         sprintf(log_line, "\nFOUND `%s' (%d) IN CACHE: `%s' ",
703             name, path_index, last_filename); 
704 /*            name+1, path_index, last_filename); */
705         show_line(log_line, 0);
706       }
707       strcpy((char *)name, last_filename); 
708       return TRUE;
709     }
710     last_path_index = path_index;
711     strcpy(last_name, (const char *)name);
712     *last_filename = '\0';          /* in case not found */
713   }
714 #endif
715
716 /* Look for it.  */ /* only call to find_path_filename in pathsrch.c */
717 #ifdef BUILDNAMEDIRECT
718   foundflag = xfind_path_filename (buffer, (char *)name, path_dirs[path_index]);
719 #else
720 /*  this returns either a newly allocated string or name */
721 /*  will need to free it later again ... */
722   foundname = find_path_filename (name, path_dirs[path_index]);
723 #endif
724 /*  If we didn't find it, and we're looking for a font, maybe it's
725     an alias defined in a mapping file.  */ 
726 /*  if (!foundname && path_index == TFMFILEPATH) */
727 #ifdef BUILDNAMEDIRECT
728   if (foundflag == 0 && path_index == TFMFILEPATH)
729 #else
730     if (foundname == NULL && path_index == TFMFILEPATH)
731 #endif
732     {
733       char *mapped_name;
734       static map_type fontmap = NULL;   /* GLOBAL, so won't recreate */
735
736 /*      fault in the mapping if necessary.  */
737       if (fontmap == NULL) {
738         if (trace_flag) {
739           sprintf(log_line, "Loading in texfonts.map file for %s\n", name);
740           show_line(log_line, 0);
741         }
742         fontmap = map_create (path_dirs[path_index]);
743       }
744
745 /*      Now look for our filename in the mapping.  */
746       mapped_name = map_lookup (fontmap, (char *) name);
747       if (mapped_name) {
748 /*      Found a possibility.  Look for the new name.  */
749 #ifdef BUILDNAMEDIRECT
750         foundflag = xfind_path_filename (buffer, mapped_name, path_dirs[path_index]);
751 #else
752         foundname = find_path_filename (mapped_name, path_dirs[path_index]);
753 #endif
754 /*  NOTE: mapped_name is NOT an allocated string to be freed ... */
755       }
756     }
757
758   if (open_trace_flag) {
759     show_line("\n", 0); /* improve trace format out 94/Jan/8 */
760   }
761
762   if (open_trace_flag) {
763 #ifdef BUILDNAMEDIRECT
764     if (foundflag != 0) {
765       sprintf(log_line, "`%s' in test_read_access\n", buffer);
766       show_line(log_line, 0);
767     }
768 #else
769     if (foundname != NULL) {
770       sprintf(log_line, "`%s' in test_read_access\n", foundname);
771       show_line(log_line, 0);
772     }
773 #endif
774   }
775
776 /*  If we found it somewhere, save it.  */
777 #ifdef BUILDNAMEDIRECT
778   if (foundflag != 0) {
779     strcpy ((char *)name, buffer); 
780 #ifdef CACHEFILENAME
781     if (cache_file_flag) {
782       strcpy(last_filename, buffer);  /* full path */
783     }
784 #endif
785   }
786 #else
787   if (foundname != NULL) {
788     strcpy (name, foundname);
789 #ifdef CACHEFILENAME
790     if (cache_file_flag) {
791       strcpy(last_filename, foundname); /* full path */
792       last_namelength = strlen(buffer);
793     }
794 #endif
795   }
796 #endif
797
798 #ifdef BUILDNAMEDIRECT
799   return foundflag;
800 #else
801   if (foundname == NULL) return FALSE;
802   else {
803     if (foundname != name) free (foundname);  /* copied, now free ??? 96/Jan/10 */
804     return TRUE;
805   }
806 #endif
807 }
808
809 /********************************************************************/
810
811 #define STREQ(s1, s2) (strcmp (s1, s2) == 0)
812
813 /* from fontmap.c */
814
815 /* Fontname mapping.  We use a straightforward hash table.  */
816
817 #define MAP_SIZE 199
818
819 /* The hash function.  We go for simplicity here.  */
820
821 static unsigned map_hash (char * key)
822 {
823   unsigned n = 0;
824
825 /*  There are very few font names which are anagrams of each other 
826     so no point in weighting the characters.  */
827   while (*key != 0) n += *key++;
828   n %= MAP_SIZE;
829   return n;
830 }
831
832 /* Look up STR in MAP.  Return the corresponding `value' or NULL.  */
833
834 static char *map_lookup_str (map_type map, char *key)
835 {
836   map_element_type *p;
837   unsigned n = map_hash (key);
838
839   for (p = map[n]; p != NULL; p = p->next)
840     if (STREQ (key, p->key)) return p->value;
841
842   return NULL;
843 }
844
845
846 /* Look up KEY in MAP; if it's not found, remove any suffix from KEY and
847    try again.  */
848
849 char *map_lookup (map_type map, char *key)
850 {
851   string suffix = find_suffix (key);
852   string ret = map_lookup_str (map, key);
853
854   if (! ret) {
855 /*    OK, the original KEY didn't work.  Let's check for the KEY without
856     an extension -- perhaps they gave foobar.tfm, but the mapping only
857     defines `foobar'.  */
858     if (suffix) {
859       string base_key = remove_suffix (key);
860       ret = map_lookup_str (map, base_key);
861       free (base_key);  // it's safe to free copy
862     }
863   }
864
865 /*  Append the same suffix we took off, if necessary.  */
866 /*  what if suffix is NULL ??? */ /* what if we didn't take off suffix ??? */
867 /*  if (ret) */
868   if (ret && suffix) {            /* 1994/March/18 */
869     ret = extend_filename (ret, suffix);
870 //    the above creates a newly allocated string ... should free old ?
871 //    except extend_filename may return the old one ?
872   }
873   return ret;
874 }
875
876 /* If KEY is not already in MAP, insert it and VALUE.  */
877 /* This was very buggy (when hash codes collided) - rewritten 94/March/18 */
878
879 void map_insert (map_type map, char *key, char *value)
880 {
881   unsigned n = map_hash (key);
882   map_element_type **ptr = &map[n];
883 /*  map_element_type ***trailer = &p; */
884
885   while (*ptr != NULL && ! STREQ (key, (*ptr)->key)) {
886 /*       *p = (*p)->next; */
887     ptr = &((*ptr)->next);
888 /*       trailer = &p; */
889   }
890
891   if (*ptr == NULL)    {
892 /*      **trailer = XTALLOC (MAP_SIZE, map_element_type); *//* 94/March/19 */
893     *ptr = (map_element_type *) xmalloc (sizeof(map_element_type));
894 /*      (**trailer)->key = xstrdup (key); */
895     (*ptr)->key = xstrdup (key);
896 /*      (**trailer)->value = xstrdup (value); */
897     (*ptr)->value = xstrdup (value);
898 /*      (**trailer)->next = NULL; */
899     (*ptr)->next = NULL;
900   }
901 }
902
903 /* Open and read the mapping file FILENAME, putting its entries into
904    MAP. Comments begin with % and continue to the end of the line.  Each
905    line of the file defines an entry: the first word is the real
906    filename (e.g., `ptmr'), the second word is the alias (e.g.,
907    `Times-Roman'), and any subsequent words are ignored.  .tfm is added
908    if either the filename or the alias have no extension.  This is the
909    same order as in Dvips' psfonts.map; unfortunately, we can't have TeX
910    read that same file, since most of the real filenames start with an
911    `r', because of the virtual fonts Dvips uses.  */
912
913 /* Modified 97/May/17 to avoid malloc for each line read */
914
915 #ifndef MALLOCLINE
916   #define MAXLINE 256
917 #endif
918
919 int map_file_parse (map_type map, char *map_filename)
920 {
921   char *l;
922   unsigned map_lineno = 0;
923   FILE *f; 
924 #ifndef MALLOCLINE
925   char line[MAXLINE];             /* 97/May/17 */
926 #endif  
927
928   if (trace_flag) {
929     sprintf(log_line, "Opening %s\n",  map_filename); /* 97/May/17 */
930     show_line(log_line, 0);
931   }
932 //  f = xfopen (map_filename, FOPEN_R_MODE);
933   f = fopen (map_filename, FOPEN_R_MODE);
934   if (f == NULL) {
935     perrormod(map_filename);  // should not happen, since we tested
936     return -1;          // failed
937   }
938 #ifdef MALLOCLINE
939   while ((l = read_line (f)) != NULL) 
940 #else
941     while ((l = read_a_line (f, line, sizeof(line))) != NULL) /* 97/May/17 */
942 #endif
943     {
944       string filename;
945       string comment_loc;
946
947 /*      comment_loc = strrchr (l, '%'); */
948       comment_loc = strchr (l, '%');      /* 96/Nov/16 */
949 /*      if (comment_loc == NULL) comment_loc = strrchr (l, ';'); */
950       if (comment_loc == NULL) comment_loc = strchr (l, ';'); /* fixed */
951
952 /*      Ignore anything after a % or;  */
953 /*      if (comment_loc) *comment_loc = 0; */
954       if (comment_loc != NULL) *comment_loc = '\0';
955
956       map_lineno++;
957
958 /*      If we don't have any filename, that's ok, the line is blank.  */
959       filename = strtok (l, " \t");
960 /*      if (filename)  */
961       if (filename != NULL) {
962         string alias = strtok (NULL, " \t");
963
964 /*        But if we have a filename and no alias, something's wrong.  */
965         if (alias == NULL || *alias == 0) {
966           sprintf(log_line,
967               " Have file name `%s', but no mapping (line %u in file %s).\n",
968               filename, map_lineno, map_filename);
969           show_line(log_line, 1);
970         }
971         else  {
972 /*          We've got everything.  Insert the new entry.  */
973           map_insert (map, alias, filename);
974         }
975       }
976 #ifdef MALLOCLINE
977       free (l);
978 #endif
979     }
980 //  xfclose (f, map_filename);
981   (void) fclose (f);    // we don't care about errors at this stage
982   return 0;       // success
983 }
984
985 void unshroud_string (char *, char *, int); /* in texmf.c */
986
987 /* Look for the file `texfonts.map' in each of the directories in
988    DIR_LIST.  Entries in earlier files override later files.  */
989
990 /* This is probably quite silly - but what the hell lets leave it in */
991
992 map_type map_create (string *dir_list)
993 {
994   map_type map = (map_type) xcalloc (MAP_SIZE, sizeof (map_element_type *));
995
996   while (*dir_list) {
997     char filename[PATH_MAX];
998
999 /*     We don't bother with the filename truncation that `readable' in
1000          `pathsrch.c' does, since we ourselves are giving the filename,
1001          and I don't think it's worth worrying about too-long
1002          intermediate directory names in the path.  */
1003     strcpy (filename, *dir_list);
1004 /*      strcat (filename, "texfonts.map"); */   /* 1993/Nov/20 */
1005     unshroud_string (filename+strlen(filename),
1006             "ufygpout/nbq", PATH_MAX - strlen(filename));
1007
1008 /*    testing access first so xfopen won't fail... */
1009 /*    maybe do this another way to avoid using `access' ? */
1010     if (file_method) {
1011       if (file_p (filename) != NULL) {    /* use file_p the new way */
1012         (void)  map_file_parse (map, filename);
1013       }
1014     }
1015     else {  
1016 /*      if (access (filename, R_OK) == 0) */  /* use access the old way */
1017       if (_access (filename, R_OK) == 0) {  /* 1999/Jan/8 ??? */
1018 /*      if (readable (filename) != NULL) */
1019         (void) map_file_parse (map, filename);
1020       }
1021     }
1022     dir_list++;
1023   }
1024   return map;
1025 }
1026
1027 /**********************************************************************/
1028
1029 /* #pragma optimize ("g", off) *//* try and avoid compiler bug here _dos_find */
1030
1031 /* NOTE: _dos_find... prevents running under Windows NT ??? */
1032 /* This is called if file_method != 0 */ /* which is currently the default */
1033
1034 #ifdef MSDOS
1035 /* see whether a file exists, is readable and is not a directory */
1036 /* 1994/Feb/13 may be faster than `access' in `readable' */
1037 /* returns NULL or the name filename passed in ??? */
1038
1039 char *file_p (string fn)
1040 {
1041   struct _finddata_t fi;
1042   long hFind;
1043   int ret;
1044
1045   if (open_trace_flag) {
1046     sprintf(log_line, "Is `%s' a readable file? ", fn);
1047     show_line(log_line, 0);
1048   }
1049
1050 /*  allow for `normal' (_A_NORMAL) as well as `read-only' files */
1051
1052   hFind = _findfirst (fn, &fi);
1053   if (hFind > 0) {
1054     ret = 0;
1055     _findclose (hFind);
1056   }
1057   else ret = -1;
1058
1059 /*  check whether found and whether *not* a sub-directory */
1060   if (ret == 0) {
1061     if ((fi.attrib & _A_SUBDIR) == 0) {
1062       if (open_trace_flag) {
1063         sprintf(log_line, "`%s' IS a readable file. ", fn);
1064         show_line(log_line, 0);
1065       }
1066       return fn;    /* true - its a file, not a dir */
1067     }
1068     else {
1069       if (open_trace_flag) {
1070         sprintf(log_line, "`%s' is a subdirectory. ", fn);
1071         show_line(log_line, 0);
1072       }
1073       return NULL;  /* false - directory */
1074     }
1075   }
1076   else {
1077     if (open_trace_flag) {
1078       sprintf(log_line, "`%s' is NOT a readable file. ", fn);
1079       show_line(log_line, 0);
1080     }
1081     return NULL;  /* false - not found or no read access */
1082   }
1083 }
1084
1085 #endif /* DOS */
1086
1087 /* #pragma optimize ("g",)  */  /* try and avoid compiler bug here _dos_find */
1088 /* #pragma optimize ("g",)*/  /* try and avoid compiler bug here _dos_find */
1089 // #pragma optimize ("", on)    /* 96/Sep/15 */
1090
1091
1092 /**************************************************************************/
1093
1094 /* S_IFMT is file type mask 0170000 and S_IFDIR is directory 0040000 */
1095
1096 #pragma optimize ("g", off)   /* try and avoid compiler bug here _dos_find */
1097
1098 /* NOTE: _dos_find... prevents running under Windows NT ??? */
1099 /* and presently dir_method = true so we do use this _dos_find_first */
1100
1101 bool dir_p (string fn)
1102 {
1103   FILE *test;
1104   char *s;
1105   struct _finddata_t fi;
1106   long hFind;
1107   int ret;
1108   char tmpfn[FILENAME_MAX];       /* long enough ??? */
1109
1110   strcpy (tmpfn, fn);           /* make copy so can modify */
1111   if (open_trace_flag)
1112   {
1113     sprintf(log_line, "Is `%s' a directory? ", tmpfn);
1114     show_line(log_line, 0);
1115   }
1116
1117   s = tmpfn + strlen(tmpfn) - 1;
1118   if (*s == '\\' || *s == '/') *s = '\0'; /* get rid of trailing path sep */
1119
1120 /*  quick test for "." and ".." case - avoid confusion later */
1121   if (strcmp (tmpfn, ".") == 0 || strcmp(tmpfn, "..") == 0) return 1; 
1122
1123   if (dir_method) {     /* use _findfirst *first* if requested */
1124     hFind = _findfirst(tmpfn, &fi);
1125     if (hFind > 0) {
1126       ret = 0;
1127       _findclose(hFind);
1128     }
1129     else ret = -1;
1130
1131     if (ret == 0) {
1132 /*      _findfirst succeeded --- now test attributes of what was found */
1133       if (fi.attrib & _A_SUBDIR) {
1134         if (open_trace_flag) {
1135           sprintf(log_line, "Directory `%s' DOES exist ", fn);
1136           show_line(log_line, 0);
1137         }
1138         return 1;     /* true - it is a sub-directory */
1139       }
1140       else {
1141         if (open_trace_flag) {
1142           sprintf(log_line, "`%s' is a FILE, not a DIRECTORY ", fn);
1143           show_line(log_line, 0);
1144         }
1145         return 0;     /* false - its a file, not a dir */
1146       }
1147     }
1148     else {
1149 /*      _findfirst failed --- possible causes: missing *or* top-level */
1150 /*      crude check first top level directory ? - assume form `c:' */
1151       if (*(tmpfn+1) != ':' || *(tmpfn+2) != '\0') {
1152 /*        it is *not* top level and _findfirst failed - give up */
1153         if (open_trace_flag) {
1154           sprintf(log_line, "Directory `%s' does NOT exist ", fn);
1155           show_line(log_line, 0);
1156         }
1157         return 0;     /* false - it is not a directory */
1158       }
1159 /*      else drop through to old method */
1160 /*      else { */
1161 /*        top-level dir, so revert to the old method after all ... */
1162 /*        return dir_p_1 (fn); */ 
1163 /*        or try _findfirst after appending PATH_SEP and nul ? */
1164 /*      } */ /* drop through */
1165     }
1166   }
1167
1168 /* either: dropped through (top-level of driver) or dir_method is false */
1169 /* use the old method --- fopen of nul in supposed directory */
1170 /* NOTE: nul device exists in all dirs */ /* Possible OS/2 and NDOS problem */
1171   strcat (tmpfn, PATH_SEP_STRING "nul"); 
1172 /*  if ((test = fopen (tmpfn, "r")) == NULL) */
1173   if (share_flag == 0) test = fopen (tmpfn, "r");
1174   else test = _fsopen(tmpfn, "r", share_flag);    /* 1994/July/12 */
1175   if (test == NULL) {
1176     if (open_trace_flag) {
1177       sprintf(log_line, "Directory `%s' does NOT exist ", tmpfn);
1178       show_line(log_line, 0);
1179     }
1180     return 0;     /* false */
1181   }
1182   else {
1183     (void) fclose(test);    /* have to remember to close it again */
1184     if (open_trace_flag) {
1185       sprintf(log_line, "Directory `%s' DOES exist ", tmpfn);
1186       show_line(log_line, 0);
1187     }
1188     return 1;     /* true */
1189   }
1190 }
1191
1192 /* #pragma optimize ("g",)*/  /* try and avoid compiler bug here _dos_find */
1193 /* #pragma optimize ("g") */  /* try and avoid compiler bug here _dos_find */
1194 #pragma optimize ("", on)   /* 96/Sep/12 */
1195
1196 /* NOTE: calling _stat makes packed EXE file 3,400 bytes larger ! */
1197
1198 /* we don't want _fsopen instead of fopen here because only for dir\nul ??? */
1199
1200 /********************************************************************************/
1201
1202 /* pathsrch.c */
1203
1204 /* If FILENAME is absolute or explicitly relative (i.e., starts with
1205    `/', `./', or `../'), or if DIR_LIST is null, we return whether
1206    FILENAME is readable as-is.  Otherwise, we test if FILENAME is in any of
1207    the directories listed in DIR_LIST.  (The last entry of DIR_LIST must
1208    be null.)  We return the complete path if found, NULL else.
1209    
1210    In the interests of doing minimal work here, we assume that each
1211    element of DIR_LIST already ends with a `/'.
1212    
1213    DIR_LIST is most conveniently made by calling `initialize_path_list'.
1214    This is a separate routine because we allow recursive searching, and
1215    it may take some time to discover the list of directories.  
1216    We do not want to incur that overhead every time we want to look for
1217    a file.
1218    
1219    (Actually, `/' is not hardwired into this routine; we use PATH_SEP,
1220    defined above.)  */
1221
1222 /* xfind_path_filename is used now */
1223
1224 /* Called only from test_read_access(...) in ourpaths.c */
1225
1226 #ifdef BUILDNAMEDIRECT
1227
1228 /* this string allocation / concatination is silly - use fixed buffer! */
1229
1230 int xfind_path_filename (string buffer, string filename, string * dir_list)
1231 {
1232   string found_name = NULL;
1233
1234   if (buffer == filename) {
1235     show_line("buffer == filename\n", 1);
1236   }
1237
1238   *buffer = '\0';       /* "" in case we fail */
1239
1240   if (open_trace_flag) {
1241     sprintf(log_line, "Find path for `%s' ", filename);
1242     show_line(log_line, 0);
1243   }
1244
1245 /*  ignore current directory for TFM files ? */ /* 1994/Jan/24 */
1246   if (!current_tfm &&  strstr(filename, ".tfm") != NULL &&
1247       strcmp(*dir_list, "./") == 0) {
1248     if (open_trace_flag) {
1249       sprintf(log_line, "Ignoring `.' for %s ", filename);
1250       show_line(log_line, 0);
1251     }
1252     dir_list++;           /* step over first entry in dir list */
1253   }
1254
1255   if (trace_flag && open_trace_flag) {    /* debugging trace 1994/Jan/8 */
1256     char **pstrs;
1257     pstrs = dir_list;
1258     show_line("\n", 0);
1259     sprintf(log_line, "Find path for `%s' ", filename);
1260     show_line(log_line, 0);
1261     show_line("- IN: ", 0);
1262     while (*pstrs != NULL) {
1263       sprintf(log_line, "%s ", *pstrs);
1264       show_line(log_line, 0);
1265       pstrs++;
1266     }
1267     show_line("\n", 0);
1268   }
1269
1270 /*  Do this before testing for absolute-ness, as a leading ~ will be an
1271   absolute pathname.  */  /* forget this for DOS ! */
1272 #ifndef MSDOS
1273   filename = expand_tilde (filename);
1274 #endif
1275
1276 #ifdef MSDOS
1277 /*  is this always safe?  That is, is filename writable and its OK to modify */
1278 /*  unixify(filename);   */ /* done `in place' */
1279   if (deslash) unixify(filename);   /* made conditional 94/Feb/24 */
1280 #endif
1281
1282 /*  following addded in attempt to catch `nul' */   /* 94/Jan/6 bkph */
1283 /*  could also try and catch `nul.tex' first - but who cares about speed ? */
1284 /*  needed to add this since `access' gets the wrong answer */
1285 /*  that is, `nul' is a file that can be opened, but `access' says not so */
1286   if (strcmp(filename, "nul") == 0) strcpy(buffer, filename); 
1287 /*  If FILENAME is absolute or explicitly relative, or if DIR_LIST is
1288     null, only check if FILENAME is readable.  */
1289 /*  if (absolute_p (filename) || dir_list == NULL) */ /* 94/Jan/6 */
1290   else if (absolute_p (filename) || dir_list == NULL) {
1291     if (file_method)  found_name = file_p (filename);
1292     else found_name = readable (filename);
1293     if (found_name != NULL) strcpy(buffer, found_name);
1294     else *buffer = '\0';
1295   }
1296   else { /* Test if FILENAME is in any of the directories in DIR_LIST.  */
1297     char *s;
1298     int sourceflag;
1299     int firsttime=1;
1300     while (*dir_list != NULL)  {
1301 /* if item is current directory, look in source file directory first */
1302 /* provided usesourcedirectory flag is set and workingdirectory in use */
1303       s = *dir_list;
1304       sourceflag = 0;
1305       if (strcmp(s, "./") == 0) {
1306         if (firsttime && usesourcedirectory && workingdirectory &&
1307             source_direct != NULL && *source_direct != '\0') {
1308           s = source_direct;
1309           if (trace_flag) {
1310             sprintf(log_line, "Using %s dir %s %s\n", "source", s, "X");
1311             show_line(log_line, 0);
1312           }
1313           sourceflag = 1;     /* avoid increment of list below */
1314           firsttime = 0;      /* special stuff only first time */
1315         }
1316         else if (trace_flag) {
1317           sprintf(log_line, "Using %s dir %s %s\n", "current",  s, "X");
1318           show_line(log_line, 0);
1319         }
1320       }
1321       if (trace_flag) {
1322         sprintf(log_line, "XCONCAT %s %s in find_path_filename\n",
1323             s, filename);
1324         show_line(log_line, 0);
1325       }
1326 /*      filename = concat (*dir_list, save_filename); */
1327       (void) xconcat (buffer, s, filename);
1328       if (file_method) found_name = file_p (buffer);  /* new way */
1329       else found_name = readable (buffer);      /* slow old way */
1330       if (found_name == NULL) {
1331         *buffer = '\0';
1332         if (! sourceflag)   /* 98/Sep/29 repeat in current dir */
1333           dir_list++;       /* try next */
1334       }
1335       else {
1336         if (found_name != buffer)
1337           strcpy(buffer, found_name);       /* success */
1338         break;
1339       }
1340     }
1341   }
1342   return (*buffer != '\0');     /* true if not empty string */
1343 }
1344
1345 #else
1346
1347 /* We are dealing with a C string here for filename presumably ... */
1348
1349 /* Also, this returns a string that was allocated --- */
1350 /* unless it happens to equal the filename sent in ... */
1351 /* this needs to be freed later - unless it happens to be ... */
1352
1353 string find_path_filename (string filename,  string * dir_list)
1354 {
1355   string found_name = NULL;
1356   
1357   if (open_trace_flag) {
1358 //    printf("Find path for `%s' ", filename);
1359     sprintf(log_line, "Find path for `%s' ", filename);
1360     show_line(log_line, 0);
1361   }
1362
1363 /*  ignore current directory for TFM files ? */ /* 1994/Jan/24 */
1364   if (!current_tfm &&
1365       strstr(filename, ".tfm") != NULL &&
1366         strcmp(*dir_list, "./") == 0) {
1367     if (open_trace_flag) {
1368       sprintf(log_line, "Ignoring `.' for %s ", filename);
1369       show_line(log_line, 0);
1370     }
1371     dir_list++;           /* step over first entry in dir list */
1372   }
1373
1374   if (trace_flag && open_trace_flag) {    /* debugging trace 1994/Jan/8 */
1375     char **pstrs;
1376     pstrs = dir_list;
1377     show_line("\n", 0);
1378     sprintf(log_line, "Find path for `%s' ", filename);
1379     show_line(log_line, 0);
1380     show_line("- IN: ", 0);
1381     while (*pstrs != NULL) {
1382 //      printf("%s ", *pstrs);
1383       sprintf(log_line, "%s ", *pstrs);
1384       show_line(log_line, 0);
1385       pstrs++;
1386     }
1387     show_line("\n", 0);
1388   }
1389
1390 /*  Do this before testing for absolute-ness, as a leading ~ will be an
1391     absolute pathname.  */  /* forget this for DOS ! */
1392 #ifndef MSDOS
1393   filename = expand_tilde (filename);
1394 #endif
1395
1396 #ifdef MSDOS
1397 /*  is this always safe?  That is, is filename writable and its OK to modify */
1398 /*  unixify(filename);   */ /* done `in place' */
1399   if (deslash) unixify(filename);    /* made conditional 94/Feb/24 */
1400 #endif
1401 /*  following addded in attempt to catch `nul' */   /* 94/Jan/6 bkph */
1402 /*  could also try and catch `nul.tex' first - but who cares about speed ? */
1403 /*  needed to add this since `access' gets the wrong answer */
1404 /*  that is, `nul' is a file that can be opened, but `access' says not so */
1405   if (strcmp(filename, "nul") == 0) found_name = filename; 
1406 /*  If FILENAME is absolute or explicitly relative, or if DIR_LIST is
1407     null, only check if FILENAME is readable.  */
1408 /*  if (absolute_p (filename) || dir_list == NULL) */ /* 94/Jan/6 */
1409   else if (absolute_p (filename) || dir_list == NULL) {
1410     if (file_method)  found_name = file_p (filename); /* new way 94/Feb/13 */
1411     else found_name = readable (filename);        /* slow old way */
1412   }
1413   else { /* Test if FILENAME is in any of the directories in DIR_LIST.  */
1414     string save_filename = filename;
1415     char *s;
1416     int sourceflag;
1417     int firsttime=1;
1418     while (*dir_list != NULL)  {
1419 /* if item is current directory, look in source file directory first */
1420 /* provided usesourcedirectory flag is set and workingdirectory in use */
1421       s = *dir_list;
1422       sourceflag = 0;
1423       if (strcmp(s, "./") == 0) {
1424         if (firsttime && usesourcedirectory && workingdirectory &&
1425           source_direct != NULL && *source_direct != '\0') {
1426           s = source_direct;
1427           if (trace_flag) {
1428             sprintf(log_line, "Using %s dir %s %s\n", "source", s, "F");
1429             show_line(log_line, 0);
1430           }
1431           sourceflag = 1;     /* avoid increment of list below */
1432           firsttime = 0;      /* special stuff only first time */
1433         }
1434         else if (trace_flag) {
1435           sprintf(log_line, "Using %s dir %s %s\n", "current", s, "F");
1436           show_line(log_line, 0);
1437         }
1438       }
1439       if (trace_flag) {
1440         sprintf(log_line, "CONCAT %s %s in find_path_filename\n",
1441                   s, save_filename); /* 1996/Jan/20 */
1442         show_line(log_line, 0);
1443       }
1444       filename = concat (s, save_filename);
1445 /*          found_name = readable (filename); */
1446       if (file_method) found_name = file_p (filename);  /* new way */
1447       else found_name = readable (filename);      /* slow old way */
1448       if (found_name == NULL)  {
1449         free (filename);    /* if this is not it, free it again */
1450         if (! sourceflag)   /* 98/Sep/29 repeat in current dir */
1451           dir_list++;
1452       }
1453       else {
1454         if (found_name != filename)
1455           free (filename);  /* don't free if is the one passed in */
1456         break;
1457       }
1458     }
1459   }
1460   return found_name;        /* return the allocated name - free later */
1461 }
1462 #endif
1463
1464 /* If NAME is readable, return it.  If the error is ENAMETOOLONG,
1465    truncate any too-long path components and return the result (unless
1466    there were no too-long components, i.e., a overall too-long name
1467    caused the error, in which case return NULL).  On any other error,
1468    return NULL.
1469    
1470    POSIX invented this brain-damage of not necessarily truncating
1471    pathname components; the system's behavior is defined by the value of
1472    the symbol _POSIX_NO_TRUNC, but you can't change it dynamically!  */
1473
1474 /* Using access (and dir_p)  is considerably slower than using dosfind */
1475 /* NOTE: this is only called from find_path_file,
1476    and then ONLY if file_method is false (-G) */
1477
1478 /* returns NULL or the file name passed in ??? */
1479
1480 /* static string readable (name) */
1481 string readable (string name)
1482 {
1483   string ret;
1484
1485   if (open_trace_flag) {
1486     sprintf(log_line, "is %s readable? ", name);
1487     show_line(log_line, 0);
1488   }
1489
1490 /*  Check first whether we have read access, then */
1491 /*  need to test if directory, since access always says OK for directory */
1492 /*  BUT: readable is called only from find_path_file */
1493 /*  So we never call this with directory, so why waste time ? bkph */
1494 /*  BUT: can be caught out by idiot with a directory called myfile.tex ? */
1495   
1496   if (_access (name, R_OK) == 0)  {
1497     if (test_dir_access) {    /* check only if we are asked to ... */
1498       if (dir_p (name)) {
1499         if (open_trace_flag) {
1500           sprintf(log_line, "tested read access of directory `%s' ", name);
1501           show_line(log_line, 0);
1502         }
1503         ret = NULL;
1504       }
1505       else ret = name;
1506     }
1507     else ret = name;
1508   }
1509 /*  if (_access (name, R_OK) == 0 && !dir_p (name))   ret = name; */
1510 #ifdef ENAMETOOLONG
1511   else if (errno == ENAMETOOLONG) { 
1512     ret = truncate_pathname (name);
1513 /* Perhaps some other error will occur with the truncated name, so
1514          let's call access again.  */
1515     if (!(_access (ret, R_OK) == 0 && !dir_p (ret))) { /* Failed.  */
1516       free (ret);
1517       ret = NULL;
1518     }
1519   }
1520 #endif
1521   else if (errno == EACCES) {
1522     if (trace_flag) show_line("Access denied!\n", 0);
1523     ret = NULL;
1524   }
1525   else if (errno == ENOENT) {
1526     if (trace_flag) show_line("File or path name not found!\n", 1);
1527     ret = NULL;
1528   }
1529   else {
1530     if (trace_flag) {
1531       sprintf(log_line, "Unknown access error %d!\n", errno);
1532       show_line(log_line, 0);
1533     }
1534     ret = NULL;
1535   }
1536   return ret;
1537 }
1538
1539 #ifdef ENAMETOOLONG
1540 /* Truncate any too-long path components in NAME, returning the result.  */
1541
1542 string truncate_pathname (string name)
1543 {
1544   unsigned c_len = 0;       /* Length of current component.  */
1545   unsigned ret_len = 0;   /* Length of constructed result.  */
1546   string ret = (string) xmalloc (PATH_MAX + 1);
1547
1548   for (; *name; name++)
1549     {
1550       if (*name == PATH_SEP)      /* not in DOS */
1551         { /* At a directory delimiter, reset component length.  */
1552           c_len = 0;
1553         }
1554       else if (c_len > NAME_MAX)
1555         { /* If past the max for a component, ignore this character.  */
1556           continue;
1557         }
1558
1559       /* If we've already copied the max, give up.  */
1560       if (ret_len == PATH_MAX)
1561         {
1562           free (ret);
1563           return NULL;
1564         }
1565
1566       /* Copy this character.  */
1567       ret[ret_len++] = *name;
1568       c_len++;
1569     }
1570   ret[ret_len] = 0;
1571
1572   return ret;
1573 }
1574 #endif  /* end of ifdef ENAMETOOLONG */
1575
1576
1577 /* Return true if FILENAME is absolute or explicitly relative, else false.  */
1578 /* Absolute: in DOS name starts with PATH_SEP, or with DRIVE letter and colon */
1579 /* Explicitly relative: starts with ./ or ../ */
1580 /* kpathsea/absolute.c */
1581 // static bool absolute_p (string filename) {
1582 bool absolute_p (string filename)
1583 {
1584   bool absolute;
1585   bool explicit_relative;
1586
1587 #ifdef MSDOS
1588 /*  absolute = (*filename == PATH_SEP) */     /* 1994/Mar/1 */
1589   absolute = (*filename == PATH_SEP || *filename == '\\')
1590                       || ((filename[1] == ':') && ISALPHA (*filename));
1591 /*                      || ISALPHA (*filename) && filename[1] == ':'; */
1592 #else
1593   absolute = (*filename == PATH_SEP);
1594 #endif
1595   if (absolute) return true;      /* don't waste any more time */
1596
1597 #ifdef MSDOS
1598   explicit_relative = (*filename == '.'
1599     && ((filename[1] == PATH_SEP || filename[1] == '\\')
1600            || (filename[1] == '.' &&
1601          (filename[2] == PATH_SEP || filename[2] == '\\'))));
1602 #else
1603   explicit_relative = (*filename == '.'  && (filename[1] == PATH_SEP
1604            || (filename[1] == '.' && filename[2] == PATH_SEP))); 
1605 #endif
1606
1607   return explicit_relative;
1608 /*  return absolute || explicit_relative; */ /* slight rewrite 1994/Feb/13 */
1609 }
1610
1611 #ifdef MSDOS
1612 /*  note: this strips off trailing white space in actual environment var ... */
1613 void striptrailing (string env_value, string env_name, string default_path)
1614 {
1615   char *s;
1616   if (env_name == NULL) {         /* 1994/Feb/24 */
1617     if (trace_flag) {
1618       sprintf(log_line, "WARNING: no env_name noted, using default %s\n",
1619         default_path);
1620       show_line(log_line, 0);
1621     }
1622     return;
1623   }
1624   if (env_value == NULL) {
1625     if (trace_flag) {
1626       sprintf(log_line, "WARNING: %s not defined in environment, using default %s\n",
1627         env_name, default_path);
1628       show_line(log_line, 0);
1629     }
1630     return;
1631   }
1632   if (strlen(env_value) == 0) return;
1633   s = env_value + strlen(env_value) - 1;
1634 /*  while (*s <= ' ') *s-- = '\0'; */
1635   while (s >= env_value && *s <= ' ')*s-- = '\0';   /* 94/Feb/24 */
1636 }
1637 #endif
1638
1639 /* convert /! and /!! to / and // 97/Mar/22 */
1640
1641 #ifdef MSDOS
1642 /* 97/Mar/22 */
1643 void convertexclam (string env_value)
1644 {
1645   char *s;
1646   if (env_value == NULL) return;
1647   s = env_value;
1648   if (strchr(s, '!') == NULL) return;
1649   while ((s = strchr(s, '!')) != NULL)
1650   {
1651     if (*(s+1) == '!')
1652     {  /* double !! */
1653       if (*(s+2) == PATH_DELIMITER || *(s+2) == '\0')
1654       {
1655         if (s > env_value && *(s-1) == PATH_SEP)
1656         {
1657           *s = PATH_SEP;    /* convert the first ! */
1658           strcpy(s+1, s+2); /* flush the second ! */
1659         }
1660       }
1661     }
1662     else
1663     {    /* single ! */  /* assume already unixified */
1664       if (*(s+1) == PATH_DELIMITER || *(s+1) == '\0')
1665       {
1666         if (s > env_value && *(s-1) == PATH_SEP)
1667           strcpy(s, s+1); /* just flush the ! */
1668       }
1669     }
1670     s++;
1671   }
1672   if (trace_flag)
1673   {
1674     sprintf(log_line,"Now is %s\n", env_value);
1675     show_line(log_line, 0);
1676   }
1677 }
1678 #endif
1679
1680 /* Return a NULL-terminated array of directory names, each name ending
1681    with PATH_SEP, created by parsing the PATH_DELIMITER-separated list
1682    in the value of the environment variable ENV_NAME, or DEFAULT_PATH if
1683    the env var is not set.
1684    
1685    A leading or trailing PATH_DELIMITER in the value of ENV_NAME is replaced
1686    by DEFAULT_PATH.
1687    
1688    Any element of the path that ends with double PATH_SEP characters
1689    (e.g., `foo//') is replaced by all its subdirectories.
1690
1691    If ENV_NAME is null, only parse DEFAULT_PATH.  If both are null, do
1692    nothing and return NULL.  */
1693
1694 string * initialize_path_list (string env_name,  string default_path)
1695 {
1696   string dir, path;
1697   string *dir_list;
1698   unsigned dir_count = 0;
1699   string env_value;
1700   string orig_path;
1701   struct _finddata_t findt;
1702 /*  _finddata_t structure *can* be reused, unlike _find_t 95/Jan/31 */
1703 /*  so save on stack space, by having one copy here, not one per expand_subdir*/
1704
1705 /*  env_value = env_name ?  getenv (env_name)  : NULL; */
1706   env_value = env_name ?  grabenv (env_name)  : NULL; /* 1994/May/19 */
1707
1708 /*  need to convert \ to / as soon as possible to avoid confusion */
1709 /*  we may be modifying DOS environment variable here ... is it always safe ? */
1710 #ifdef MSDOS
1711   if (deslash) unixify (env_value);       /* 1994/Feb/24 */
1712 #endif
1713   if (trace_flag) {
1714     if (env_name) {     /* only if env_name is non-null 94/Feb/24 */
1715       sprintf(log_line, "\nSet %s=", env_name);
1716       show_line(log_line, 0);
1717       if (env_value) {  /* only if env_name value is set */
1718         show_line(env_value, 0);
1719       }
1720       show_line("\n", 0);
1721     }
1722   }
1723 #ifdef MSDOS
1724 /*  strip off trailing white space which would confuse things - bkph  */
1725   striptrailing (env_value, env_name, default_path);
1726   convertexclam (env_value);              /* 97/Mar/22 */
1727 #endif
1728   orig_path = expand_default (env_value, default_path);
1729
1730   if (orig_path == NULL || *orig_path == 0)  return NULL;
1731
1732 /*  need to convert \ to / as soon as possible to avoid confusion */
1733 #ifdef MSDOS
1734   if (deslash)  unixify (orig_path);  /* redundant ? */
1735 #endif
1736
1737 /*  If we've already seen this PATH_DELIMITER-separated list, then just get
1738     it back instead of going back to the filesystem.  */
1739   dir_list = find_dir_list (orig_path);
1740   if (dir_list != NULL) return dir_list;
1741   
1742 /*  Be sure `path' is in writable memory.  */ /* if not, copy it */
1743   path = (orig_path == env_value || orig_path == default_path
1744           ? xstrdup (orig_path) : orig_path); 
1745
1746 /*  Find each element in the path in turn.  */
1747 #ifdef MSDOS
1748 /*  if (!switchflag) */ 
1749   if (current_flag) {   /* suppress adding current directory - debugging */
1750     if (trace_flag) {
1751       sprintf(log_line, "Adding directory `%s'\n", "."); /* 95/Jan/24 */
1752       show_line(log_line, 0);
1753     }
1754     add_directory(&dir_list, &dir_count, ".");
1755   }
1756 #endif
1757   for (dir = strtok (path, PATH_DELIMITER_STRING); dir != NULL;
1758     dir = strtok (NULL, PATH_DELIMITER_STRING)) {
1759     int len;
1760
1761 // #ifdef MYDEBUG
1762   if (trace_flag) {
1763     sprintf(log_line, "dir %s\n", dir);
1764     show_line(log_line, 0);
1765   }
1766 // #endif
1767       /* If the path starts with ~ or ~user, expand it.  Do this
1768          before calling `expand_subdir' or `add_directory', so that
1769          1) we don't expand the same ~ for every subdirectory; and 
1770          2) pathnames in `expand_subdir' don't have a `~' in them
1771             (since the system won't grok `~/foo' as a directory).  */
1772 #ifndef MSDOS
1773     dir = expand_tilde (dir);
1774 #endif
1775     len = strlen (dir);
1776
1777       /* If `dir' is the empty string, ignore it.  */
1778     if (len == 0)  continue;
1779
1780       /* If `dir' ends in double PATH_SEP, do subdirectories (and remove
1781          the second PATH_SEP, so the final pathnames we return don't look
1782          like foo//bar).  Because we obviously want to do subdirectories
1783          of `dir', we don't check if it is a leaf.  This means that if
1784          `dir' is `foo//', and `foo' contains only symlinks (so our leaf
1785          test below would be true), the symlinks are chased.  */
1786
1787 /* modified to treat single PATH_SEP as expand subdirs without recursion */
1788 /* modified to treat double PATH_SEP as expand subdirs *with*  recursion */
1789
1790 #ifdef MSDOS
1791     if (len > 2 &&                /* 1994/Mar/1 */
1792       (dir[len - 1] == PATH_SEP || dir[len - 1] == '\\')
1793         && (dir[len - 2] == PATH_SEP || dir[len - 2] == '\\'))
1794 #else
1795     if (len > 2 && dir[len - 1] == PATH_SEP && dir[len - 2] == PATH_SEP) 
1796 #endif
1797     {
1798       if (open_trace_flag) {
1799         sprintf(log_line, "Double backslash on `%s' ", dir);  /* bkph */
1800         show_line(log_line, 0);
1801       }
1802
1803       dir[len - 1] = 0;
1804       if (dir_p (dir)) {
1805         if (trace_flag) {
1806           sprintf(log_line, "Adding directory `%s'\n", dir);
1807           show_line(log_line, 0);
1808         }
1809         add_directory (&dir_list, &dir_count, dir);
1810 /* local variable 'findt' used without having been initialized ? &findt ? */
1811         expand_subdir (&dir_list, &dir_count, dir, findt, 1);  /* 95/Jan/31 */
1812       }
1813     }
1814 /* following is new to find only directories to one level 1994/Jan/24 */
1815 #ifdef MSDOS
1816     else if (len > 1 &&         /* 1994/Mar/1 */
1817       (dir[len - 1] == PATH_SEP || dir[len - 1] == '\\'))
1818 #else
1819     else if (len > 1 && dir[len - 1] == PATH_SEP) 
1820 #endif
1821         {
1822       if (open_trace_flag) {
1823         sprintf(log_line, "Single backslash on `%s' ", dir);  /* bkph */
1824         show_line(log_line, 0);
1825       }
1826
1827 /*      dir[len - 1] = 0; */
1828       if (dir_p (dir)) {
1829         if (trace_flag) {
1830           sprintf(log_line, "Adding directory `%s'\n", dir);
1831           show_line(log_line, 0);
1832         }
1833         add_directory (&dir_list, &dir_count, dir);
1834         expand_subdir (&dir_list, &dir_count, dir,
1835             findt, 0); /* 95/Jan/31 */
1836       }
1837     }
1838       else { /* Don't bother to add the directory if it doesn't exist.  */
1839       if (dir_p (dir)) {
1840         if (trace_flag) {
1841           sprintf(log_line, "Adding directory `%s'\n", dir);
1842           show_line(log_line, 0);
1843         }
1844         add_directory (&dir_list, &dir_count, dir);
1845       }
1846     }
1847   }
1848   
1849 // #ifdef MYDEBUG
1850   if (trace_flag) {
1851     show_line("Adding terminating null\n", 0);
1852   }
1853 // #endif
1854
1855 /*  Add the terminating null entry to `dir_list'.  */
1856   dir_count++;
1857   XRETALLOC (dir_list, dir_count, string);
1858   dir_list[dir_count - 1] = NULL;
1859   
1860 /*  Save the directory list we just found.  */
1861   save_dir_list (orig_path, dir_list);
1862
1863   return dir_list;
1864 }
1865
1866 /* Subroutines for `initialize_path_list'.  */
1867
1868 /* Add a newly-allocated copy of DIR to the end of the array pointed to
1869    by DIR_LIST_PTR. Increment DIR_COUNT_PTR.  Append a `/' to DIR if
1870    necessary.  We assume DIR is a directory, to avoid an unnecessary
1871    call to `stat'.  */
1872
1873 void add_directory (string **dir_list_ptr, unsigned *dir_count_ptr, string dir)
1874 {
1875   if (dir == NULL) return;        /* paranoia 1995/Jan/24 */
1876   /* If `dir' does not end with a `/', add it.  We can't just
1877      write it in place, since that would overwrite the null that
1878      strtok may have put in.  So we ALWAYS make a copy of DIR.  */
1879 #ifdef MSDOS
1880   dir = (dir[strlen (dir) - 1] == PATH_SEP ||   /* 1994/Mar/1 */
1881       dir[strlen (dir) - 1] == '\\') ?
1882         xstrdup (dir) : concat (dir, PATH_SEP_STRING);
1883 #else
1884   dir = (dir[strlen (dir) - 1] == PATH_SEP ? xstrdup (dir)
1885          : concat (dir, PATH_SEP_STRING)); 
1886 #endif
1887 #ifdef MSDOS
1888   if (deslash) unixify (dir);     /* redundant ? bkph */ 
1889 #endif
1890
1891 // #ifdef MYDEBUG
1892   if (trace_flag) {
1893     sprintf(log_line, "Adding directory `%s'\n", dir);
1894     show_line(log_line, 0);
1895   }
1896 // #else
1897 //     if (open_trace_flag) {
1898 //    sprintf(log_line, "Adding directory `%s' ", dir);
1899 //    show_line(log_line, 0);
1900 //  }
1901 // #endif
1902
1903   /* Add `dir' to the list of the directories.  */
1904   (*dir_count_ptr)++;
1905   XRETALLOC (*dir_list_ptr, *dir_count_ptr, string);
1906   (*dir_list_ptr)[*dir_count_ptr - 1] = dir;
1907 }
1908 /* 1994/Jan/25 */
1909 void lowercase (char *s)
1910 {
1911   while (*s)
1912     *s++ = (char) tolower(*s);
1913 }
1914
1915
1916 /* These routines, while not strictly needed to be exported, are
1917    plausibly useful to be called by outsiders.  */
1918
1919 /* Replace a leading or trailing PATH_DELIMITER in ENV_PATH with
1920    DEFAULT_PATH.  If neither is present, return ENV_PATH if that is 
1921    non-null, else DEFAULT_PATH.  */
1922
1923 string  expand_default (string env_path, string default_path)
1924 {
1925   string expansion;
1926   
1927   if (env_path == NULL) expansion = default_path;
1928   else if (*env_path == PATH_DELIMITER)
1929     expansion = concat (default_path, env_path);
1930   else if (env_path[strlen (env_path) - 1] == PATH_DELIMITER)
1931     expansion = concat (env_path, default_path);
1932   else expansion = env_path;
1933   
1934   if (trace_flag) {               /* 1994/Jan/8 */
1935     if (env_path == NULL) {
1936       sprintf(log_line, "Using the default %s\n", expansion);
1937       show_line(log_line, 0);
1938     }
1939     else if (expansion == env_path) {
1940       sprintf(log_line, "Using %s (default was %s)\n", expansion, default_path);
1941       show_line(log_line, 0);
1942     }
1943     else {                /* expansion != env_path */
1944       sprintf(log_line, "Expanded %s (default was %s) to %s\n",
1945         env_path, default_path, expansion);
1946       show_line(log_line, 0);
1947     }
1948   }
1949   return expansion;
1950 }
1951
1952
1953 #ifndef MSDOS
1954 /* Expand a leading ~ or ~user, Unix-style, unless we are some weirdo
1955    operating system.  */
1956
1957 string expand_tilde (string name)
1958 {
1959 #if defined (MSDOS) || defined (VMS) || defined (VMCMS)
1960   return name;
1961 #else
1962   string expansion;
1963   string home;
1964   
1965   /* If no leading tilde, do nothing.  */
1966   if (*name != '~')
1967     expansion = name;
1968   
1969   /* If `~' or `~/', use $HOME if it exists, or `.' if it doesn't.  */
1970   else if (name[1] == PATH_SEP || name[1] == 0)     /* not in DOS */
1971     {
1972       home = getenv ("HOME");             /* not in DOS */
1973       if (home == NULL)
1974         home = ".";
1975         
1976       expansion
1977         = name[1] == 0 ? home : concat3 (home, PATH_SEP_STRING, name + 2);
1978     }
1979   
1980   /* If `~user' or `~user/', look up user in the passwd database.  */
1981   else
1982     {
1983       struct passwd *p;
1984       string user;
1985       unsigned c = 2;
1986       while (name[c] != PATH_SEP && name[c] != 0) /* not in DOS */
1987         c++;
1988       
1989       user = (string) xmalloc (c);
1990       strncpy (user, name + 1, c - 1);
1991       user[c - 1] = 0;
1992       
1993       /* We only need the cast here for those (old deficient) systems
1994          which do not declare `getpwnam' in <pwd.h>.  */
1995       p = (struct passwd *) getpwnam (user);
1996       free (user);
1997       /* If no such user, just use `.'.  */
1998       home = p == NULL ? "." : p->pw_dir;
1999       
2000       expansion = name[c] == 0 ? home : concat (home, name + c);
2001     }
2002   
2003   return expansion;
2004 #endif /* not (DOS or VMS or VM/CMS) */
2005 }
2006 #endif
2007
2008 // structure used for manipulation dir lists
2009
2010 typedef struct {
2011   string path;
2012   string *dir_list;
2013 } saved_path_entry;
2014
2015 /* Routines to save and retrieve a directory list keyed by the original
2016    PATH_DELIMITER-separated path.  This is useful because 1) it can take a
2017    significant amount of time to discover all the subdirectories of a
2018    given directory, and 2) many paths all have the same basic default,
2019    and thus would recompute the directory list.  */
2020
2021 static saved_path_entry *saved_paths = NULL;
2022 static unsigned saved_paths_length = 0;
2023
2024 /* We implement the data structure as a simple linear list, since it's
2025    unlikely to ever be more than a dozen or so elements long.  We don't
2026    bother to check here if PATH has already been saved; we always add it
2027    to our list.  */
2028
2029 void save_dir_list (string path,  string *dir_list)
2030 {
2031 //  saved_paths_length++;
2032   XRETALLOC (saved_paths, saved_paths_length+1, saved_path_entry);
2033   saved_paths[saved_paths_length].path = path;
2034   saved_paths[saved_paths_length].dir_list = dir_list;
2035   saved_paths_length++;
2036 }
2037
2038 /* When we retrieve, just check the list in order.  */
2039
2040 string *find_dir_list (string path)
2041 {
2042   unsigned p;
2043   
2044 // #ifdef MYDEBUG
2045   if (trace_flag) {
2046     sprintf(log_line, "Find Dir List for path: %s\n", path);
2047     show_line(log_line, 0);
2048   }
2049 // #endif
2050
2051   for (p = 0; p < saved_paths_length; p++) {
2052     if (strcmp (saved_paths[p].path, path) == 0)
2053       return saved_paths[p].dir_list;
2054   }
2055   return NULL;
2056 }
2057
2058 /* Unixify filename and path (turn \ into /) --- assumes null terminated */
2059
2060 char *unixify (char * t)
2061 {
2062   char * s = t;
2063   if (s == NULL) return s;    /* paranoia -- 1993/Apr/10 */
2064 #ifdef MSDOS
2065   if (t != '\0') {
2066     while (*s != '\0') {        /* paranoia -- 1997/Oct/23 */
2067 /*      if (*s == '\\') *s = '/'; */
2068       if (*s == '\\') *s = PATH_SEP;
2069       s++;
2070     }       /* endwhile */
2071   }
2072 // #ifdef MYDEBUG
2073   if (trace_flag) {
2074     sprintf(log_line, "Unixified name: %s\n", t);
2075     show_line(log_line, 0);
2076   }
2077 // #endif
2078 #endif /* DOS */
2079   return t;
2080 }
2081
2082 /****************************************************************************/
2083
2084 /* moved here to avoid problems with pragma */
2085
2086 /* struct _finddata_t findt; */ /* make global, can be reused unlike _find_t */
2087                                 /* avoids heavy stack usage in tree search */
2088                                 /* but ties up some global fixed space ... */
2089
2090 #pragma optimize ("g", off)   /* try and avoid compiler bug here _dos_find */
2091
2092 /* Add DIRNAME to DIR_LIST and look for subdirectories, possibly recursively.
2093    We assume DIRNAME is the name of a directory.  */
2094
2095 /* NOTE: _dos_find... prevents running under Windows NT as console app ??? */
2096 /* Yes, so lets flush it! use _findfirst, _findnext, _findclose instead */
2097
2098 /* called only from initialize_path_list  (and recursively) */
2099 /* kpathsea/elt-dirs.c */
2100 /* renamed to do_subdir */
2101 void expand_subdir (string **dir_list_ptr, unsigned *dir_count_ptr, string dirname,
2102           struct _finddata_t findt, integer recurseflag)
2103 {
2104 #ifdef MSDOS
2105 /*  struct _finddata_t findt; */
2106   long hFind;
2107   int ret;
2108   int len;
2109 /*  char buffer[PATH_MAX]; */   /* pretty long? potential recursion problem? */
2110   char buffer[FILENAME_MAX];  /* this is DOS and Windows NT after all ... */
2111   char *potential;
2112 #endif  /* DOS */
2113
2114   if (trace_flag) {
2115     sprintf(log_line, "\nExpanding sub dir %s ", dirname);
2116     show_line(log_line, 0);
2117   }
2118
2119 #ifdef MSDOS
2120   strcpy(buffer, dirname);
2121   len = strlen(dirname);
2122
2123 #ifdef MSDOS
2124 /*  if (buffer[len-1] == PATH_SEP) strcat(buffer, "*.*"); */
2125   if (buffer[len-1] == PATH_SEP || buffer[len-1] == '\\')
2126     strcat(buffer, "*.*");            /* 1994/Mar/1 */
2127   else strcat(buffer, PATH_SEP_STRING "*.*");
2128 #else
2129   if (buffer[len-1] == PATH_SEP) strcat(buffer, "*");
2130   else strcat(buffer, PATH_SEP_STRING "*");
2131 #endif  /* MSDOS */
2132
2133 /*  Note: the _A_SUBDIR means we get ordinary files PLUS sub-directories */
2134   if (open_trace_flag)  {
2135     sprintf(log_line, "\nDIRNAME `%s' ", dirname);
2136     show_line(log_line, 0);
2137   }
2138 /*  we'll need to step over `.' and `..' up front of directory list */
2139   hFind = _findfirst(buffer, &findt);
2140   if (hFind > 0) ret = 0;
2141   else ret = -1;
2142 /*  _dos_findnext( &findt ); */
2143 /*  while(_dos_findnext(&findt)== 0)  { */
2144   while (ret == 0)  {
2145 /*    if (open_trace_flag) */
2146     if (open_trace_flag && trace_flag) {
2147       sprintf(log_line, "NEXT `%s' (%0x) ", findt.name, findt.attrib);
2148       show_line(log_line, 0);
2149     }
2150 /*    if (strchr(findt.name, '.') != NULL) continue; *//* not needed */
2151     if (findt.name[0] != '.' &&   /* ignore "." and ".." */
2152       findt.attrib & _A_SUBDIR){  /* only look at SUBDIRs */
2153       if (open_trace_flag)  {
2154         sprintf(log_line, "\nDIRNAME `%s' ", dirname);
2155         show_line(log_line, 0);
2156       }
2157 #ifdef MSDOS
2158       potential = concat3(dirname,
2159         (dirname[len-1] == PATH_SEP || dirname[len-1] == '\\')
2160           ? "" : PATH_SEP_STRING, findt.name);
2161 #else
2162       potential = concat3(dirname, dirname[len-1] == PATH_SEP 
2163         ? "" : PATH_SEP_STRING, findt.name);
2164 #endif  /* DOS */
2165       lowercase (potential);          /* make look nicer ? */
2166       if (open_trace_flag) {
2167         sprintf(log_line, "POTENTIAL `%s' ", potential);
2168         show_line(log_line, 0);
2169       }
2170       if (trace_flag) {
2171         sprintf(log_line, "Adding directory `%s'\n", potential); /* 95/Jan/24 */
2172         show_line(log_line, 0);
2173       }
2174       add_directory(dir_list_ptr, dir_count_ptr, potential);
2175       if (recurseflag) 
2176         expand_subdir(dir_list_ptr, dir_count_ptr,
2177             potential, findt, 1);  /* 95/Jan/31 */
2178       free(potential);
2179     }     /* end of findt.attrib & _A_SUBDIR != 0 */
2180     ret = _findnext(hFind, &findt);
2181   }
2182 #ifdef MSDOS
2183   if (hFind > 0) _findclose (hFind);
2184 #endif
2185
2186 #ifndef MSDOS
2187   _dos_findclose(&findt);
2188 #endif
2189
2190 #else  /* end of MSDOS (way up there) */
2191
2192 /*  This is how we do this if we are NOT using DOS */
2193   DIR *dir;
2194   struct dirent *e;
2195   unsigned length;
2196   char potential[PATH_MAX];
2197   struct _stat st;
2198   
2199    /* We will be looking at its contents.  */
2200   dir = opendir (dirname);
2201   if (dir == NULL)
2202     return;
2203   
2204   /* Compute the length of DIRNAME, since it's loop-invariant.  */
2205   length = strlen (dirname);
2206
2207   /* Construct the part of the pathname that doesn't change.  */
2208   strcpy (potential, dirname);
2209   if (potential[length - 1] != PATH_SEP)  /* not in DOS */
2210     {
2211       potential[length] = PATH_SEP;
2212       potential[length + 1] = 0;
2213       length++;
2214     }
2215   
2216 /* about to use _stat --- shouldn't get here when using MSDOS anyway */
2217
2218   while ((e = readdir (dir)) != NULL)
2219     { /* If it's . or .., never mind.  */
2220       if (!(e->d_name[0] == '.'
2221             && (e->d_name[1] == 0
2222                 || (e->d_name[1] == '.' && e->d_name[2] == 0))))
2223         { /* If it's not a directory, we will skip it on the
2224              recursive call.  */
2225           strcat (potential, e->d_name);
2226
2227           /* If we can't _stat it, or if it isn't a directory, continue.  */
2228           if (_stat (potential, &st) == 0 && S_ISDIR (st.st_mode))
2229             { /* It's a subdirectory; add `potential' to the list.  */
2230         if (trace_flag) {
2231           sprintf(log_line, "Adding directory `%s'\n", potential); /* 95/Jan/24 */
2232           show_line(log_line, 0);
2233         }
2234               add_directory (dir_list_ptr, dir_count_ptr, potential);
2235
2236               /* If it's not a leaf, quit.  Assume that leaf
2237                  directories have two links (one for . and one for ..).
2238                  This means that symbolic links to directories do not affect
2239                  the leaf-ness.  This is arguably wrong, but the only
2240                  alternative I know of is to _stat every entry in the
2241                  directory, and that is unacceptably slow.  */
2242               if (st.st_nlink > 2)
2243                 { /* All criteria are met; find subdirectories.  */
2244                   expand_subdir (dir_list_ptr, dir_count_ptr, potential,
2245             findt, 1);  /* 95/Jan/31 */
2246                 }
2247             }
2248
2249           /* ``Remove'' the directory entry name.  */
2250           potential[length] = 0;
2251         }
2252     }
2253   
2254   closedir (dir);
2255 #endif  /* end of *not* DOS case */
2256 }
2257
2258 // #pragma optimize ("", on)    /* 96/Sep/12 */
2259
2260 /************************************************************************/
2261
2262 #define ARGSEP '='
2263
2264 /* This version of `getopt' appears to the caller like standard Unix `getopt'
2265    but it behaves differently for the user, since it allows the user
2266    to intersperse the options with the other arguments.
2267
2268    As `getopt' works, it permutes the elements of ARGV so that,
2269    when it is done, all the options precede everything else.  Thus
2270    all application programs are extended to handle flexible argument
2271    order. */
2272
2273 /* For communication from `getopt' to the caller.
2274    When `getopt' finds an option that takes an argument,
2275    the argument value is returned here.
2276    Also, when `ordering' is RETURN_IN_ORDER,
2277    each non-option ARGV-element is returned here.  */
2278
2279 char *optarg = 0;
2280
2281 /* Index in ARGV of the next element to be scanned.
2282    This is used for communication to and from the caller
2283    and for communication between successive calls to `getopt'.
2284
2285    On entry to `getopt', zero means this is the first call; initialize.
2286
2287    When `getopt' returns EOF, this is the index of the first of the
2288    non-option elements that the caller should itself scan.
2289
2290    Otherwise, `optind' communicates from one call to the next
2291    how much of ARGV has been scanned so far.  */
2292
2293 int optind = 0;
2294
2295 /* The next char to be scanned in the option-element
2296    in which the last option character we returned was found.
2297    This allows us to pick up the scan where we left off.
2298
2299    If this is zero, or a null string, it means resume the scan
2300    by advancing to the next ARGV-element.  */
2301
2302 static char *nextchar;
2303
2304 /* Callers store zero here to inhibit the error message
2305    for unrecognized options.  */
2306
2307 int opterr = 1;
2308
2309 /* Describe how to deal with options that follow non-option ARGV-elements.
2310
2311    If the caller did not specify anything,
2312    the default is REQUIRE_ORDER if the environment variable
2313    POSIXLY_CORRECT is defined, PERMUTE otherwise.
2314
2315    REQUIRE_ORDER means don't recognize them as options;
2316    stop option processing when the first non-option is seen.
2317    This is what Unix does.
2318    This mode of operation is selected by either setting the environment
2319    variable POSIXLY_CORRECT, or using `+' as the first character
2320    of the list of option characters.
2321
2322    PERMUTE is the default.  We permute the contents of ARGV as we scan,
2323    so that eventually all the non-options are at the end.  This allows options
2324    to be given in any order, even with programs that were not written to
2325    expect this.
2326
2327    RETURN_IN_ORDER is an option available to programs that were written
2328    to expect options and other ARGV-elements in any order and that care about
2329    the ordering of the two.  We describe each non-option ARGV-element
2330    as if it were the argument of an option with character code 1.
2331    Using `-' as the first character of the list of option characters
2332    selects this mode of operation.
2333
2334    The special argument `--' forces an end of option-scanning regardless
2335    of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
2336    `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
2337
2338 static enum
2339 {
2340   REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
2341 } ordering;
2342
2343
2344 #define my_index strchr
2345 #define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
2346
2347
2348 /* Handle permutation of arguments.  */
2349
2350 /* Describe the part of ARGV that contains non-options that have
2351    been skipped.  `first_nonopt' is the index in ARGV of the first of them;
2352    `last_nonopt' is the index after the last of them.  */
2353
2354 static int first_nonopt;
2355 static int last_nonopt;
2356
2357 /* Exchange two adjacent subsequences of ARGV.
2358    One subsequence is elements [first_nonopt,last_nonopt)
2359    which contains all the non-options that have been skipped so far.
2360    The other is elements [last_nonopt,optind), which contains all
2361    the options processed since those non-options were skipped.
2362
2363    `first_nonopt' and `last_nonopt' are relocated so that they describe
2364    the new indices of the non-options in ARGV after they are moved.  */
2365 /* kpathsea/getopt.c */
2366 static void exchange (char **argv)
2367 {
2368   int nonopts_size;         /* paranoia - bkph */
2369   char **temp;            /* paranoia - bkph */
2370 /*  int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); */
2371   nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
2372 /*  char **temp = (char **) _alloca (nonopts_size); */
2373   temp = (char **) _alloca (nonopts_size);
2374
2375   /* Interchange the two blocks of data in ARGV.  */
2376
2377   my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
2378   my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
2379       (optind - last_nonopt) * sizeof (char *));
2380   my_bcopy ((char *) temp,
2381       (char *) &argv[first_nonopt + optind - last_nonopt],
2382       nonopts_size);
2383
2384   /* Update records for the slots the non-options now occupy.  */
2385
2386   first_nonopt += (optind - last_nonopt);
2387   last_nonopt = optind;
2388 }
2389
2390
2391 char *get_env_shroud (char *);    /* in texmf.c */
2392
2393 /* Scan elements of ARGV (whose length is ARGC) for option characters
2394    given in OPTSTRING.
2395
2396    If an element of ARGV starts with '-', and is not exactly "-" or "--",
2397    then it is an option element.  The characters of this element
2398    (aside from the initial '-') are option characters.  If `getopt'
2399    is called repeatedly, it returns successively each of the option characters
2400    from each of the option elements.
2401
2402    If `getopt' finds another option character, it returns that character,
2403    updating `optind' and `nextchar' so that the next call to `getopt' can
2404    resume the scan with the following option character or ARGV-element.
2405
2406    If there are no more option characters, `getopt' returns `EOF'.
2407    Then `optind' is the index in ARGV of the first ARGV-element
2408    that is not an option.  (The ARGV-elements have been permuted
2409    so that those that are not options now come last.)
2410
2411    OPTSTRING is a string containing the legitimate option characters.
2412    If an option character is seen that is not listed in OPTSTRING,
2413    return '?' after printing an error message.  If you set `opterr' to
2414    zero, the error message is suppressed but we still return '?'.
2415
2416    If a char in OPTSTRING is followed by a colon, that means it wants an arg,
2417    so the following text in the same ARGV-element, or the text of the following
2418    ARGV-element, is returned in `optarg'.  Two colons mean an option that
2419    wants an optional arg; if there is text in the current ARGV-element,
2420    it is returned in `optarg', otherwise `optarg' is set to zero.
2421
2422    If OPTSTRING starts with `-' or `+', it requests different methods of
2423    handling the non-option ARGV-elements.
2424    See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
2425
2426    Long-named options begin with `--' instead of `-'.
2427    Their names may be abbreviated as long as the abbreviation is unique
2428    or is an exact match for some defined option.  If they have an
2429    argument, it follows the option name in the same ARGV-element, separated
2430    from the option name by a `=', or else the in next ARGV-element.
2431    When `getopt' finds a long-named option, it returns 0 if that option's
2432    `flag' field is nonzero, the value of the option's `val' field
2433    if the `flag' field is zero.
2434
2435    The elements of ARGV aren't really const, because we permute them.
2436    But we pretend they're const in the prototype to be compatible
2437    with other systems.
2438
2439    LONGOPTS is a vector of `struct option' terminated by an
2440    element containing a name which is zero.
2441
2442    LONGIND returns the index in LONGOPT of the long-named option found.
2443    It is only valid when a long-named option has been found by the most
2444    recent call.
2445
2446    If LONG_ONLY is nonzero, '-' as well as '--' can introduce
2447    long-named options.  */
2448 /* kpathsea/getopt.c */
2449 int _getopt_internal (int argc, char *const *argv, const char *optstring,
2450             const struct option *longopts, int *longind, int long_only)
2451 {
2452   int option_index;
2453   char *commandlineflag = "command line flag";
2454
2455   optarg = 0;
2456
2457   /* Initialize the internal data when the first call is made.
2458      Start processing options with ARGV-element 1 (since ARGV-element 0
2459      is the program name); the sequence of previously skipped
2460      non-option ARGV-elements is empty.  */
2461
2462   if (optind == 0) {
2463     first_nonopt = last_nonopt = optind = 1;
2464
2465     nextchar = NULL;
2466
2467 /*     Determine how to handle the ordering of options and nonoptions.  */
2468
2469     if (optstring[0] == '-') {
2470       ordering = RETURN_IN_ORDER;
2471       ++optstring;
2472     }
2473     else if (optstring[0] == '+') {
2474       ordering = REQUIRE_ORDER;
2475       ++optstring;
2476     }
2477 /*      else if (getenv ("POSIXLY_CORRECT") != NULL) */
2478     else if (get_env_shroud ("QPTJYMZ`DPSSFDU") != NULL)
2479       ordering = REQUIRE_ORDER;
2480     else
2481       ordering = PERMUTE;
2482   }
2483
2484   if (nextchar == NULL || *nextchar == '\0') {
2485     if (ordering == PERMUTE) {
2486     /* If we have just processed some options following some non-options,
2487        exchange them so that the options come first.  */
2488
2489       if (first_nonopt != last_nonopt && last_nonopt != optind)
2490         exchange ((char **) argv);
2491       else if (last_nonopt != optind)
2492         first_nonopt = optind;
2493
2494     /* Now skip any additional non-options
2495        and extend the range of non-options previously skipped.  */
2496
2497       while (optind < argc
2498            && (argv[optind][0] != '-' || argv[optind][1] == '\0')
2499          )
2500         optind++;
2501       last_nonopt = optind;
2502     }
2503
2504 /*   Special ARGV-element `--' means premature end of options.
2505    Skip it like a null option,
2506    then exchange with previous non-options as if it were an option,
2507    then skip everything else like a non-option.  */
2508
2509     if (optind != argc && !strcmp (argv[optind], "--")) {
2510       optind++;
2511
2512       if (first_nonopt != last_nonopt && last_nonopt != optind)
2513         exchange ((char **) argv);
2514       else if (first_nonopt == last_nonopt)
2515         first_nonopt = optind;
2516       last_nonopt = argc;
2517
2518       optind = argc;
2519     }
2520
2521 /*   If we have done all the ARGV-elements, stop the scan
2522    and back over any non-options that we skipped and permuted.  */
2523
2524     if (optind == argc) {
2525     /* Set the next-arg-index to point at the non-options
2526        that we previously skipped, so the caller will digest them.  */
2527       if (first_nonopt != last_nonopt)
2528         optind = first_nonopt;
2529       return EOF;
2530     }
2531
2532 /*   If we have come to a non-option and did not permute it,
2533    either stop the scan or describe it to the caller and pass it by.  */
2534
2535     if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) {
2536       if (ordering == REQUIRE_ORDER)
2537         return EOF;
2538       optarg = argv[optind++];
2539       return 1;
2540     }
2541
2542 /*   We have found another option-ARGV-element.
2543    Start decoding its characters.  */ /* unusual use of bool */
2544
2545     nextchar = (argv[optind] + 1
2546           + (longopts != NULL && argv[optind][1] == '-'));
2547   }
2548
2549   if (longopts != NULL
2550       && ((argv[optind][0] == '-'
2551          && (argv[optind][1] == '-' || long_only)))) {
2552     const struct option *p;
2553     char *s = nextchar;
2554     int exact = 0;
2555     int ambig = 0;
2556     const struct option *pfound = NULL;
2557     int indfound=0;   /* keep compiler quiet */
2558
2559     while (*s && *s != '=')
2560       s++;
2561
2562 /*    Test all options for either exact match or abbreviated matches.  */
2563     for (p = longopts, option_index = 0; p->name;
2564        p++, option_index++)
2565       if (!strncmp (p->name, nextchar, s - nextchar))
2566       {
2567 /*      if (s - nextchar == strlen (p->name)) */
2568         if (s - nextchar == (int) strlen (p->name)) { /* avoid warning bkph */
2569 /*      Exact match found.  */
2570           pfound = p;
2571           indfound = option_index;
2572           exact = 1;
2573           break;
2574         }
2575         else if (pfound == NULL) {
2576     /* First nonexact match found.  */
2577           pfound = p;
2578           indfound = option_index;
2579         }
2580         else
2581       /* Second nonexact match found.  */
2582           ambig = 1;
2583       }
2584
2585     if (ambig && !exact) {
2586       if (opterr) {
2587         sprintf(log_line,
2588             "%s `%s' is ambiguous\n", commandlineflag, argv[optind]);
2589         show_line(log_line, 1);
2590       }
2591       nextchar += strlen (nextchar);
2592       optind++;
2593       return '?';
2594     }
2595
2596     if (pfound != NULL) {
2597       option_index = indfound;
2598       optind++;
2599       if (*s) {
2600 /*      Don't test has_arg with >, because some C compilers don't
2601       allow it to be used on enums.  */
2602         if (pfound->has_arg)
2603           optarg = s + 1;
2604         else {
2605           if (opterr) {
2606             if (argv[optind - 1][1] == '-') {   /* --option */
2607 //          fprintf (stderr,
2608               sprintf(log_line,
2609                   "%s `--%s' does not take an argument\n",
2610                   commandlineflag,pfound->name);
2611               show_line(log_line, 1);
2612             }
2613             else {      /* +option or -option */
2614 //          fprintf (stderr,
2615               sprintf(log_line,
2616                   "%s `%c%s' does not take an argument\n",
2617                   commandlineflag, argv[optind - 1][0], pfound->name);
2618               show_line(log_line, 1);
2619             }
2620           }
2621           nextchar += strlen (nextchar);
2622           return '?';
2623         }
2624       }
2625       else if (pfound->has_arg == 1) {
2626         if (optind < argc)
2627           optarg = argv[optind++];
2628         else  {
2629           if (opterr) {
2630 //        fprintf (stderr, 
2631             sprintf(log_line,
2632                 "%s `%s' requires an argument\n",
2633                 commandlineflag, argv[optind - 1]);
2634             show_line(log_line, 1);
2635           }
2636           nextchar += strlen (nextchar);
2637           return '?';
2638         }
2639       }
2640       nextchar += strlen (nextchar);
2641       if (longind != NULL)
2642         *longind = option_index;
2643       if (pfound->flag)
2644       {
2645         *(pfound->flag) = pfound->val;
2646         return 0;
2647       }
2648       return pfound->val;
2649     }
2650     /* Can't find it as a long option.  If this is not getopt_long_only,
2651    or the option starts with '--' or is not a valid short
2652    option, then it's an error.
2653    Otherwise interpret it as a short option.  */
2654     if (!long_only || argv[optind][1] == '-'
2655         || my_index (optstring, *nextchar) == NULL) {
2656       if (opterr) {
2657         if (argv[optind][1] == '-') {   /* --option */
2658           sprintf (log_line,
2659                "don't understand %s `--%s'\n",
2660                commandlineflag, nextchar);
2661           show_line(log_line, 1);
2662         }
2663         else {    /* +option or -option */
2664           sprintf (log_line,
2665                "don't understand %s `%c%s'\n",
2666                commandlineflag, argv[optind][0], nextchar);
2667           show_line(log_line, 1);
2668         }
2669       }
2670       nextchar = (char *) "";
2671       optind++;
2672       return '?';
2673     }
2674   }
2675
2676   /* Look at and handle the next option-character.  */
2677
2678   {
2679     char c = *nextchar++;
2680     char *temp = my_index (optstring, c);
2681
2682   /* Increment `optind' when we start to process its last character.  */
2683     if (*nextchar == '\0')
2684       ++optind;
2685
2686 /*    if (temp == NULL || c == ':') */
2687     if (temp == NULL || c == ARGSEP) {
2688       if (opterr) {
2689         if (c < 040 || c >= 0177) {
2690 //          fprintf (stderr,
2691           sprintf(log_line,
2692               "Unrecognized %s (0%o)\n", commandlineflag, c);
2693           show_line(log_line, 1);
2694         }
2695         else {
2696 //      fprintf (stderr,
2697           sprintf(log_line,
2698               "Unrecognized %s `-%c'\n", commandlineflag, c);
2699           show_line(log_line, 1);
2700         }
2701       }
2702       return '?';
2703     }
2704 /*    if (temp[1] == ':') */
2705     if (temp[1] == ARGSEP) {
2706 /*      if (temp[2] == ':') */
2707       if (temp[2] == ARGSEP) {
2708 /**       This is an option that accepts an argument optionally.  */
2709         if (*nextchar != '\0')  {
2710           optarg = nextchar;
2711           optind++;
2712         }
2713         else
2714           optarg = 0;
2715         nextchar = NULL;
2716       }
2717       else {
2718 /*        This is an option that requires an argument.  */
2719         if (*nextchar != '\0') {
2720           optarg = nextchar;
2721 /*      If we end this ARGV-element by taking the rest as an arg,
2722       we must advance to the next element now.  */
2723           optind++;
2724         }
2725         else if (optind == argc) {
2726           if (opterr) {
2727             sprintf(log_line,
2728                 "%s `-%c' requires an argument\n",
2729                 commandlineflag, c);
2730             show_line(log_line, 1);
2731           }
2732           c = '?';
2733         }
2734         else
2735       /* We already incremented `optind' once;
2736      increment it again when taking next ARGV-elt as argument.  */
2737           optarg = argv[optind++];
2738         nextchar = NULL;
2739       }
2740     }
2741     return c;
2742   }
2743 }
2744 /* kpathsea/getopt1.c */
2745 int getopt (int argc, char *const *argv, const char *optstring)
2746 {
2747   return _getopt_internal (argc, argv, optstring,
2748          (const struct option *) 0,
2749          (int *) 0,
2750          0);
2751 }
2752
2753 #pragma optimize ("", on)
2754
2755 /* this uses output to stderr quite a bit for errors on command line */
2756 /* clean up command line option error output */
2757 /* disallow all the weird combinations like -- */
2758
2759 //////////////////////////////////////////////////////////////////////