OSDN Git Service

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