OSDN Git Service

updating codes.
[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 #pragma warning(disable:4996)
29 #pragma warning(disable:4131) // old style declarator
30 #pragma warning(disable:4135) // conversion between different integral types
31 #pragma warning(disable:4127) // conditional expression is constant
32
33 #include <setjmp.h>
34 #include <assert.h>
35
36 #define EXTERN extern
37
38 #include "texd.h"
39
40 #include <io.h>    // needed for _finddata_t
41 #include <ctype.h> // needed for isascii and isalpha
42
43 #define NAME_MAX 255      // max size of name component
44
45 #define ISALPHA(c) (isascii (c) && isalpha(c))
46
47 #define PATH_SEP              '/'
48 #define PATH_SEP_STRING       "/"
49 #define PATH_DELIMITER        ';'
50 #define PATH_DELIMITER_STRING ";"
51
52 // default paths to look for things
53
54 #define TEXPATH    "C:/yandy/yandytex/"
55 #define TEXFORMATS "C:/yandy/yandytex/fmt"
56 #define TEXPOOL    "C:/yandy/yandytex/pool"
57 #define TEXFONTS   "C:/yandy/yandytex/tfm"
58 #define TEXINPUTS  TEXPATH "tex//;" "C:/tex;" "C:/texinput"
59
60 // structure used by fontmap
61
62 typedef struct map_element_struct
63 {
64   char * key;
65   char * value;
66   struct map_element_struct * next;
67 } map_element_type;
68
69 typedef map_element_type **map_type;
70
71 extern bool usesourcedirectory; /* in local.c */
72
73 extern bool workingdirectory;   /* in local.c */
74
75 bool absolute_p (string filename);
76 string readable (string name);
77 string truncate_pathname (string name);
78 char * file_p(string fn);
79 string *find_dir_list (string path);
80 void add_directory (string **dir_list_ptr, unsigned *dir_count_ptr, string dir);
81 void expand_subdir (string **dir_list_ptr, unsigned *dir_count_ptr, string dirname, struct _finddata_t findt, integer recurseflag);
82 void save_dir_list (string path,  string *dir_list);
83 string *initialize_path_list (string env_name,  string default_path);
84 int xfind_path_filename (string buffer, string filename,  string * dir_list);
85 string expand_default (string env_path, string default_path);
86 map_type map_create (string *dir_list);
87 char *map_lookup (map_type map, char *key);
88
89 // the following do *not* use MALLOC
90
91 extern char * xconcat  (char *buffer, char *s1, char *s2);           /* openinou.c */
92 extern char * xconcat3 (char *buffer, char *s1, char *s2, char *s3); /* openinou.c */
93
94 /////////////////////////////////////////////////////////////////////////
95
96 // used only in jump_out in tex0.c, and in texbody in itex.c
97 // and main in texmf.c and a few other abort situations in texmf.c
98 /* texk/web2c/lib/uexit.c */
99 void uexit (int unix_code)
100 {
101   int final_code;
102
103 #ifndef _WINDOWS
104   fflush(stdout);
105 #endif
106
107   if (unix_code == 0)
108     final_code = EXIT_SUCCESS;
109   else if (unix_code == 1)
110     final_code = EXIT_FAILURE;
111   else
112     final_code = unix_code;
113
114   if (jump_used)
115   {
116     show_line("Jump Buffer already used\n", 1);
117     exit(1);
118   }
119
120   jump_used++;
121   exit(final_code);
122 }
123 /* texk/web2c/lib/zround.c */
124 integer zround (double r)
125 {
126   integer i;
127
128   if (r > 2147483647.0)
129     i = 2147483647;
130   else if (r < -2147483647.0)
131     i = -2147483647;
132   else if (r >= 0.0)
133     i = (integer) (r + 0.5);
134   else
135     i = (integer) (r - 0.5);
136
137   return i;
138 }
139 /* texk/web2c/lib/eofeoln.c */
140 bool eoln (FILE * file)
141 {
142   register int c;
143
144   if (feof (file))
145     return true;
146
147   c = getc (file);
148
149   if (c != EOF)
150     (void) ungetc (c, file);
151
152   return c == '\n' || c == '\r' || c == EOF;
153 }
154
155 char * read_a_line (FILE *f,  char *line, int limit)
156 {
157   int c;
158   int loc = 0;
159
160   while ((c = getc (f)) != EOF)
161   {
162     if (c == '\n' || c == '\r')
163     {
164       if (loc > 0) break;
165       else continue;        /* ignore \r\n and blank lines */
166     }
167
168     line[loc] = (char) c;
169     loc++;
170
171     if (loc == limit - 1)
172     {
173       sprintf(log_line, " ERROR: line too long\n");
174       show_line(log_line, 1);
175       show_line(line, 0);
176       show_line("\n", 0);
177       break;
178     }
179   }
180
181   if (c != EOF || loc > 0)
182   {
183     line[loc] = '\0';       /* terminate */
184     return line;          /* and return */
185   }
186   else
187     return(NULL);          /* true EOF */
188 }
189 /****************************************************************************************/
190
191 /* from ourpaths.c */
192
193 #define BUILDNAMEDIRECT /* avoid malloc for string concat */
194
195 #define CACHEFILENAME   /* cache last full path/file name 96/Nov/16 */
196                         /* to speed up LaTeX 2e which opens files twice */
197
198 /* `path_dirs' is initialized in `set_paths', to a null-terminated array
199    of directories to search for.  */
200
201 static string *path_dirs[LAST_PATH];
202
203 /* This sets up the paths, by either copying from an environment variable
204    or using the default path, which is defined as a preprocessor symbol
205    (with the same name as the environment variable) in `site.h'.  The
206    parameter PATH_BITS is a logical or of the paths we need to set.  */
207 void set_paths (int path_bits)
208 {
209   int n;                          /* 97/Apr/2 */
210   char *s, *t, *u;                /* 94/Jan/6 */
211   char buffer[PATH_MAX];
212
213
214   if (path_bits & TEXFORMATPATHBIT)
215   {
216     s = "TEXFORMATS";
217     t = TEXFORMATS;
218
219     if (grabenv(s) == NULL)
220     {
221       strcpy(buffer, texpath);
222       strcat(buffer, PATH_SEP_STRING); 
223       strcat(buffer, "fmt");
224
225       if (trace_flag)
226       {
227         sprintf(log_line, "Checking `%s' = %s %s %s\n", buffer, texpath, PATH_SEP_STRING, "fmt");
228         show_line(log_line, 0);
229       }
230
231       if (dir_p(buffer))
232         t = xstrdup(buffer);
233       else
234       {
235         s = "TEXFMTS";
236         if (getenv(s) == NULL)  s = "TEXFMT";
237       }
238
239       if (trace_flag)
240       {
241         sprintf(log_line, "\nSetting up %s (default %s) ", "TEXFORMATS", t);
242         show_line(log_line, 0);
243       }
244     }
245
246     path_dirs[TEXFORMATPATH] = initialize_path_list (s, t);
247   }
248
249   if (path_bits & TFMFILEPATHBIT)
250   {
251     s = "TEXFONTS";
252
253     if ((u = grabenv("ENCODING")) != NULL)
254     {
255       encoding_name = u;
256
257       if ((t = grabenv(u)) != NULL)
258       {
259         if (strchr(t, ':') != NULL && sscanf(t, "%d", &n) == 0)
260         {
261           s = u;
262         }
263       }
264     }
265
266     t = TEXFONTS;
267
268     if (grabenv(s) == NULL)
269     {
270       strcpy(buffer, texpath);
271       strcat(buffer, PATH_SEP_STRING); 
272       strcat(buffer, "tfm");
273
274       if (trace_flag)
275       {
276         sprintf(log_line, "Checking `%s' = %s %s %s\n",
277             buffer, texpath, PATH_SEP_STRING, "tfm");
278         show_line(log_line, 0);
279       }
280
281       if (dir_p(buffer))
282         t = xstrdup(buffer);
283       else
284       {
285         s = "TEXTFMS";
286         if (getenv(s) == NULL) s = "TEXTFM";
287       }
288
289       if (trace_flag)
290       {
291         sprintf(log_line, "\nSetting up %s (default %s) ", "TEXFONTS", t);
292         show_line(log_line, 0);
293       }
294     }
295
296     path_dirs[TFMFILEPATH] = initialize_path_list (s, t);
297   }
298
299   if (path_bits & TEXINPUTPATHBIT)
300   {
301     if (format_specific)
302     {
303       s = format_name;
304
305       if (grabenv(s) == NULL)
306         s = "TEXINPUTS";
307     }
308     else
309       s = "TEXINPUTS";
310
311     if (grabenv(s) == NULL)
312     {
313       s = "TEXINPUT";
314
315       if (trace_flag)
316       {
317         sprintf(log_line, "\nSetting up %s ", "TEXINPUTS");
318         show_line(log_line, 0);
319       }
320     }
321
322     path_dirs[TEXINPUTPATH]  = initialize_path_list (s, TEXINPUTS);
323   }
324 }
325
326 #ifdef CACHEFILENAME
327   char last_filename[PATH_MAX] = "";  /* last full path / file name found C */
328   char last_name[PATH_MAX]     = "";  /* last file name searched for C */
329   int last_path_index          = -1;  /* last path_index */
330 #endif
331
332 /* Look for NAME, a C string (no longer Pascal), in the colon-separated list 
333    of directories given by `path_dirs[PATH_INDEX]'.  If the search is
334    successful, leave the full pathname in NAME (which therefore must
335    have enough room for such a pathname), padded with blanks.
336    Otherwise, or if NAME is an absolute or relative pathname, just leave
337    it alone.  */
338
339 /*  changed to take C string 97/June/5 - used to take Pascal strings */
340 /*  now expects null terminated strings */
341 /* used only in open_input.*/
342 bool test_read_access (unsigned char *name, int path_index)
343
344 #ifdef BUILDNAMEDIRECT
345   char buffer[PATH_MAX];  /* for constructing name 1996/Jan/20 */
346   int foundflag;          /* true if path found */
347 #else
348   string foundname;
349 #endif  
350
351   if (*name == '\0')
352     return FALSE;
353
354 #ifdef CACHEFILENAME
355 /*  If file name and path_index matches - and saved filename exists */
356 /*  then use cached full path / file name 96/Nov/16 */
357   if (cache_file_flag)
358   {
359     if (path_index == last_path_index &&
360         strcmp((const char *)name, last_name) == 0 && *last_filename != '\0')
361     {
362       if (open_trace_flag)
363       {
364         sprintf(log_line, "\nFOUND `%s' (%d) IN CACHE: `%s' ", name, path_index, last_filename); 
365         show_line(log_line, 0);
366       }
367
368       strcpy((char *)name, last_filename); 
369       return TRUE;
370     }
371
372     last_path_index = path_index;
373     strcpy(last_name, (const char *)name);
374     *last_filename = '\0';          /* in case not found */
375   }
376 #endif
377
378 #ifdef BUILDNAMEDIRECT
379   foundflag = xfind_path_filename (buffer, (char *)name, path_dirs[path_index]);
380 #else
381   foundname = find_path_filename (name, path_dirs[path_index]);
382 #endif
383
384 #ifdef BUILDNAMEDIRECT
385   if (foundflag == 0 && path_index == TFMFILEPATH)
386 #else
387   if (foundname == NULL && path_index == TFMFILEPATH)
388 #endif
389   {
390     char *mapped_name;
391     static map_type fontmap = NULL;   /* GLOBAL, so won't recreate */
392
393     if (fontmap == NULL)
394     {
395       if (trace_flag)
396       {
397         sprintf(log_line, "Loading in texfonts.map file for %s\n", name);
398         show_line(log_line, 0);
399       }
400
401       fontmap = map_create (path_dirs[path_index]);
402     }
403
404     mapped_name = map_lookup (fontmap, (char *) name);
405
406     if (mapped_name)
407     {
408 #ifdef BUILDNAMEDIRECT
409       foundflag = xfind_path_filename (buffer, mapped_name, path_dirs[path_index]);
410 #else
411       foundname = find_path_filename (mapped_name, path_dirs[path_index]);
412 #endif
413     }
414   }
415
416   if (open_trace_flag)
417   {
418     show_line("\n", 0);
419   }
420
421   if (open_trace_flag)
422   {
423 #ifdef BUILDNAMEDIRECT
424     if (foundflag != 0)
425     {
426       sprintf(log_line, "`%s' in test_read_access\n", buffer);
427       show_line(log_line, 0);
428     }
429 #else
430     if (foundname != NULL)
431     {
432       sprintf(log_line, "`%s' in test_read_access\n", foundname);
433       show_line(log_line, 0);
434     }
435 #endif
436   }
437
438 /*  If we found it somewhere, save it.  */
439 #ifdef BUILDNAMEDIRECT
440   if (foundflag != 0)
441   {
442     strcpy ((char *)name, buffer); 
443
444 #ifdef CACHEFILENAME
445     if (cache_file_flag)
446     {
447       strcpy(last_filename, buffer);  /* full path */
448     }
449 #endif
450   }
451 #else
452   if (foundname != NULL)
453   {
454     strcpy (name, foundname);
455 #ifdef CACHEFILENAME
456     if (cache_file_flag)
457     {
458       strcpy(last_filename, foundname); /* full path */
459       last_namelength = strlen(buffer);
460     }
461 #endif
462   }
463 #endif
464
465 #ifdef BUILDNAMEDIRECT
466   return foundflag;
467 #else
468   if (foundname == NULL) return FALSE;
469   else {
470     if (foundname != name) free (foundname);  /* copied, now free ??? 96/Jan/10 */
471     return TRUE;
472   }
473 #endif
474 }
475
476 /********************************************************************/
477 /* from fontmap.c */
478 /* Fontname mapping.  We use a straightforward hash table.  */
479
480 #define MAP_SIZE 199
481
482 /* The hash function.  We go for simplicity here.  */
483 /* NOT USED! */
484 static unsigned map_hash (char * key)
485 {
486   unsigned n = 0;
487
488 /*  There are very few font names which are anagrams of each other 
489     so no point in weighting the characters.  */
490   while (*key != 0) n += *key++;
491   n %= MAP_SIZE;
492   return n;
493 }
494
495 /* Look up STR in MAP.  Return the corresponding `value' or NULL.  */
496 /* NOT USED! */
497 static char *map_lookup_str (map_type map, char *key)
498 {
499   map_element_type *p;
500   unsigned n = map_hash (key);
501
502   for (p = map[n]; p != NULL; p = p->next)
503     if (strcmp(key, p->key) == 0) return p->value;
504
505   return NULL;
506 }
507
508
509 /* Look up KEY in MAP; if it's not found, remove any suffix from KEY and
510    try again.  */
511 /* NOT USED! */
512 char *map_lookup (map_type map, char *key)
513 {
514   string suffix = find_suffix (key);
515   string ret = map_lookup_str (map, key);
516
517   if (! ret) {
518 /*    OK, the original KEY didn't work.  Let's check for the KEY without
519     an extension -- perhaps they gave foobar.tfm, but the mapping only
520     defines `foobar'.  */
521     if (suffix) {
522       string base_key = remove_suffix (key);
523       ret = map_lookup_str (map, base_key);
524       free (base_key);  // it's safe to free copy
525     }
526   }
527
528 /*  Append the same suffix we took off, if necessary.  */
529 /*  what if suffix is NULL ??? */ /* what if we didn't take off suffix ??? */
530 /*  if (ret) */
531   if (ret && suffix) {            /* 1994/March/18 */
532     ret = extend_filename (ret, suffix);
533 //    the above creates a newly allocated string ... should free old ?
534 //    except extend_filename may return the old one ?
535   }
536   return ret;
537 }
538
539 /* If KEY is not already in MAP, insert it and VALUE.  */
540 /* This was very buggy (when hash codes collided) - rewritten 94/March/18 */
541 /* NOT USED! */
542 void map_insert (map_type map, char *key, char *value)
543 {
544   unsigned n = map_hash (key);
545   map_element_type **ptr = &map[n];
546 /*  map_element_type ***trailer = &p; */
547
548   while (*ptr != NULL && !(strcmp(key, (*ptr)->key) == 0)) {
549 /*       *p = (*p)->next; */
550     ptr = &((*ptr)->next);
551 /*       trailer = &p; */
552   }
553
554   if (*ptr == NULL)
555   {
556     *ptr = (map_element_type *) xmalloc (sizeof(map_element_type));
557     (*ptr)->key = xstrdup (key);
558     (*ptr)->value = xstrdup (value);
559     (*ptr)->next = NULL;
560   }
561 }
562
563 /* Open and read the mapping file FILENAME, putting its entries into
564    MAP. Comments begin with % and continue to the end of the line.  Each
565    line of the file defines an entry: the first word is the real
566    filename (e.g., `ptmr'), the second word is the alias (e.g.,
567    `Times-Roman'), and any subsequent words are ignored.  .tfm is added
568    if either the filename or the alias have no extension.  This is the
569    same order as in Dvips' psfonts.map; unfortunately, we can't have TeX
570    read that same file, since most of the real filenames start with an
571    `r', because of the virtual fonts Dvips uses.  */
572
573 /* Modified 97/May/17 to avoid malloc for each line read */
574
575 #ifndef MALLOCLINE
576   #define MAXLINE 256
577 #endif
578 /* NOT USED! */
579 int map_file_parse (map_type map, char *map_filename)
580 {
581   char *l;
582   unsigned map_lineno = 0;
583   FILE *f; 
584 #ifndef MALLOCLINE
585   char line[MAXLINE];             /* 97/May/17 */
586 #endif  
587
588   if (trace_flag) {
589     sprintf(log_line, "Opening %s\n",  map_filename); /* 97/May/17 */
590     show_line(log_line, 0);
591   }
592 //  f = xfopen (map_filename, FOPEN_R_MODE);
593   f = fopen (map_filename, FOPEN_R_MODE);
594   if (f == NULL) {
595     perrormod(map_filename);  // should not happen, since we tested
596     return -1;          // failed
597   }
598 #ifdef MALLOCLINE
599   while ((l = read_line (f)) != NULL) 
600 #else
601     while ((l = read_a_line (f, line, sizeof(line))) != NULL) /* 97/May/17 */
602 #endif
603     {
604       string filename;
605       string comment_loc;
606
607 /*      comment_loc = strrchr (l, '%'); */
608       comment_loc = strchr (l, '%');      /* 96/Nov/16 */
609 /*      if (comment_loc == NULL) comment_loc = strrchr (l, ';'); */
610       if (comment_loc == NULL) comment_loc = strchr (l, ';'); /* fixed */
611
612 /*      Ignore anything after a % or;  */
613 /*      if (comment_loc) *comment_loc = 0; */
614       if (comment_loc != NULL) *comment_loc = '\0';
615
616       map_lineno++;
617
618 /*      If we don't have any filename, that's ok, the line is blank.  */
619       filename = strtok (l, " \t");
620 /*      if (filename)  */
621       if (filename != NULL) {
622         string alias = strtok (NULL, " \t");
623
624 /*        But if we have a filename and no alias, something's wrong.  */
625         if (alias == NULL || *alias == 0) {
626           sprintf(log_line,
627               " Have file name `%s', but no mapping (line %u in file %s).\n",
628               filename, map_lineno, map_filename);
629           show_line(log_line, 1);
630         }
631         else  {
632 /*          We've got everything.  Insert the new entry.  */
633           map_insert (map, alias, filename);
634         }
635       }
636 #ifdef MALLOCLINE
637       free (l);
638 #endif
639     }
640 //  xfclose (f, map_filename);
641   (void) fclose (f);    // we don't care about errors at this stage
642   return 0;       // success
643 }
644 /* NOT USED! */
645 void unshroud_string (char *, char *, int); /* in texmf.c */
646 /* Look for the file `texfonts.map' in each of the directories in
647    DIR_LIST.  Entries in earlier files override later files.  */
648
649 /* This is probably quite silly - but what the hell lets leave it in */
650 /* NOT USED! */
651 map_type map_create (string *dir_list)
652 {
653   map_type map = (map_type) xcalloc (MAP_SIZE, sizeof (map_element_type *));
654
655   while (*dir_list) {
656     char filename[PATH_MAX];
657
658 /*     We don't bother with the filename truncation that `readable' in
659          `pathsrch.c' does, since we ourselves are giving the filename,
660          and I don't think it's worth worrying about too-long
661          intermediate directory names in the path.  */
662     strcpy (filename, *dir_list);
663 /*      strcat (filename, "texfonts.map"); */   /* 1993/Nov/20 */
664     unshroud_string (filename+strlen(filename),
665             "ufygpout/nbq", PATH_MAX - strlen(filename));
666
667 /*    testing access first so xfopen won't fail... */
668 /*    maybe do this another way to avoid using `access' ? */
669     if (file_method) {
670       if (file_p (filename) != NULL) {    /* use file_p the new way */
671         (void)  map_file_parse (map, filename);
672       }
673     }
674     else {  
675 /*      if (access (filename, R_OK) == 0) */  /* use access the old way */
676       if (_access (filename, R_OK) == 0) {  /* 1999/Jan/8 ??? */
677 /*      if (readable (filename) != NULL) */
678         (void) map_file_parse (map, filename);
679       }
680     }
681     dir_list++;
682   }
683   return map;
684 }
685 /**********************************************************************/
686
687 /* #pragma optimize ("g", off) *//* try and avoid compiler bug here _dos_find */
688
689 /* NOTE: _dos_find... prevents running under Windows NT ??? */
690 /* This is called if file_method != 0 */ /* which is currently the default */
691
692 #ifdef MSDOS
693 /* see whether a file exists, is readable and is not a directory */
694 /* 1994/Feb/13 may be faster than `access' in `readable' */
695 /* returns NULL or the name filename passed in ??? */
696 /* kpathsea/file_p.c */
697 char *file_p (string fn)
698 {
699   struct _finddata_t fi;
700   long hFind;
701   int ret;
702
703   if (open_trace_flag)
704   {
705     sprintf(log_line, "Is `%s' a readable file? ", fn);
706     show_line(log_line, 0);
707   }
708
709 /*  allow for `normal' (_A_NORMAL) as well as `read-only' files */
710
711   hFind = _findfirst (fn, &fi);
712
713   if (hFind > 0)
714   {
715     ret = 0;
716     _findclose (hFind);
717   }
718   else
719     ret = -1;
720
721 /*  check whether found and whether *not* a sub-directory */
722   if (ret == 0) {
723     if ((fi.attrib & _A_SUBDIR) == 0) {
724       if (open_trace_flag) {
725         sprintf(log_line, "`%s' IS a readable file. ", fn);
726         show_line(log_line, 0);
727       }
728       return fn;    /* true - its a file, not a dir */
729     }
730     else {
731       if (open_trace_flag) {
732         sprintf(log_line, "`%s' is a subdirectory. ", fn);
733         show_line(log_line, 0);
734       }
735       return NULL;  /* false - directory */
736     }
737   }
738   else {
739     if (open_trace_flag) {
740       sprintf(log_line, "`%s' is NOT a readable file. ", fn);
741       show_line(log_line, 0);
742     }
743     return NULL;  /* false - not found or no read access */
744   }
745 }
746
747 #endif /* DOS */
748
749 /* #pragma optimize ("g",)  */  /* try and avoid compiler bug here _dos_find */
750 /* #pragma optimize ("g",)*/  /* try and avoid compiler bug here _dos_find */
751 // #pragma optimize ("", on)    /* 96/Sep/15 */
752
753
754 /**************************************************************************/
755
756 /********************************************************************************/
757
758 /* pathsrch.c */
759
760 /* If FILENAME is absolute or explicitly relative (i.e., starts with
761    `/', `./', or `../'), or if DIR_LIST is null, we return whether
762    FILENAME is readable as-is.  Otherwise, we test if FILENAME is in any of
763    the directories listed in DIR_LIST.  (The last entry of DIR_LIST must
764    be null.)  We return the complete path if found, NULL else.
765    
766    In the interests of doing minimal work here, we assume that each
767    element of DIR_LIST already ends with a `/'.
768    
769    DIR_LIST is most conveniently made by calling `initialize_path_list'.
770    This is a separate routine because we allow recursive searching, and
771    it may take some time to discover the list of directories.  
772    We do not want to incur that overhead every time we want to look for
773    a file.
774    
775    (Actually, `/' is not hardwired into this routine; we use PATH_SEP,
776    defined above.)  */
777
778 /* xfind_path_filename is used now */
779
780 /* Called only from test_read_access(...) in ourpaths.c */
781
782 #ifdef BUILDNAMEDIRECT
783
784 /* this string allocation / concatination is silly - use fixed buffer! */
785 /* NOT USED£¡ */
786 int xfind_path_filename (string buffer, string filename, string * dir_list)
787 {
788   string found_name = NULL;
789
790   if (buffer == filename)
791   {
792     show_line("buffer == filename\n", 1);
793   }
794
795   *buffer = '\0';       /* "" in case we fail */
796
797   if (open_trace_flag)
798   {
799     sprintf(log_line, "Find path for `%s' ", filename);
800     show_line(log_line, 0);
801   }
802
803 /*  ignore current directory for TFM files ? */ /* 1994/Jan/24 */
804   if (!current_tfm &&  strstr(filename, ".tfm") != NULL && strcmp(*dir_list, "./") == 0)
805   {
806     if (open_trace_flag)
807     {
808       sprintf(log_line, "Ignoring `.' for %s ", filename);
809       show_line(log_line, 0);
810     }
811
812     dir_list++;           /* step over first entry in dir list */
813   }
814
815   if (trace_flag && open_trace_flag)
816   {
817     char **pstrs;
818     pstrs = dir_list;
819     show_line("\n", 0);
820     sprintf(log_line, "Find path for `%s' ", filename);
821     show_line(log_line, 0);
822     show_line("- IN: ", 0);
823
824     while (*pstrs != NULL)
825     {
826       sprintf(log_line, "%s ", *pstrs);
827       show_line(log_line, 0);
828       pstrs++;
829     }
830
831     show_line("\n", 0);
832   }
833
834 /*  Do this before testing for absolute-ness, as a leading ~ will be an
835   absolute pathname.  */  /* forget this for DOS ! */
836 #ifndef MSDOS
837   filename = expand_tilde (filename);
838 #endif
839
840 #ifdef MSDOS
841 /*  is this always safe?  That is, is filename writable and its OK to modify */
842 /*  unixify(filename);   */ /* done `in place' */
843   if (deslash) unixify(filename);   /* made conditional 94/Feb/24 */
844 #endif
845
846 /*  following addded in attempt to catch `nul' */   /* 94/Jan/6 bkph */
847 /*  could also try and catch `nul.tex' first - but who cares about speed ? */
848 /*  needed to add this since `access' gets the wrong answer */
849 /*  that is, `nul' is a file that can be opened, but `access' says not so */
850   if (strcmp(filename, "nul") == 0)
851     strcpy(buffer, filename); 
852 /*  If FILENAME is absolute or explicitly relative, or if DIR_LIST is
853     null, only check if FILENAME is readable.  */
854 /*  if (absolute_p (filename) || dir_list == NULL) */ /* 94/Jan/6 */
855   else if (absolute_p (filename) || dir_list == NULL)
856   {
857     if (file_method)
858       found_name = file_p (filename);
859     else
860       found_name = readable (filename);
861
862     if (found_name != NULL)
863       strcpy(buffer, found_name);
864     else
865       *buffer = '\0';
866   }
867   else  /* Test if FILENAME is in any of the directories in DIR_LIST.  */
868   {
869     char *s;
870     int sourceflag;
871     int firsttime=1;
872
873     while (*dir_list != NULL) 
874     {
875 /* if item is current directory, look in source file directory first */
876 /* provided usesourcedirectory flag is set and workingdirectory in use */
877       s = *dir_list;
878       sourceflag = 0;
879
880       if (strcmp(s, "./") == 0)
881       {
882         if (firsttime && usesourcedirectory && workingdirectory &&
883             source_direct != NULL && *source_direct != '\0')
884         {
885           s = source_direct;
886
887           if (trace_flag)
888           {
889             sprintf(log_line, "Using %s dir %s %s\n", "source", s, "X");
890             show_line(log_line, 0);
891           }
892
893           sourceflag = 1;     /* avoid increment of list below */
894           firsttime = 0;      /* special stuff only first time */
895         }
896         else if (trace_flag)
897         {
898           sprintf(log_line, "Using %s dir %s %s\n", "current",  s, "X");
899           show_line(log_line, 0);
900         }
901       }
902
903       if (trace_flag)
904       {
905         sprintf(log_line, "XCONCAT %s %s in find_path_filename\n",
906             s, filename);
907         show_line(log_line, 0);
908       }
909 /*      filename = concat (*dir_list, save_filename); */
910       (void) xconcat (buffer, s, filename);
911       if (file_method) found_name = file_p (buffer);  /* new way */
912       else found_name = readable (buffer);      /* slow old way */
913       if (found_name == NULL) {
914         *buffer = '\0';
915         if (! sourceflag)   /* 98/Sep/29 repeat in current dir */
916           dir_list++;       /* try next */
917       }
918       else {
919         if (found_name != buffer)
920           strcpy(buffer, found_name);       /* success */
921         break;
922       }
923     }
924   }
925   return (*buffer != '\0');     /* true if not empty string */
926 }
927
928 #else
929 /* We are dealing with a C string here for filename presumably ... */
930 /* Also, this returns a string that was allocated --- */
931 /* unless it happens to equal the filename sent in ... */
932 /* this needs to be freed later - unless it happens to be ... */
933
934 string find_path_filename (string filename,  string * dir_list)
935 {
936   string found_name = NULL;
937   
938   if (open_trace_flag) {
939 //    printf("Find path for `%s' ", filename);
940     sprintf(log_line, "Find path for `%s' ", filename);
941     show_line(log_line, 0);
942   }
943
944 /*  ignore current directory for TFM files ? */ /* 1994/Jan/24 */
945   if (!current_tfm &&
946       strstr(filename, ".tfm") != NULL &&
947         strcmp(*dir_list, "./") == 0) {
948     if (open_trace_flag) {
949       sprintf(log_line, "Ignoring `.' for %s ", filename);
950       show_line(log_line, 0);
951     }
952     dir_list++;           /* step over first entry in dir list */
953   }
954
955   if (trace_flag && open_trace_flag) {    /* debugging trace 1994/Jan/8 */
956     char **pstrs;
957     pstrs = dir_list;
958     show_line("\n", 0);
959     sprintf(log_line, "Find path for `%s' ", filename);
960     show_line(log_line, 0);
961     show_line("- IN: ", 0);
962     while (*pstrs != NULL) {
963 //      printf("%s ", *pstrs);
964       sprintf(log_line, "%s ", *pstrs);
965       show_line(log_line, 0);
966       pstrs++;
967     }
968     show_line("\n", 0);
969   }
970
971 /*  Do this before testing for absolute-ness, as a leading ~ will be an
972     absolute pathname.  */  /* forget this for DOS ! */
973 #ifndef MSDOS
974   filename = expand_tilde (filename);
975 #endif
976
977 #ifdef MSDOS
978 /*  is this always safe?  That is, is filename writable and its OK to modify */
979 /*  unixify(filename);   */ /* done `in place' */
980   if (deslash) unixify(filename);    /* made conditional 94/Feb/24 */
981 #endif
982 /*  following addded in attempt to catch `nul' */   /* 94/Jan/6 bkph */
983 /*  could also try and catch `nul.tex' first - but who cares about speed ? */
984 /*  needed to add this since `access' gets the wrong answer */
985 /*  that is, `nul' is a file that can be opened, but `access' says not so */
986   if (strcmp(filename, "nul") == 0) found_name = filename; 
987 /*  If FILENAME is absolute or explicitly relative, or if DIR_LIST is
988     null, only check if FILENAME is readable.  */
989 /*  if (absolute_p (filename) || dir_list == NULL) */ /* 94/Jan/6 */
990   else if (absolute_p (filename) || dir_list == NULL) {
991     if (file_method)  found_name = file_p (filename); /* new way 94/Feb/13 */
992     else found_name = readable (filename);        /* slow old way */
993   }
994   else { /* Test if FILENAME is in any of the directories in DIR_LIST.  */
995     string save_filename = filename;
996     char *s;
997     int sourceflag;
998     int firsttime=1;
999     while (*dir_list != NULL)  {
1000 /* if item is current directory, look in source file directory first */
1001 /* provided usesourcedirectory flag is set and workingdirectory in use */
1002       s = *dir_list;
1003       sourceflag = 0;
1004       if (strcmp(s, "./") == 0) {
1005         if (firsttime && usesourcedirectory && workingdirectory &&
1006           source_direct != NULL && *source_direct != '\0') {
1007           s = source_direct;
1008           if (trace_flag) {
1009             sprintf(log_line, "Using %s dir %s %s\n", "source", s, "F");
1010             show_line(log_line, 0);
1011           }
1012           sourceflag = 1;     /* avoid increment of list below */
1013           firsttime = 0;      /* special stuff only first time */
1014         }
1015         else if (trace_flag) {
1016           sprintf(log_line, "Using %s dir %s %s\n", "current", s, "F");
1017           show_line(log_line, 0);
1018         }
1019       }
1020       if (trace_flag) {
1021         sprintf(log_line, "CONCAT %s %s in find_path_filename\n",
1022                   s, save_filename); /* 1996/Jan/20 */
1023         show_line(log_line, 0);
1024       }
1025       filename = concat (s, save_filename);
1026 /*          found_name = readable (filename); */
1027       if (file_method) found_name = file_p (filename);  /* new way */
1028       else found_name = readable (filename);      /* slow old way */
1029       if (found_name == NULL)  {
1030         free (filename);    /* if this is not it, free it again */
1031         if (! sourceflag)   /* 98/Sep/29 repeat in current dir */
1032           dir_list++;
1033       }
1034       else {
1035         if (found_name != filename)
1036           free (filename);  /* don't free if is the one passed in */
1037         break;
1038       }
1039     }
1040   }
1041   return found_name;        /* return the allocated name - free later */
1042 }
1043 #endif
1044
1045 /* If NAME is readable, return it.  If the error is ENAMETOOLONG,
1046    truncate any too-long path components and return the result (unless
1047    there were no too-long components, i.e., a overall too-long name
1048    caused the error, in which case return NULL).  On any other error,
1049    return NULL.
1050    
1051    POSIX invented this brain-damage of not necessarily truncating
1052    pathname components; the system's behavior is defined by the value of
1053    the symbol _POSIX_NO_TRUNC, but you can't change it dynamically!  */
1054
1055 /* Using access (and dir_p)  is considerably slower than using dosfind */
1056 /* NOTE: this is only called from find_path_file,
1057    and then ONLY if file_method is false (-G) */
1058
1059 /* returns NULL or the file name passed in ??? */
1060
1061 /* static string readable (name) */
1062 /* kpathsea/readable.c */
1063 string readable (string name)
1064 {
1065   string ret;
1066
1067   if (open_trace_flag) {
1068     sprintf(log_line, "is %s readable? ", name);
1069     show_line(log_line, 0);
1070   }
1071
1072 /*  Check first whether we have read access, then */
1073 /*  need to test if directory, since access always says OK for directory */
1074 /*  BUT: readable is called only from find_path_file */
1075 /*  So we never call this with directory, so why waste time ? bkph */
1076 /*  BUT: can be caught out by idiot with a directory called myfile.tex ? */
1077   
1078   if (_access (name, R_OK) == 0)  {
1079     if (test_dir_access) {    /* check only if we are asked to ... */
1080       if (dir_p (name)) {
1081         if (open_trace_flag) {
1082           sprintf(log_line, "tested read access of directory `%s' ", name);
1083           show_line(log_line, 0);
1084         }
1085         ret = NULL;
1086       }
1087       else ret = name;
1088     }
1089     else ret = name;
1090   }
1091 /*  if (_access (name, R_OK) == 0 && !dir_p (name))   ret = name; */
1092 #ifdef ENAMETOOLONG
1093   else if (errno == ENAMETOOLONG) { 
1094     ret = truncate_pathname (name);
1095 /* Perhaps some other error will occur with the truncated name, so
1096          let's call access again.  */
1097     if (!(_access (ret, R_OK) == 0 && !dir_p (ret))) { /* Failed.  */
1098       free (ret);
1099       ret = NULL;
1100     }
1101   }
1102 #endif
1103   else if (errno == EACCES) {
1104     if (trace_flag) show_line("Access denied!\n", 0);
1105     ret = NULL;
1106   }
1107   else if (errno == ENOENT) {
1108     if (trace_flag) show_line("File or path name not found!\n", 1);
1109     ret = NULL;
1110   }
1111   else {
1112     if (trace_flag) {
1113       sprintf(log_line, "Unknown access error %d!\n", errno);
1114       show_line(log_line, 0);
1115     }
1116     ret = NULL;
1117   }
1118   return ret;
1119 }
1120
1121 #ifdef ENAMETOOLONG
1122 /* Truncate any too-long path components in NAME, returning the result.  */
1123
1124 string truncate_pathname (string name)
1125 {
1126   unsigned c_len = 0;       /* Length of current component.  */
1127   unsigned ret_len = 0;   /* Length of constructed result.  */
1128   string ret = (string) xmalloc (PATH_MAX + 1);
1129
1130   for (; *name; name++)
1131     {
1132       if (*name == PATH_SEP)      /* not in DOS */
1133         { /* At a directory delimiter, reset component length.  */
1134           c_len = 0;
1135         }
1136       else if (c_len > NAME_MAX)
1137         { /* If past the max for a component, ignore this character.  */
1138           continue;
1139         }
1140
1141       /* If we've already copied the max, give up.  */
1142       if (ret_len == PATH_MAX)
1143         {
1144           free (ret);
1145           return NULL;
1146         }
1147
1148       /* Copy this character.  */
1149       ret[ret_len++] = *name;
1150       c_len++;
1151     }
1152   ret[ret_len] = 0;
1153
1154   return ret;
1155 }
1156 #endif  /* end of ifdef ENAMETOOLONG */
1157
1158
1159 /* Return true if FILENAME is absolute or explicitly relative, else false.  */
1160 /* Absolute: in DOS name starts with PATH_SEP, or with DRIVE letter and colon */
1161 /* Explicitly relative: starts with ./ or ../ */
1162 /* kpathsea/absolute.c */
1163 // static bool absolute_p (string filename) {
1164 bool absolute_p (string filename)
1165 {
1166   bool absolute;
1167   bool explicit_relative;
1168
1169 #ifdef MSDOS
1170 /*  absolute = (*filename == PATH_SEP) */     /* 1994/Mar/1 */
1171   absolute = (*filename == PATH_SEP || *filename == '\\')
1172                       || ((filename[1] == ':') && ISALPHA (*filename));
1173 /*                      || ISALPHA (*filename) && filename[1] == ':'; */
1174 #else
1175   absolute = (*filename == PATH_SEP);
1176 #endif
1177   if (absolute) return true;      /* don't waste any more time */
1178
1179 #ifdef MSDOS
1180   explicit_relative = (*filename == '.'
1181     && ((filename[1] == PATH_SEP || filename[1] == '\\')
1182            || (filename[1] == '.' &&
1183          (filename[2] == PATH_SEP || filename[2] == '\\'))));
1184 #else
1185   explicit_relative = (*filename == '.'  && (filename[1] == PATH_SEP
1186            || (filename[1] == '.' && filename[2] == PATH_SEP))); 
1187 #endif
1188
1189   return explicit_relative;
1190 /*  return absolute || explicit_relative; */ /* slight rewrite 1994/Feb/13 */
1191 }
1192
1193 #ifdef MSDOS
1194 /*  note: this strips off trailing white space in actual environment var ... */
1195 void striptrailing (string env_value, string env_name, string default_path)
1196 {
1197   char *s;
1198   if (env_name == NULL) {         /* 1994/Feb/24 */
1199     if (trace_flag) {
1200       sprintf(log_line, "WARNING: no env_name noted, using default %s\n",
1201         default_path);
1202       show_line(log_line, 0);
1203     }
1204     return;
1205   }
1206   if (env_value == NULL) {
1207     if (trace_flag) {
1208       sprintf(log_line, "WARNING: %s not defined in environment, using default %s\n",
1209         env_name, default_path);
1210       show_line(log_line, 0);
1211     }
1212     return;
1213   }
1214   if (strlen(env_value) == 0) return;
1215   s = env_value + strlen(env_value) - 1;
1216 /*  while (*s <= ' ') *s-- = '\0'; */
1217   while (s >= env_value && *s <= ' ')*s-- = '\0';   /* 94/Feb/24 */
1218 }
1219 #endif
1220
1221 /* convert /! and /!! to / and // 97/Mar/22 */
1222
1223 #ifdef MSDOS
1224 /* 97/Mar/22 */
1225 void convertexclam (string env_value)
1226 {
1227   char *s;
1228   if (env_value == NULL) return;
1229   s = env_value;
1230   if (strchr(s, '!') == NULL) return;
1231   while ((s = strchr(s, '!')) != NULL)
1232   {
1233     if (*(s+1) == '!')
1234     {  /* double !! */
1235       if (*(s+2) == PATH_DELIMITER || *(s+2) == '\0')
1236       {
1237         if (s > env_value && *(s-1) == PATH_SEP)
1238         {
1239           *s = PATH_SEP;    /* convert the first ! */
1240           strcpy(s+1, s+2); /* flush the second ! */
1241         }
1242       }
1243     }
1244     else
1245     {    /* single ! */  /* assume already unixified */
1246       if (*(s+1) == PATH_DELIMITER || *(s+1) == '\0')
1247       {
1248         if (s > env_value && *(s-1) == PATH_SEP)
1249           strcpy(s, s+1); /* just flush the ! */
1250       }
1251     }
1252     s++;
1253   }
1254   if (trace_flag)
1255   {
1256     sprintf(log_line,"Now is %s\n", env_value);
1257     show_line(log_line, 0);
1258   }
1259 }
1260 #endif
1261
1262 /* Return a NULL-terminated array of directory names, each name ending
1263    with PATH_SEP, created by parsing the PATH_DELIMITER-separated list
1264    in the value of the environment variable ENV_NAME, or DEFAULT_PATH if
1265    the env var is not set.
1266    
1267    A leading or trailing PATH_DELIMITER in the value of ENV_NAME is replaced
1268    by DEFAULT_PATH.
1269    
1270    Any element of the path that ends with double PATH_SEP characters
1271    (e.g., `foo//') is replaced by all its subdirectories.
1272
1273    If ENV_NAME is null, only parse DEFAULT_PATH.  If both are null, do
1274    nothing and return NULL.  */
1275
1276 string * initialize_path_list (string env_name,  string default_path)
1277 {
1278   string dir, path;
1279   string *dir_list;
1280   unsigned dir_count = 0;
1281   string env_value;
1282   string orig_path;
1283   struct _finddata_t findt;
1284 /*  _finddata_t structure *can* be reused, unlike _find_t 95/Jan/31 */
1285 /*  so save on stack space, by having one copy here, not one per expand_subdir*/
1286
1287 /*  env_value = env_name ?  getenv (env_name)  : NULL; */
1288   env_value = env_name ?  grabenv (env_name)  : NULL; /* 1994/May/19 */
1289
1290 /*  need to convert \ to / as soon as possible to avoid confusion */
1291 /*  we may be modifying DOS environment variable here ... is it always safe ? */
1292 #ifdef MSDOS
1293   if (deslash) unixify (env_value);       /* 1994/Feb/24 */
1294 #endif
1295   if (trace_flag) {
1296     if (env_name) {     /* only if env_name is non-null 94/Feb/24 */
1297       sprintf(log_line, "\nSet %s=", env_name);
1298       show_line(log_line, 0);
1299       if (env_value) {  /* only if env_name value is set */
1300         show_line(env_value, 0);
1301       }
1302       show_line("\n", 0);
1303     }
1304   }
1305 #ifdef MSDOS
1306 /*  strip off trailing white space which would confuse things - bkph  */
1307   striptrailing (env_value, env_name, default_path);
1308   convertexclam (env_value);              /* 97/Mar/22 */
1309 #endif
1310   orig_path = expand_default (env_value, default_path);
1311
1312   if (orig_path == NULL || *orig_path == 0)  return NULL;
1313
1314 /*  need to convert \ to / as soon as possible to avoid confusion */
1315 #ifdef MSDOS
1316   if (deslash)  unixify (orig_path);  /* redundant ? */
1317 #endif
1318
1319 /*  If we've already seen this PATH_DELIMITER-separated list, then just get
1320     it back instead of going back to the filesystem.  */
1321   dir_list = find_dir_list (orig_path);
1322   if (dir_list != NULL) return dir_list;
1323   
1324 /*  Be sure `path' is in writable memory.  */ /* if not, copy it */
1325   path = (orig_path == env_value || orig_path == default_path
1326           ? xstrdup (orig_path) : orig_path); 
1327
1328 /*  Find each element in the path in turn.  */
1329 #ifdef MSDOS
1330 /*  if (!switchflag) */ 
1331   if (current_flag) {   /* suppress adding current directory - debugging */
1332     if (trace_flag) {
1333       sprintf(log_line, "Adding directory `%s'\n", "."); /* 95/Jan/24 */
1334       show_line(log_line, 0);
1335     }
1336     add_directory(&dir_list, &dir_count, ".");
1337   }
1338 #endif
1339   for (dir = strtok (path, PATH_DELIMITER_STRING); dir != NULL;
1340     dir = strtok (NULL, PATH_DELIMITER_STRING)) {
1341     int len;
1342
1343 // #ifdef MYDEBUG
1344   if (trace_flag) {
1345     sprintf(log_line, "dir %s\n", dir);
1346     show_line(log_line, 0);
1347   }
1348 // #endif
1349       /* If the path starts with ~ or ~user, expand it.  Do this
1350          before calling `expand_subdir' or `add_directory', so that
1351          1) we don't expand the same ~ for every subdirectory; and 
1352          2) pathnames in `expand_subdir' don't have a `~' in them
1353             (since the system won't grok `~/foo' as a directory).  */
1354 #ifndef MSDOS
1355     dir = expand_tilde (dir);
1356 #endif
1357     len = strlen (dir);
1358
1359       /* If `dir' is the empty string, ignore it.  */
1360     if (len == 0)  continue;
1361
1362       /* If `dir' ends in double PATH_SEP, do subdirectories (and remove
1363          the second PATH_SEP, so the final pathnames we return don't look
1364          like foo//bar).  Because we obviously want to do subdirectories
1365          of `dir', we don't check if it is a leaf.  This means that if
1366          `dir' is `foo//', and `foo' contains only symlinks (so our leaf
1367          test below would be true), the symlinks are chased.  */
1368
1369 /* modified to treat single PATH_SEP as expand subdirs without recursion */
1370 /* modified to treat double PATH_SEP as expand subdirs *with*  recursion */
1371
1372 #ifdef MSDOS
1373     if (len > 2 &&                /* 1994/Mar/1 */
1374       (dir[len - 1] == PATH_SEP || dir[len - 1] == '\\')
1375         && (dir[len - 2] == PATH_SEP || dir[len - 2] == '\\'))
1376 #else
1377     if (len > 2 && dir[len - 1] == PATH_SEP && dir[len - 2] == PATH_SEP) 
1378 #endif
1379     {
1380       if (open_trace_flag) {
1381         sprintf(log_line, "Double backslash on `%s' ", dir);  /* bkph */
1382         show_line(log_line, 0);
1383       }
1384
1385       dir[len - 1] = 0;
1386       if (dir_p (dir)) {
1387         if (trace_flag) {
1388           sprintf(log_line, "Adding directory `%s'\n", dir);
1389           show_line(log_line, 0);
1390         }
1391         add_directory (&dir_list, &dir_count, dir);
1392 /* local variable 'findt' used without having been initialized ? &findt ? */
1393         expand_subdir (&dir_list, &dir_count, dir, findt, 1);  /* 95/Jan/31 */
1394       }
1395     }
1396 /* following is new to find only directories to one level 1994/Jan/24 */
1397 #ifdef MSDOS
1398     else if (len > 1 &&         /* 1994/Mar/1 */
1399       (dir[len - 1] == PATH_SEP || dir[len - 1] == '\\'))
1400 #else
1401     else if (len > 1 && dir[len - 1] == PATH_SEP) 
1402 #endif
1403         {
1404       if (open_trace_flag) {
1405         sprintf(log_line, "Single backslash on `%s' ", dir);  /* bkph */
1406         show_line(log_line, 0);
1407       }
1408
1409 /*      dir[len - 1] = 0; */
1410       if (dir_p (dir)) {
1411         if (trace_flag) {
1412           sprintf(log_line, "Adding directory `%s'\n", dir);
1413           show_line(log_line, 0);
1414         }
1415         add_directory (&dir_list, &dir_count, dir);
1416         expand_subdir (&dir_list, &dir_count, dir,
1417             findt, 0); /* 95/Jan/31 */
1418       }
1419     }
1420       else { /* Don't bother to add the directory if it doesn't exist.  */
1421       if (dir_p (dir)) {
1422         if (trace_flag) {
1423           sprintf(log_line, "Adding directory `%s'\n", dir);
1424           show_line(log_line, 0);
1425         }
1426         add_directory (&dir_list, &dir_count, dir);
1427       }
1428     }
1429   }
1430   
1431 // #ifdef MYDEBUG
1432   if (trace_flag) {
1433     show_line("Adding terminating null\n", 0);
1434   }
1435 // #endif
1436
1437 /*  Add the terminating null entry to `dir_list'.  */
1438   dir_count++;
1439   XRETALLOC (dir_list, dir_count, string);
1440   dir_list[dir_count - 1] = NULL;
1441   
1442 /*  Save the directory list we just found.  */
1443   save_dir_list (orig_path, dir_list);
1444
1445   return dir_list;
1446 }
1447
1448 /* Subroutines for `initialize_path_list'.  */
1449
1450 /* Add a newly-allocated copy of DIR to the end of the array pointed to
1451    by DIR_LIST_PTR. Increment DIR_COUNT_PTR.  Append a `/' to DIR if
1452    necessary.  We assume DIR is a directory, to avoid an unnecessary
1453    call to `stat'.  */
1454
1455 void add_directory (string **dir_list_ptr, unsigned *dir_count_ptr, string dir)
1456 {
1457   if (dir == NULL) return;        /* paranoia 1995/Jan/24 */
1458   /* If `dir' does not end with a `/', add it.  We can't just
1459      write it in place, since that would overwrite the null that
1460      strtok may have put in.  So we ALWAYS make a copy of DIR.  */
1461 #ifdef MSDOS
1462   dir = (dir[strlen (dir) - 1] == PATH_SEP ||   /* 1994/Mar/1 */
1463       dir[strlen (dir) - 1] == '\\') ?
1464         xstrdup (dir) : concat (dir, PATH_SEP_STRING);
1465 #else
1466   dir = (dir[strlen (dir) - 1] == PATH_SEP ? xstrdup (dir)
1467          : concat (dir, PATH_SEP_STRING)); 
1468 #endif
1469 #ifdef MSDOS
1470   if (deslash) unixify (dir);     /* redundant ? bkph */ 
1471 #endif
1472
1473 // #ifdef MYDEBUG
1474   if (trace_flag) {
1475     sprintf(log_line, "Adding directory `%s'\n", dir);
1476     show_line(log_line, 0);
1477   }
1478 // #else
1479 //     if (open_trace_flag) {
1480 //    sprintf(log_line, "Adding directory `%s' ", dir);
1481 //    show_line(log_line, 0);
1482 //  }
1483 // #endif
1484
1485   /* Add `dir' to the list of the directories.  */
1486   (*dir_count_ptr)++;
1487   XRETALLOC (*dir_list_ptr, *dir_count_ptr, string);
1488   (*dir_list_ptr)[*dir_count_ptr - 1] = dir;
1489 }
1490 /* 1994/Jan/25 */
1491 void lowercase (char *s)
1492 {
1493   while (*s)
1494     *s++ = (char) tolower(*s);
1495 }
1496
1497
1498 /* These routines, while not strictly needed to be exported, are
1499    plausibly useful to be called by outsiders.  */
1500
1501 /* Replace a leading or trailing PATH_DELIMITER in ENV_PATH with
1502    DEFAULT_PATH.  If neither is present, return ENV_PATH if that is 
1503    non-null, else DEFAULT_PATH.  */
1504
1505 string  expand_default (string env_path, string default_path)
1506 {
1507   string expansion;
1508   
1509   if (env_path == NULL) expansion = default_path;
1510   else if (*env_path == PATH_DELIMITER)
1511     expansion = concat (default_path, env_path);
1512   else if (env_path[strlen (env_path) - 1] == PATH_DELIMITER)
1513     expansion = concat (env_path, default_path);
1514   else expansion = env_path;
1515   
1516   if (trace_flag) {               /* 1994/Jan/8 */
1517     if (env_path == NULL) {
1518       sprintf(log_line, "Using the default %s\n", expansion);
1519       show_line(log_line, 0);
1520     }
1521     else if (expansion == env_path) {
1522       sprintf(log_line, "Using %s (default was %s)\n", expansion, default_path);
1523       show_line(log_line, 0);
1524     }
1525     else {                /* expansion != env_path */
1526       sprintf(log_line, "Expanded %s (default was %s) to %s\n",
1527         env_path, default_path, expansion);
1528       show_line(log_line, 0);
1529     }
1530   }
1531   return expansion;
1532 }
1533
1534
1535 #ifndef MSDOS
1536 /* Expand a leading ~ or ~user, Unix-style, unless we are some weirdo
1537    operating system.  */
1538
1539 string expand_tilde (string name)
1540 {
1541 #if defined (MSDOS) || defined (VMS) || defined (VMCMS)
1542   return name;
1543 #else
1544   string expansion;
1545   string home;
1546   
1547   /* If no leading tilde, do nothing.  */
1548   if (*name != '~')
1549     expansion = name;
1550   
1551   /* If `~' or `~/', use $HOME if it exists, or `.' if it doesn't.  */
1552   else if (name[1] == PATH_SEP || name[1] == 0)     /* not in DOS */
1553     {
1554       home = getenv ("HOME");             /* not in DOS */
1555       if (home == NULL)
1556         home = ".";
1557         
1558       expansion
1559         = name[1] == 0 ? home : concat3 (home, PATH_SEP_STRING, name + 2);
1560     }
1561   
1562   /* If `~user' or `~user/', look up user in the passwd database.  */
1563   else
1564     {
1565       struct passwd *p;
1566       string user;
1567       unsigned c = 2;
1568       while (name[c] != PATH_SEP && name[c] != 0) /* not in DOS */
1569         c++;
1570       
1571       user = (string) xmalloc (c);
1572       strncpy (user, name + 1, c - 1);
1573       user[c - 1] = 0;
1574       
1575       /* We only need the cast here for those (old deficient) systems
1576          which do not declare `getpwnam' in <pwd.h>.  */
1577       p = (struct passwd *) getpwnam (user);
1578       free (user);
1579       /* If no such user, just use `.'.  */
1580       home = p == NULL ? "." : p->pw_dir;
1581       
1582       expansion = name[c] == 0 ? home : concat (home, name + c);
1583     }
1584   
1585   return expansion;
1586 #endif /* not (DOS or VMS or VM/CMS) */
1587 }
1588 #endif
1589
1590 // structure used for manipulation dir lists
1591
1592 typedef struct {
1593   string path;
1594   string *dir_list;
1595 } saved_path_entry;
1596
1597 /* Routines to save and retrieve a directory list keyed by the original
1598    PATH_DELIMITER-separated path.  This is useful because 1) it can take a
1599    significant amount of time to discover all the subdirectories of a
1600    given directory, and 2) many paths all have the same basic default,
1601    and thus would recompute the directory list.  */
1602
1603 static saved_path_entry *saved_paths = NULL;
1604 static unsigned saved_paths_length = 0;
1605
1606 /* We implement the data structure as a simple linear list, since it's
1607    unlikely to ever be more than a dozen or so elements long.  We don't
1608    bother to check here if PATH has already been saved; we always add it
1609    to our list.  */
1610 /* NOT USED! */
1611 void save_dir_list (string path,  string *dir_list)
1612 {
1613 //  saved_paths_length++;
1614   XRETALLOC (saved_paths, saved_paths_length+1, saved_path_entry);
1615   saved_paths[saved_paths_length].path = path;
1616   saved_paths[saved_paths_length].dir_list = dir_list;
1617   saved_paths_length++;
1618 }
1619
1620 /* When we retrieve, just check the list in order.  */
1621 /* NOT USED! */
1622 string *find_dir_list (string path)
1623 {
1624   unsigned p;
1625   
1626 // #ifdef MYDEBUG
1627   if (trace_flag) {
1628     sprintf(log_line, "Find Dir List for path: %s\n", path);
1629     show_line(log_line, 0);
1630   }
1631 // #endif
1632
1633   for (p = 0; p < saved_paths_length; p++) {
1634     if (strcmp (saved_paths[p].path, path) == 0)
1635       return saved_paths[p].dir_list;
1636   }
1637   return NULL;
1638 }
1639
1640 /* Unixify filename and path (turn \ into /) --- assumes null terminated */
1641 /* NEED HACK! */
1642 char *unixify (char * t)
1643 {
1644   char * s = t;
1645   if (s == NULL) return s;    /* paranoia -- 1993/Apr/10 */
1646 #ifdef MSDOS
1647   if (t != '\0') {
1648     while (*s != '\0') {        /* paranoia -- 1997/Oct/23 */
1649 /*      if (*s == '\\') *s = '/'; */
1650       if (*s == '\\') *s = PATH_SEP;
1651       s++;
1652     }       /* endwhile */
1653   }
1654 // #ifdef MYDEBUG
1655   if (trace_flag) {
1656     sprintf(log_line, "Unixified name: %s\n", t);
1657     show_line(log_line, 0);
1658   }
1659 // #endif
1660 #endif /* DOS */
1661   return t;
1662 }
1663
1664 /****************************************************************************/
1665
1666 /* moved here to avoid problems with pragma */
1667
1668 /* struct _finddata_t findt; */ /* make global, can be reused unlike _find_t */
1669                                 /* avoids heavy stack usage in tree search */
1670                                 /* but ties up some global fixed space ... */
1671
1672 //#pragma optimize ("g", off)   /* try and avoid compiler bug here _dos_find */
1673
1674 /* Add DIRNAME to DIR_LIST and look for subdirectories, possibly recursively.
1675    We assume DIRNAME is the name of a directory.  */
1676
1677 /* NOTE: _dos_find... prevents running under Windows NT as console app ??? */
1678 /* Yes, so lets flush it! use _findfirst, _findnext, _findclose instead */
1679
1680 /* called only from initialize_path_list  (and recursively) */
1681 /* kpathsea/elt-dirs.c */
1682 /* renamed to do_subdir */
1683 void expand_subdir (string **dir_list_ptr, unsigned *dir_count_ptr, string dirname,
1684           struct _finddata_t findt, integer recurseflag)
1685 {
1686 #ifdef MSDOS
1687 /*  struct _finddata_t findt; */
1688   long hFind;
1689   int ret;
1690   int len;
1691 /*  char buffer[PATH_MAX]; */   /* pretty long? potential recursion problem? */
1692   char buffer[FILENAME_MAX];  /* this is DOS and Windows NT after all ... */
1693   char *potential;
1694 #endif  /* DOS */
1695
1696   if (trace_flag)
1697   {
1698     sprintf(log_line, "\nExpanding sub dir %s ", dirname);
1699     show_line(log_line, 0);
1700   }
1701
1702 #ifdef MSDOS
1703   strcpy(buffer, dirname);
1704   len = strlen(dirname);
1705
1706 #ifdef MSDOS
1707 /*  if (buffer[len-1] == PATH_SEP) strcat(buffer, "*.*"); */
1708   if (buffer[len-1] == PATH_SEP || buffer[len-1] == '\\')
1709     strcat(buffer, "*.*");            /* 1994/Mar/1 */
1710   else strcat(buffer, PATH_SEP_STRING "*.*");
1711 #else
1712   if (buffer[len-1] == PATH_SEP) strcat(buffer, "*");
1713   else strcat(buffer, PATH_SEP_STRING "*");
1714 #endif  /* MSDOS */
1715
1716 /*  Note: the _A_SUBDIR means we get ordinary files PLUS sub-directories */
1717   if (open_trace_flag)  {
1718     sprintf(log_line, "\nDIRNAME `%s' ", dirname);
1719     show_line(log_line, 0);
1720   }
1721 /*  we'll need to step over `.' and `..' up front of directory list */
1722   hFind = _findfirst(buffer, &findt);
1723   if (hFind > 0) ret = 0;
1724   else ret = -1;
1725 /*  _dos_findnext( &findt ); */
1726 /*  while(_dos_findnext(&findt)== 0)  { */
1727   while (ret == 0)  {
1728 /*    if (open_trace_flag) */
1729     if (open_trace_flag && trace_flag) {
1730       sprintf(log_line, "NEXT `%s' (%0x) ", findt.name, findt.attrib);
1731       show_line(log_line, 0);
1732     }
1733 /*    if (strchr(findt.name, '.') != NULL) continue; *//* not needed */
1734     if (findt.name[0] != '.' &&   /* ignore "." and ".." */
1735       findt.attrib & _A_SUBDIR){  /* only look at SUBDIRs */
1736       if (open_trace_flag)  {
1737         sprintf(log_line, "\nDIRNAME `%s' ", dirname);
1738         show_line(log_line, 0);
1739       }
1740 #ifdef MSDOS
1741       potential = concat3(dirname,
1742         (dirname[len-1] == PATH_SEP || dirname[len-1] == '\\')
1743           ? "" : PATH_SEP_STRING, findt.name);
1744 #else
1745       potential = concat3(dirname, dirname[len-1] == PATH_SEP 
1746         ? "" : PATH_SEP_STRING, findt.name);
1747 #endif  /* DOS */
1748       lowercase (potential);          /* make look nicer ? */
1749       if (open_trace_flag) {
1750         sprintf(log_line, "POTENTIAL `%s' ", potential);
1751         show_line(log_line, 0);
1752       }
1753       if (trace_flag) {
1754         sprintf(log_line, "Adding directory `%s'\n", potential); /* 95/Jan/24 */
1755         show_line(log_line, 0);
1756       }
1757       add_directory(dir_list_ptr, dir_count_ptr, potential);
1758       if (recurseflag) 
1759         expand_subdir(dir_list_ptr, dir_count_ptr,
1760             potential, findt, 1);  /* 95/Jan/31 */
1761       free(potential);
1762     }     /* end of findt.attrib & _A_SUBDIR != 0 */
1763     ret = _findnext(hFind, &findt);
1764   }
1765 #ifdef MSDOS
1766   if (hFind > 0) _findclose (hFind);
1767 #endif
1768
1769 #ifndef MSDOS
1770   _dos_findclose(&findt);
1771 #endif
1772
1773 #else  /* end of MSDOS (way up there) */
1774
1775 /*  This is how we do this if we are NOT using DOS */
1776   DIR *dir;
1777   struct dirent *e;
1778   unsigned length;
1779   char potential[PATH_MAX];
1780   struct _stat st;
1781   
1782    /* We will be looking at its contents.  */
1783   dir = opendir (dirname);
1784   if (dir == NULL)
1785     return;
1786   
1787   /* Compute the length of DIRNAME, since it's loop-invariant.  */
1788   length = strlen (dirname);
1789
1790   /* Construct the part of the pathname that doesn't change.  */
1791   strcpy (potential, dirname);
1792   if (potential[length - 1] != PATH_SEP)  /* not in DOS */
1793     {
1794       potential[length] = PATH_SEP;
1795       potential[length + 1] = 0;
1796       length++;
1797     }
1798   
1799 /* about to use _stat --- shouldn't get here when using MSDOS anyway */
1800
1801   while ((e = readdir (dir)) != NULL)
1802     { /* If it's . or .., never mind.  */
1803       if (!(e->d_name[0] == '.'
1804             && (e->d_name[1] == 0
1805                 || (e->d_name[1] == '.' && e->d_name[2] == 0))))
1806         { /* If it's not a directory, we will skip it on the
1807              recursive call.  */
1808           strcat (potential, e->d_name);
1809
1810           /* If we can't _stat it, or if it isn't a directory, continue.  */
1811           if (_stat (potential, &st) == 0 && S_ISDIR (st.st_mode))
1812             { /* It's a subdirectory; add `potential' to the list.  */
1813         if (trace_flag) {
1814           sprintf(log_line, "Adding directory `%s'\n", potential); /* 95/Jan/24 */
1815           show_line(log_line, 0);
1816         }
1817               add_directory (dir_list_ptr, dir_count_ptr, potential);
1818
1819               /* If it's not a leaf, quit.  Assume that leaf
1820                  directories have two links (one for . and one for ..).
1821                  This means that symbolic links to directories do not affect
1822                  the leaf-ness.  This is arguably wrong, but the only
1823                  alternative I know of is to _stat every entry in the
1824                  directory, and that is unacceptably slow.  */
1825               if (st.st_nlink > 2)
1826                 { /* All criteria are met; find subdirectories.  */
1827                   expand_subdir (dir_list_ptr, dir_count_ptr, potential,
1828             findt, 1);  /* 95/Jan/31 */
1829                 }
1830             }
1831
1832           /* ``Remove'' the directory entry name.  */
1833           potential[length] = 0;
1834         }
1835     }
1836   
1837   closedir (dir);
1838 #endif  /* end of *not* DOS case */
1839 }
1840
1841 char *get_env_shroud (char *);    /* in texmf.c */