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.
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.
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
23 #pragma warning(disable:4115) // kill rpcasync.h complaint
25 #define MYLIBAPI __declspec(dllexport)
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
40 #include <io.h> // needed for _finddata_t
41 #include <ctype.h> // needed for isascii and isalpha
43 #define NAME_MAX 255 // max size of name component
45 #define ISALPHA(c) (isascii (c) && isalpha(c))
48 #define PATH_SEP_STRING "/"
49 #define PATH_DELIMITER ';'
50 #define PATH_DELIMITER_STRING ";"
52 // default paths to look for things
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"
60 // structure used by fontmap
62 typedef struct map_element_struct
66 struct map_element_struct * next;
69 typedef map_element_type **map_type;
71 extern bool usesourcedirectory; /* in local.c */
73 extern bool workingdirectory; /* in local.c */
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);
89 // the following do *not* use MALLOC
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 */
94 /////////////////////////////////////////////////////////////////////////
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)
108 final_code = EXIT_SUCCESS;
109 else if (unix_code == 1)
110 final_code = EXIT_FAILURE;
112 final_code = unix_code;
116 show_line("Jump Buffer already used\n", 1);
123 /* texk/web2c/lib/zround.c */
124 integer zround (double r)
128 if (r > 2147483647.0)
130 else if (r < -2147483647.0)
133 i = (integer) (r + 0.5);
135 i = (integer) (r - 0.5);
139 /* texk/web2c/lib/eofeoln.c */
140 bool eoln (FILE * file)
150 (void) ungetc (c, file);
152 return c == '\n' || c == '\r' || c == EOF;
155 char * read_a_line (FILE *f, char *line, int limit)
160 while ((c = getc (f)) != EOF)
162 if (c == '\n' || c == '\r')
165 else continue; /* ignore \r\n and blank lines */
168 line[loc] = (char) c;
171 if (loc == limit - 1)
173 sprintf(log_line, " ERROR: line too long\n");
174 show_line(log_line, 1);
181 if (c != EOF || loc > 0)
183 line[loc] = '\0'; /* terminate */
184 return line; /* and return */
187 return(NULL); /* true EOF */
189 /****************************************************************************************/
191 /* from ourpaths.c */
193 #define BUILDNAMEDIRECT /* avoid malloc for string concat */
195 #define CACHEFILENAME /* cache last full path/file name 96/Nov/16 */
196 /* to speed up LaTeX 2e which opens files twice */
198 /* `path_dirs' is initialized in `set_paths', to a null-terminated array
199 of directories to search for. */
201 static string *path_dirs[LAST_PATH];
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)
209 int n; /* 97/Apr/2 */
210 char *s, *t, *u; /* 94/Jan/6 */
211 char buffer[PATH_MAX];
214 if (path_bits & TEXFORMATPATHBIT)
219 if (grabenv(s) == NULL)
221 strcpy(buffer, texpath);
222 strcat(buffer, PATH_SEP_STRING);
223 strcat(buffer, "fmt");
227 sprintf(log_line, "Checking `%s' = %s %s %s\n", buffer, texpath, PATH_SEP_STRING, "fmt");
228 show_line(log_line, 0);
236 if (getenv(s) == NULL) s = "TEXFMT";
241 sprintf(log_line, "\nSetting up %s (default %s) ", "TEXFORMATS", t);
242 show_line(log_line, 0);
246 path_dirs[TEXFORMATPATH] = initialize_path_list (s, t);
249 if (path_bits & TFMFILEPATHBIT)
253 if ((u = grabenv("ENCODING")) != NULL)
257 if ((t = grabenv(u)) != NULL)
259 if (strchr(t, ':') != NULL && sscanf(t, "%d", &n) == 0)
268 if (grabenv(s) == NULL)
270 strcpy(buffer, texpath);
271 strcat(buffer, PATH_SEP_STRING);
272 strcat(buffer, "tfm");
276 sprintf(log_line, "Checking `%s' = %s %s %s\n",
277 buffer, texpath, PATH_SEP_STRING, "tfm");
278 show_line(log_line, 0);
286 if (getenv(s) == NULL) s = "TEXTFM";
291 sprintf(log_line, "\nSetting up %s (default %s) ", "TEXFONTS", t);
292 show_line(log_line, 0);
296 path_dirs[TFMFILEPATH] = initialize_path_list (s, t);
299 if (path_bits & TEXINPUTPATHBIT)
305 if (grabenv(s) == NULL)
311 if (grabenv(s) == NULL)
317 sprintf(log_line, "\nSetting up %s ", "TEXINPUTS");
318 show_line(log_line, 0);
322 path_dirs[TEXINPUTPATH] = initialize_path_list (s, TEXINPUTS);
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 */
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
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)
344 #ifdef BUILDNAMEDIRECT
345 char buffer[PATH_MAX]; /* for constructing name 1996/Jan/20 */
346 int foundflag; /* true if path found */
355 /* If file name and path_index matches - and saved filename exists */
356 /* then use cached full path / file name 96/Nov/16 */
359 if (path_index == last_path_index &&
360 strcmp((const char *)name, last_name) == 0 && *last_filename != '\0')
364 sprintf(log_line, "\nFOUND `%s' (%d) IN CACHE: `%s' ", name, path_index, last_filename);
365 show_line(log_line, 0);
368 strcpy((char *)name, last_filename);
372 last_path_index = path_index;
373 strcpy(last_name, (const char *)name);
374 *last_filename = '\0'; /* in case not found */
378 #ifdef BUILDNAMEDIRECT
379 foundflag = xfind_path_filename (buffer, (char *)name, path_dirs[path_index]);
381 foundname = find_path_filename (name, path_dirs[path_index]);
384 #ifdef BUILDNAMEDIRECT
385 if (foundflag == 0 && path_index == TFMFILEPATH)
387 if (foundname == NULL && path_index == TFMFILEPATH)
391 static map_type fontmap = NULL; /* GLOBAL, so won't recreate */
397 sprintf(log_line, "Loading in texfonts.map file for %s\n", name);
398 show_line(log_line, 0);
401 fontmap = map_create (path_dirs[path_index]);
404 mapped_name = map_lookup (fontmap, (char *) name);
408 #ifdef BUILDNAMEDIRECT
409 foundflag = xfind_path_filename (buffer, mapped_name, path_dirs[path_index]);
411 foundname = find_path_filename (mapped_name, path_dirs[path_index]);
423 #ifdef BUILDNAMEDIRECT
426 sprintf(log_line, "`%s' in test_read_access\n", buffer);
427 show_line(log_line, 0);
430 if (foundname != NULL)
432 sprintf(log_line, "`%s' in test_read_access\n", foundname);
433 show_line(log_line, 0);
438 /* If we found it somewhere, save it. */
439 #ifdef BUILDNAMEDIRECT
442 strcpy ((char *)name, buffer);
447 strcpy(last_filename, buffer); /* full path */
452 if (foundname != NULL)
454 strcpy (name, foundname);
458 strcpy(last_filename, foundname); /* full path */
459 last_namelength = strlen(buffer);
465 #ifdef BUILDNAMEDIRECT
468 if (foundname == NULL) return FALSE;
470 if (foundname != name) free (foundname); /* copied, now free ??? 96/Jan/10 */
476 /********************************************************************/
478 /* Fontname mapping. We use a straightforward hash table. */
482 /* The hash function. We go for simplicity here. */
484 static unsigned map_hash (char * key)
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++;
495 /* Look up STR in MAP. Return the corresponding `value' or NULL. */
497 static char *map_lookup_str (map_type map, char *key)
500 unsigned n = map_hash (key);
502 for (p = map[n]; p != NULL; p = p->next)
503 if (strcmp(key, p->key) == 0) return p->value;
509 /* Look up KEY in MAP; if it's not found, remove any suffix from KEY and
512 char *map_lookup (map_type map, char *key)
514 string suffix = find_suffix (key);
515 string ret = map_lookup_str (map, key);
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
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
528 /* Append the same suffix we took off, if necessary. */
529 /* what if suffix is NULL ??? */ /* what if we didn't take off suffix ??? */
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 ?
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 */
542 void map_insert (map_type map, char *key, char *value)
544 unsigned n = map_hash (key);
545 map_element_type **ptr = &map[n];
546 /* map_element_type ***trailer = &p; */
548 while (*ptr != NULL && !(strcmp(key, (*ptr)->key) == 0)) {
549 /* *p = (*p)->next; */
550 ptr = &((*ptr)->next);
556 *ptr = (map_element_type *) xmalloc (sizeof(map_element_type));
557 (*ptr)->key = xstrdup (key);
558 (*ptr)->value = xstrdup (value);
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. */
573 /* Modified 97/May/17 to avoid malloc for each line read */
579 int map_file_parse (map_type map, char *map_filename)
582 unsigned map_lineno = 0;
585 char line[MAXLINE]; /* 97/May/17 */
589 sprintf(log_line, "Opening %s\n", map_filename); /* 97/May/17 */
590 show_line(log_line, 0);
592 // f = xfopen (map_filename, FOPEN_R_MODE);
593 f = fopen (map_filename, FOPEN_R_MODE);
595 perrormod(map_filename); // should not happen, since we tested
599 while ((l = read_line (f)) != NULL)
601 while ((l = read_a_line (f, line, sizeof(line))) != NULL) /* 97/May/17 */
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 */
612 /* Ignore anything after a % or; */
613 /* if (comment_loc) *comment_loc = 0; */
614 if (comment_loc != NULL) *comment_loc = '\0';
618 /* If we don't have any filename, that's ok, the line is blank. */
619 filename = strtok (l, " \t");
621 if (filename != NULL) {
622 string alias = strtok (NULL, " \t");
624 /* But if we have a filename and no alias, something's wrong. */
625 if (alias == NULL || *alias == 0) {
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);
632 /* We've got everything. Insert the new entry. */
633 map_insert (map, alias, filename);
640 // xfclose (f, map_filename);
641 (void) fclose (f); // we don't care about errors at this stage
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. */
649 /* This is probably quite silly - but what the hell lets leave it in */
651 map_type map_create (string *dir_list)
653 map_type map = (map_type) xcalloc (MAP_SIZE, sizeof (map_element_type *));
656 char filename[PATH_MAX];
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));
667 /* testing access first so xfopen won't fail... */
668 /* maybe do this another way to avoid using `access' ? */
670 if (file_p (filename) != NULL) { /* use file_p the new way */
671 (void) map_file_parse (map, filename);
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);
685 /**********************************************************************/
687 /* #pragma optimize ("g", off) *//* try and avoid compiler bug here _dos_find */
689 /* NOTE: _dos_find... prevents running under Windows NT ??? */
690 /* This is called if file_method != 0 */ /* which is currently the default */
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)
699 struct _finddata_t fi;
705 sprintf(log_line, "Is `%s' a readable file? ", fn);
706 show_line(log_line, 0);
709 /* allow for `normal' (_A_NORMAL) as well as `read-only' files */
711 hFind = _findfirst (fn, &fi);
721 /* check whether found and whether *not* a sub-directory */
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);
728 return fn; /* true - its a file, not a dir */
731 if (open_trace_flag) {
732 sprintf(log_line, "`%s' is a subdirectory. ", fn);
733 show_line(log_line, 0);
735 return NULL; /* false - directory */
739 if (open_trace_flag) {
740 sprintf(log_line, "`%s' is NOT a readable file. ", fn);
741 show_line(log_line, 0);
743 return NULL; /* false - not found or no read access */
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 */
754 /**************************************************************************/
756 /********************************************************************************/
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.
766 In the interests of doing minimal work here, we assume that each
767 element of DIR_LIST already ends with a `/'.
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
775 (Actually, `/' is not hardwired into this routine; we use PATH_SEP,
778 /* xfind_path_filename is used now */
780 /* Called only from test_read_access(...) in ourpaths.c */
782 #ifdef BUILDNAMEDIRECT
784 /* this string allocation / concatination is silly - use fixed buffer! */
786 int xfind_path_filename (string buffer, string filename, string * dir_list)
788 string found_name = NULL;
790 if (buffer == filename)
792 show_line("buffer == filename\n", 1);
795 *buffer = '\0'; /* "" in case we fail */
799 sprintf(log_line, "Find path for `%s' ", filename);
800 show_line(log_line, 0);
803 /* ignore current directory for TFM files ? */ /* 1994/Jan/24 */
804 if (!current_tfm && strstr(filename, ".tfm") != NULL && strcmp(*dir_list, "./") == 0)
808 sprintf(log_line, "Ignoring `.' for %s ", filename);
809 show_line(log_line, 0);
812 dir_list++; /* step over first entry in dir list */
815 if (trace_flag && open_trace_flag)
820 sprintf(log_line, "Find path for `%s' ", filename);
821 show_line(log_line, 0);
822 show_line("- IN: ", 0);
824 while (*pstrs != NULL)
826 sprintf(log_line, "%s ", *pstrs);
827 show_line(log_line, 0);
834 /* Do this before testing for absolute-ness, as a leading ~ will be an
835 absolute pathname. */ /* forget this for DOS ! */
837 filename = expand_tilde (filename);
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 */
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)
858 found_name = file_p (filename);
860 found_name = readable (filename);
862 if (found_name != NULL)
863 strcpy(buffer, found_name);
867 else /* Test if FILENAME is in any of the directories in DIR_LIST. */
873 while (*dir_list != NULL)
875 /* if item is current directory, look in source file directory first */
876 /* provided usesourcedirectory flag is set and workingdirectory in use */
880 if (strcmp(s, "./") == 0)
882 if (firsttime && usesourcedirectory && workingdirectory &&
883 source_direct != NULL && *source_direct != '\0')
889 sprintf(log_line, "Using %s dir %s %s\n", "source", s, "X");
890 show_line(log_line, 0);
893 sourceflag = 1; /* avoid increment of list below */
894 firsttime = 0; /* special stuff only first time */
898 sprintf(log_line, "Using %s dir %s %s\n", "current", s, "X");
899 show_line(log_line, 0);
905 sprintf(log_line, "XCONCAT %s %s in find_path_filename\n",
907 show_line(log_line, 0);
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) {
915 if (! sourceflag) /* 98/Sep/29 repeat in current dir */
916 dir_list++; /* try next */
919 if (found_name != buffer)
920 strcpy(buffer, found_name); /* success */
925 return (*buffer != '\0'); /* true if not empty string */
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 ... */
934 string find_path_filename (string filename, string * dir_list)
936 string found_name = NULL;
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);
944 /* ignore current directory for TFM files ? */ /* 1994/Jan/24 */
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);
952 dir_list++; /* step over first entry in dir list */
955 if (trace_flag && open_trace_flag) { /* debugging trace 1994/Jan/8 */
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);
971 /* Do this before testing for absolute-ness, as a leading ~ will be an
972 absolute pathname. */ /* forget this for DOS ! */
974 filename = expand_tilde (filename);
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 */
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 */
994 else { /* Test if FILENAME is in any of the directories in DIR_LIST. */
995 string save_filename = filename;
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 */
1004 if (strcmp(s, "./") == 0) {
1005 if (firsttime && usesourcedirectory && workingdirectory &&
1006 source_direct != NULL && *source_direct != '\0') {
1009 sprintf(log_line, "Using %s dir %s %s\n", "source", s, "F");
1010 show_line(log_line, 0);
1012 sourceflag = 1; /* avoid increment of list below */
1013 firsttime = 0; /* special stuff only first time */
1015 else if (trace_flag) {
1016 sprintf(log_line, "Using %s dir %s %s\n", "current", s, "F");
1017 show_line(log_line, 0);
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);
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 */
1035 if (found_name != filename)
1036 free (filename); /* don't free if is the one passed in */
1041 return found_name; /* return the allocated name - free later */
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,
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! */
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) */
1059 /* returns NULL or the file name passed in ??? */
1061 /* static string readable (name) */
1062 /* kpathsea/readable.c */
1063 string readable (string name)
1067 if (open_trace_flag) {
1068 sprintf(log_line, "is %s readable? ", name);
1069 show_line(log_line, 0);
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 ? */
1078 if (_access (name, R_OK) == 0) {
1079 if (test_dir_access) { /* check only if we are asked to ... */
1081 if (open_trace_flag) {
1082 sprintf(log_line, "tested read access of directory `%s' ", name);
1083 show_line(log_line, 0);
1091 /* if (_access (name, R_OK) == 0 && !dir_p (name)) ret = name; */
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. */
1103 else if (errno == EACCES) {
1104 if (trace_flag) show_line("Access denied!\n", 0);
1107 else if (errno == ENOENT) {
1108 if (trace_flag) show_line("File or path name not found!\n", 1);
1113 sprintf(log_line, "Unknown access error %d!\n", errno);
1114 show_line(log_line, 0);
1122 /* Truncate any too-long path components in NAME, returning the result. */
1124 string truncate_pathname (string name)
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);
1130 for (; *name; name++)
1132 if (*name == PATH_SEP) /* not in DOS */
1133 { /* At a directory delimiter, reset component length. */
1136 else if (c_len > NAME_MAX)
1137 { /* If past the max for a component, ignore this character. */
1141 /* If we've already copied the max, give up. */
1142 if (ret_len == PATH_MAX)
1148 /* Copy this character. */
1149 ret[ret_len++] = *name;
1156 #endif /* end of ifdef ENAMETOOLONG */
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)
1167 bool explicit_relative;
1170 /* absolute = (*filename == PATH_SEP) */ /* 1994/Mar/1 */
1171 absolute = (*filename == PATH_SEP || *filename == '\\')
1172 || ((filename[1] == ':') && ISALPHA (*filename));
1173 /* || ISALPHA (*filename) && filename[1] == ':'; */
1175 absolute = (*filename == PATH_SEP);
1177 if (absolute) return true; /* don't waste any more time */
1180 explicit_relative = (*filename == '.'
1181 && ((filename[1] == PATH_SEP || filename[1] == '\\')
1182 || (filename[1] == '.' &&
1183 (filename[2] == PATH_SEP || filename[2] == '\\'))));
1185 explicit_relative = (*filename == '.' && (filename[1] == PATH_SEP
1186 || (filename[1] == '.' && filename[2] == PATH_SEP)));
1189 return explicit_relative;
1190 /* return absolute || explicit_relative; */ /* slight rewrite 1994/Feb/13 */
1194 /* note: this strips off trailing white space in actual environment var ... */
1195 void striptrailing (string env_value, string env_name, string default_path)
1198 if (env_name == NULL) { /* 1994/Feb/24 */
1200 sprintf(log_line, "WARNING: no env_name noted, using default %s\n",
1202 show_line(log_line, 0);
1206 if (env_value == NULL) {
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);
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 */
1221 /* convert /! and /!! to / and // 97/Mar/22 */
1225 void convertexclam (string env_value)
1228 if (env_value == NULL) return;
1230 if (strchr(s, '!') == NULL) return;
1231 while ((s = strchr(s, '!')) != NULL)
1235 if (*(s+2) == PATH_DELIMITER || *(s+2) == '\0')
1237 if (s > env_value && *(s-1) == PATH_SEP)
1239 *s = PATH_SEP; /* convert the first ! */
1240 strcpy(s+1, s+2); /* flush the second ! */
1245 { /* single ! */ /* assume already unixified */
1246 if (*(s+1) == PATH_DELIMITER || *(s+1) == '\0')
1248 if (s > env_value && *(s-1) == PATH_SEP)
1249 strcpy(s, s+1); /* just flush the ! */
1256 sprintf(log_line,"Now is %s\n", env_value);
1257 show_line(log_line, 0);
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.
1267 A leading or trailing PATH_DELIMITER in the value of ENV_NAME is replaced
1270 Any element of the path that ends with double PATH_SEP characters
1271 (e.g., `foo//') is replaced by all its subdirectories.
1273 If ENV_NAME is null, only parse DEFAULT_PATH. If both are null, do
1274 nothing and return NULL. */
1276 string * initialize_path_list (string env_name, string default_path)
1280 unsigned dir_count = 0;
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*/
1287 /* env_value = env_name ? getenv (env_name) : NULL; */
1288 env_value = env_name ? grabenv (env_name) : NULL; /* 1994/May/19 */
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 ? */
1293 if (deslash) unixify (env_value); /* 1994/Feb/24 */
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);
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 */
1310 orig_path = expand_default (env_value, default_path);
1312 if (orig_path == NULL || *orig_path == 0) return NULL;
1314 /* need to convert \ to / as soon as possible to avoid confusion */
1316 if (deslash) unixify (orig_path); /* redundant ? */
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;
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);
1328 /* Find each element in the path in turn. */
1330 /* if (!switchflag) */
1331 if (current_flag) { /* suppress adding current directory - debugging */
1333 sprintf(log_line, "Adding directory `%s'\n", "."); /* 95/Jan/24 */
1334 show_line(log_line, 0);
1336 add_directory(&dir_list, &dir_count, ".");
1339 for (dir = strtok (path, PATH_DELIMITER_STRING); dir != NULL;
1340 dir = strtok (NULL, PATH_DELIMITER_STRING)) {
1345 sprintf(log_line, "dir %s\n", dir);
1346 show_line(log_line, 0);
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). */
1355 dir = expand_tilde (dir);
1359 /* If `dir' is the empty string, ignore it. */
1360 if (len == 0) continue;
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. */
1369 /* modified to treat single PATH_SEP as expand subdirs without recursion */
1370 /* modified to treat double PATH_SEP as expand subdirs *with* recursion */
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] == '\\'))
1377 if (len > 2 && dir[len - 1] == PATH_SEP && dir[len - 2] == PATH_SEP)
1380 if (open_trace_flag) {
1381 sprintf(log_line, "Double backslash on `%s' ", dir); /* bkph */
1382 show_line(log_line, 0);
1388 sprintf(log_line, "Adding directory `%s'\n", dir);
1389 show_line(log_line, 0);
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 */
1396 /* following is new to find only directories to one level 1994/Jan/24 */
1398 else if (len > 1 && /* 1994/Mar/1 */
1399 (dir[len - 1] == PATH_SEP || dir[len - 1] == '\\'))
1401 else if (len > 1 && dir[len - 1] == PATH_SEP)
1404 if (open_trace_flag) {
1405 sprintf(log_line, "Single backslash on `%s' ", dir); /* bkph */
1406 show_line(log_line, 0);
1409 /* dir[len - 1] = 0; */
1412 sprintf(log_line, "Adding directory `%s'\n", dir);
1413 show_line(log_line, 0);
1415 add_directory (&dir_list, &dir_count, dir);
1416 expand_subdir (&dir_list, &dir_count, dir,
1417 findt, 0); /* 95/Jan/31 */
1420 else { /* Don't bother to add the directory if it doesn't exist. */
1423 sprintf(log_line, "Adding directory `%s'\n", dir);
1424 show_line(log_line, 0);
1426 add_directory (&dir_list, &dir_count, dir);
1433 show_line("Adding terminating null\n", 0);
1437 /* Add the terminating null entry to `dir_list'. */
1439 XRETALLOC (dir_list, dir_count, string);
1440 dir_list[dir_count - 1] = NULL;
1442 /* Save the directory list we just found. */
1443 save_dir_list (orig_path, dir_list);
1448 /* Subroutines for `initialize_path_list'. */
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
1455 void add_directory (string **dir_list_ptr, unsigned *dir_count_ptr, string dir)
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. */
1462 dir = (dir[strlen (dir) - 1] == PATH_SEP || /* 1994/Mar/1 */
1463 dir[strlen (dir) - 1] == '\\') ?
1464 xstrdup (dir) : concat (dir, PATH_SEP_STRING);
1466 dir = (dir[strlen (dir) - 1] == PATH_SEP ? xstrdup (dir)
1467 : concat (dir, PATH_SEP_STRING));
1470 if (deslash) unixify (dir); /* redundant ? bkph */
1475 sprintf(log_line, "Adding directory `%s'\n", dir);
1476 show_line(log_line, 0);
1479 // if (open_trace_flag) {
1480 // sprintf(log_line, "Adding directory `%s' ", dir);
1481 // show_line(log_line, 0);
1485 /* Add `dir' to the list of the directories. */
1487 XRETALLOC (*dir_list_ptr, *dir_count_ptr, string);
1488 (*dir_list_ptr)[*dir_count_ptr - 1] = dir;
1491 void lowercase (char *s)
1494 *s++ = (char) tolower(*s);
1498 /* These routines, while not strictly needed to be exported, are
1499 plausibly useful to be called by outsiders. */
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. */
1505 string expand_default (string env_path, string default_path)
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;
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);
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);
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);
1536 /* Expand a leading ~ or ~user, Unix-style, unless we are some weirdo
1537 operating system. */
1539 string expand_tilde (string name)
1541 #if defined (MSDOS) || defined (VMS) || defined (VMCMS)
1547 /* If no leading tilde, do nothing. */
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 */
1554 home = getenv ("HOME"); /* not in DOS */
1559 = name[1] == 0 ? home : concat3 (home, PATH_SEP_STRING, name + 2);
1562 /* If `~user' or `~user/', look up user in the passwd database. */
1568 while (name[c] != PATH_SEP && name[c] != 0) /* not in DOS */
1571 user = (string) xmalloc (c);
1572 strncpy (user, name + 1, c - 1);
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);
1579 /* If no such user, just use `.'. */
1580 home = p == NULL ? "." : p->pw_dir;
1582 expansion = name[c] == 0 ? home : concat (home, name + c);
1586 #endif /* not (DOS or VMS or VM/CMS) */
1590 // structure used for manipulation dir lists
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. */
1603 static saved_path_entry *saved_paths = NULL;
1604 static unsigned saved_paths_length = 0;
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
1611 void save_dir_list (string path, string *dir_list)
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++;
1620 /* When we retrieve, just check the list in order. */
1622 string *find_dir_list (string path)
1628 sprintf(log_line, "Find Dir List for path: %s\n", path);
1629 show_line(log_line, 0);
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;
1640 /* Unixify filename and path (turn \ into /) --- assumes null terminated */
1642 char *unixify (char * t)
1645 if (s == NULL) return s; /* paranoia -- 1993/Apr/10 */
1648 while (*s != '\0') { /* paranoia -- 1997/Oct/23 */
1649 /* if (*s == '\\') *s = '/'; */
1650 if (*s == '\\') *s = PATH_SEP;
1656 sprintf(log_line, "Unixified name: %s\n", t);
1657 show_line(log_line, 0);
1664 /****************************************************************************/
1666 /* moved here to avoid problems with pragma */
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 ... */
1672 //#pragma optimize ("g", off) /* try and avoid compiler bug here _dos_find */
1674 /* Add DIRNAME to DIR_LIST and look for subdirectories, possibly recursively.
1675 We assume DIRNAME is the name of a directory. */
1677 /* NOTE: _dos_find... prevents running under Windows NT as console app ??? */
1678 /* Yes, so lets flush it! use _findfirst, _findnext, _findclose instead */
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)
1687 /* struct _finddata_t findt; */
1691 /* char buffer[PATH_MAX]; */ /* pretty long? potential recursion problem? */
1692 char buffer[FILENAME_MAX]; /* this is DOS and Windows NT after all ... */
1698 sprintf(log_line, "\nExpanding sub dir %s ", dirname);
1699 show_line(log_line, 0);
1703 strcpy(buffer, dirname);
1704 len = strlen(dirname);
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 "*.*");
1712 if (buffer[len-1] == PATH_SEP) strcat(buffer, "*");
1713 else strcat(buffer, PATH_SEP_STRING "*");
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);
1721 /* we'll need to step over `.' and `..' up front of directory list */
1722 hFind = _findfirst(buffer, &findt);
1723 if (hFind > 0) ret = 0;
1725 /* _dos_findnext( &findt ); */
1726 /* while(_dos_findnext(&findt)== 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);
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);
1741 potential = concat3(dirname,
1742 (dirname[len-1] == PATH_SEP || dirname[len-1] == '\\')
1743 ? "" : PATH_SEP_STRING, findt.name);
1745 potential = concat3(dirname, dirname[len-1] == PATH_SEP
1746 ? "" : PATH_SEP_STRING, findt.name);
1748 lowercase (potential); /* make look nicer ? */
1749 if (open_trace_flag) {
1750 sprintf(log_line, "POTENTIAL `%s' ", potential);
1751 show_line(log_line, 0);
1754 sprintf(log_line, "Adding directory `%s'\n", potential); /* 95/Jan/24 */
1755 show_line(log_line, 0);
1757 add_directory(dir_list_ptr, dir_count_ptr, potential);
1759 expand_subdir(dir_list_ptr, dir_count_ptr,
1760 potential, findt, 1); /* 95/Jan/31 */
1762 } /* end of findt.attrib & _A_SUBDIR != 0 */
1763 ret = _findnext(hFind, &findt);
1766 if (hFind > 0) _findclose (hFind);
1770 _dos_findclose(&findt);
1773 #else /* end of MSDOS (way up there) */
1775 /* This is how we do this if we are NOT using DOS */
1779 char potential[PATH_MAX];
1782 /* We will be looking at its contents. */
1783 dir = opendir (dirname);
1787 /* Compute the length of DIRNAME, since it's loop-invariant. */
1788 length = strlen (dirname);
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 */
1794 potential[length] = PATH_SEP;
1795 potential[length + 1] = 0;
1799 /* about to use _stat --- shouldn't get here when using MSDOS anyway */
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
1808 strcat (potential, e->d_name);
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. */
1814 sprintf(log_line, "Adding directory `%s'\n", potential); /* 95/Jan/24 */
1815 show_line(log_line, 0);
1817 add_directory (dir_list_ptr, dir_count_ptr, potential);
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 */
1832 /* ``Remove'' the directory entry name. */
1833 potential[length] = 0;
1838 #endif /* end of *not* DOS case */
1841 char *get_env_shroud (char *); /* in texmf.c */