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)
30 #pragma warning(disable:4996)
31 #pragma warning(disable:4131) // old style declarator
32 #pragma warning(disable:4135) // conversion between different integral types
33 #pragma warning(disable:4127) // conditional expression is constant
44 #include <io.h> // needed for _finddata_t
45 #include <ctype.h> // needed for isascii and isalpha
47 //////////////////////////////////////////////////////////////////////////////////
49 #define NAME_MAX 255 // max size of name component
51 #define ISALPHA(c) (isascii (c) && isalpha(c))
54 #define PATH_SEP_STRING "/"
55 #define PATH_DELIMITER ';'
56 #define PATH_DELIMITER_STRING ";"
58 // default paths to look for things
60 #define TEXPATH "C:/yandy/yandytex/"
62 #define TEXFORMATS TEXPATH "fmt"
63 #define TEXPOOL TEXPATH "pool"
64 #define TEXFONTS TEXPATH "tfm"
65 #define TEXINPUTS TEXPATH "tex//;" "C:/tex;" "C:/texinput"
67 // structure used by fontmap
69 typedef struct map_element_struct
73 struct map_element_struct * next;
76 typedef map_element_type **map_type;
78 extern bool usesourcedirectory; /* in local.c */
80 extern bool workingdirectory; /* in local.c */
82 bool absolute_p (string filename);
83 string readable (string name);
84 string truncate_pathname (string name);
85 char * file_p(string fn);
86 bool dir_p(string fn);
87 string *find_dir_list (string path);
88 void add_directory (string **dir_list_ptr, unsigned *dir_count_ptr, string dir);
89 void expand_subdir (string **dir_list_ptr, unsigned *dir_count_ptr, string dirname,
90 struct _finddata_t findt, integer recurseflag);
91 void save_dir_list (string path, string *dir_list);
92 string *initialize_path_list (string env_name, string default_path);
93 int xfind_path_filename (string buffer, string filename, string * dir_list);
94 string expand_default (string env_path, string default_path);
95 map_type map_create (string *dir_list);
96 char *map_lookup (map_type map, char *key);
98 // the following do *not* use MALLOC
100 extern char * xconcat (char *buffer, char *s1, char *s2); /* openinou.c */
101 extern char * xconcat3 (char *buffer, char *s1, char *s2, char *s3); /* openinou.c */
103 /////////////////////////////////////////////////////////////////////////
105 // used only in jump_out in tex0.c, and in texbody in itex.c
106 // and main in texmf.c and a few other abort situations in texmf.c
107 /* texk/web2c/lib/uexit.c */
108 void uexit (int unix_code)
117 final_code = EXIT_SUCCESS;
118 else if (unix_code == 1)
119 final_code = EXIT_FAILURE;
121 final_code = unix_code;
125 show_line("Jump Buffer already used\n", 1);
133 // round a double being careful about very large and very small values
134 // used only in tex0.c, a Pascal function
135 /* texk/web2c/lib/zround.c */
136 integer zround (double r)
140 /* R can be outside the range of an integer if glue is stretching or
141 shrinking a lot. We can't do any better than returning the largest
142 or smallest integer possible in that case. It doesn't seem to make
143 any practical difference. Here is a sample input file which
144 demonstrates the problem, from phil@cs.arizona.edu:
145 \documentstyle{article}
152 djb@silverton.berkeley.edu points out we should testing against
153 TeX's largest or smallest integer (32 bits), not the machine's. So
154 we might as well use a floating-point constant, and avoid potential
155 compiler bugs (also noted by djb, on BSDI). */
156 if (r > 2147483647.0)
158 else if (r < -2147483647.0)
161 i = (integer)(r + 0.5);
163 i = (integer)(r - 0.5);
168 /****************************************************************************/
170 // malloc with error checking and error message
171 /* kpathsea/xmalloc.c */
172 void * xmalloc (unsigned size)
174 void * new_mem = (void*) malloc (size ? size : 1);
176 if (new_mem == NULL) {
177 sprintf(log_line, "malloc: Unable to honor request for %u bytes.\n", size);
178 show_line(log_line, 1);
184 sprintf(log_line, "XMALLOC %d\n", size); /* 1996/Jan/20 */
185 show_line(log_line, 0);
190 // calloc with error checking - used by map_create
191 /* kpathsea/xcalloc.c */
192 address xcalloc (unsigned nelem, unsigned elsize)
194 address new_mem = (address) calloc (nelem, elsize);
197 sprintf(log_line, "Unable to honor request for %u elements of size %u.\n", nelem, elsize);
198 show_line(log_line, 1);
203 /* Return a copy of s in new storage. */ /* xmalloc does error checking */
204 /* kpathsea/xstrdup.c */
205 string xstrdup (string s)
207 string pnew_string = (string) xmalloc (strlen (s) + 1);
211 sprintf(log_line, "XSTRDUP %d %s\n", strlen(s)+1, s);
212 show_line(log_line, 0);
215 return strcpy (pnew_string, s);
217 /* only used by line.c (which in turn is only used by fontmap.c) */
218 /* kpathsea/xrealloc.c */
219 address xrealloc (address old_ptr, unsigned size)
223 if (old_ptr == NULL) new_mem = xmalloc (size);
226 new_mem = (address) realloc (old_ptr, size);
229 sprintf(log_line, "Unable to honor request for %u bytes.\n", size);
230 show_line(log_line, 1);
236 // returns newly allocated string
237 /* kpathsea/concat.c */
238 string concat (string s1, string s2)
244 sprintf(log_line, "CONCAT %s and %s ", s1, s2);
245 show_line(log_line, 0);
248 answer = (string) xmalloc (strlen (s1) + strlen (s2) + 1);
254 sprintf(log_line, "=> %s\n", answer);
255 show_line(log_line, 0);
260 // returns newly allocated string
261 /* kpathsea/concat3.c */
262 string concat3 (string s1, string s2, string s3)
268 sprintf(log_line, "CONCAT3 %s, %s, and %s ", s1, s2, s3);
269 show_line(log_line, 0);
272 answer = (string) xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
279 sprintf(log_line, "=> %s\n", answer);
280 show_line(log_line, 0);
286 /***********************************************************************/
287 // following used only in itex.c on pool file
288 /* Return true if we're at the end of FILE, else false. This implements */
289 /* Pascal's `eof' builtin. */
290 /* It differs from C feof in that the latter is not true at end of file
291 unless an attempt has actually been made to read past EOF */
292 /* texk/web2c/lib/eofeoln.c */
293 bool test_eof (FILE * file)
296 /* Maybe we're already at the end? */
303 if ((c = getc (file)) == EOF)
305 /* We weren't at the end. Back up. */
306 (void) ungetc (c, file);
309 /* Return true on end-of-line in FILE or at the end of FILE, else false. */
310 /* texk/web2c/lib/eofeoln.c */
311 bool eoln (FILE * file)
321 (void) ungetc (c, file);
323 return c == '\n' || c == '\r' || c == EOF; // ???
324 /* Type mismatch (return) (int/enum) ? */
326 /***********************************************************************/
328 // following used only by fontmap.c and openinou.c
330 // #define FATAL_PERROR(s) do { perrormod (s); exit (errno); } while (0)
332 // perrormod puts error message on stdout instead of stderr
334 /* These routines just check the return status from standard library
335 routines and abort if an error happens. */
337 // xfopen used in open_input in openinou.c
338 /* kpathsea/xfopen.c */
339 FILE * xfopen (char *filename, char * fmode)
343 assert(filename && mode);
344 /* if share_flag is non-zero and we are opening for reading use fsopen */
345 /* f = fopen (filename, mode); */
346 if (share_flag == 0 || *fmode != 'r')
347 f = fopen (filename, fmode);
349 f = _fsopen (filename, fmode, share_flag);
352 // FATAL_PERROR (filename);
358 // xfclose not used ...
359 /* kpathsea/xfopen.c */
360 int xfclose (FILE *f, char *filename)
364 if (ferror(f) != 0 || fclose(f) != 0)
366 // FATAL_PERROR (filename);
373 /********************************************************************************/
374 // following used only in map_lookup
375 // return pointer to start of extension --- or NULL if there isn't one
376 /* kpathsea/find-suffix.c */
377 string find_suffix (string name)
382 dot_pos = strrchr (name, '.');
384 if ((slash_pos = strrchr (name, PATH_SEP)) != NULL);
385 else if ((slash_pos = strrchr (name, '\\')) != NULL);
386 else if ((slash_pos = strrchr (name, ':')) != NULL);
387 else slash_pos = name;
389 slash_pos = strrchr (name, PATH_SEP);
392 /* If the name is `foo' or `/foo.bar/baz', we have no extension. */
393 return dot_pos == NULL || dot_pos < slash_pos ? NULL : dot_pos + 1;
395 // remove extension of file name - returns copy or NULL
396 /* kpathsea/rm-suffix.c */
397 string remove_suffix (string s)
400 string suffix = find_suffix (s);
403 suffix--; /* Back up to before the dot. */
404 ret = (char *) xmalloc (suffix - s + 1);
405 strncpy (ret, s, suffix - s);
406 ret[suffix - s] = '\0';
412 // add extension to file name unless it already has one
413 // returns copy or the old one (warning: danger when freeing)
414 /* kpathsea/extend-fname.c */
415 string extend_filename (string name, string default_suffix)
418 string suffix = find_suffix (name);
420 new_s = (suffix == NULL ? concat3 (name, ".", default_suffix) : name);
423 /****************************************************************************************/
426 #define BLOCK_SIZE 64
428 // this returns newly allocated character string
430 char *read_line (FILE * f)
433 unsigned int limit = BLOCK_SIZE;
434 unsigned int loc = 0;
435 char * line = (char *) xmalloc (limit);
437 /* while ((c = getc (f)) != EOF && c != '\n') */
438 while ((c = getc (f)) != EOF && c != '\n' && c != '\r')
440 line[loc] = (char) c;
442 /* By testing after the assignment, we guarantee that we'll always
443 have space for the null we append below. We know we always
444 have room for the first char, since we start with BLOCK_SIZE. */
448 line = (char *) xrealloc (line, limit);
452 /* If we read anything, return it. This can't represent a last
453 ``line'' which doesn't end in a newline, but so what. */
454 /* This is Tom Rokicki's mistake -- lets fix it ! 1994/March/18 */
457 /* Terminate the string. We can't represent nulls in the file,
458 either. Again, it doesn't matter. */
462 { /* c == EOF, but line not empty 1994/March/18 */
466 { /* Real EOF --- at end of file. */
475 /* Modified version 97/May/17 to avoid malloc for every line read ... */
476 char * read_a_line (FILE *f, char *line, int limit)
481 /* while ((c = getc (f)) != EOF && c != '\n') */
482 while ((c = getc (f)) != EOF)
484 if (c == '\n' || c == '\r')
487 else continue; /* ignore \r\n and blank lines */
489 line[loc] = (char) c;
491 if (loc == limit - 1)
492 { /* very unlikely */
493 sprintf(log_line, " ERROR: line too long\n");
494 show_line(log_line, 1);
501 if (c != EOF || loc > 0)
502 { /* normal line or EOF at end of line */
503 line[loc] = '\0'; /* terminate */
504 return line; /* and return */
506 else return(NULL); /* true EOF */
508 /****************************************************************************************/
510 /* from ourpaths.c */
512 #define BUILDNAMEDIRECT /* avoid malloc for string concat */
514 #define CACHEFILENAME /* cache last full path/file name 96/Nov/16 */
515 /* to speed up LaTeX 2e which opens files twice */
517 /* `path_dirs' is initialized in `set_paths', to a null-terminated array
518 of directories to search for. */
520 static string *path_dirs[LAST_PATH];
522 /* This sets up the paths, by either copying from an environment variable
523 or using the default path, which is defined as a preprocessor symbol
524 (with the same name as the environment variable) in `site.h'. The
525 parameter PATH_BITS is a logical or of the paths we need to set. */
526 void set_paths (int path_bits)
528 int n; /* 97/Apr/2 */
529 char *s, *t, *u; /* 94/Jan/6 */
530 char buffer[PATH_MAX];
532 /* eliminated lots of junk not needed for TeX itself 93/Nov/20 bkph */
534 /* added code to look for pool file in format directory also */
535 /* added code to look for fmt directory in tex directory also */
536 /* added code to look for tfm directory in tex directory also */
537 /* added code to look for some PC TeX flavour environment names 94/Jan/6 */
538 /* which, in case of formats, could lead to I'm stymied errors ... */
540 if (path_bits & TEXFORMATPATHBIT) {
543 if (grabenv(s) == NULL) { /* see if env var defined 94/May/19*/
544 strcpy(buffer, texpath); /* not, see if texpath\fmt is directory */
545 strcat(buffer, PATH_SEP_STRING);
546 strcat(buffer, "fmt");
548 sprintf(log_line, "Checking `%s' = %s %s %s\n", buffer, texpath, PATH_SEP_STRING, "fmt"); /* 95/Jan/25 */
549 show_line(log_line, 0);
551 if (dir_p(buffer)) t = xstrdup(buffer); /* 96/Jan/20 */
553 s = "TEXFMTS"; /* added PC-TeX version 94/Jan/6 */
554 if (getenv(s) == NULL) s = "TEXFMT"; /* em-TeX ... */
557 sprintf(log_line, "\nSetting up %s (default %s) ", "TEXFORMATS", t);
558 show_line(log_line, 0);
561 /* path_dirs[TEXFORMATPATH] = initialize_path_list ("TEXFORMATS", TEXFORMATS); */
562 /* path_dirs[TEXFORMATPATH] = initialize_path_list (s, TEXFORMATS); */
563 path_dirs[TEXFORMATPATH] = initialize_path_list (s, t);
564 /* if (t != TEXFORMATS) free (t); */
567 if (path_bits & TEXPOOLPATHBIT) {
570 if (grabenv(s) == NULL) { /* 1994/May/19 */
571 s = "TEXFORMATS"; /* try in format directory next */
572 if (grabenv(s) == NULL) { /* see if environment var defined */
573 strcpy(buffer, texpath); /* no, see if texpath\fmt is direct */
574 strcat(buffer, PATH_SEP_STRING);
575 strcat(buffer, "fmt");
577 sprintf(log_line, "Checking `%s' = %s %s %s\n", buffer, texpath, PATH_SEP_STRING, "fmt"); /* 95/Jan/25 */
578 show_line(log_line, 0);
580 if (dir_p(buffer)) t = xstrdup(buffer); /* 96/Jan/20 */
582 s = "TEXFMTS"; /* added PC-TeX version 94/Jan/6 */
583 if (getenv(s) == NULL) s = "TEXFMT"; /* em-TeX ... */
586 sprintf(log_line, "\nSetting up %s (default %s) ", "TEXPOOL", t);
587 show_line(log_line, 0);
591 /* path_dirs[TEXPOOLPATH] = initialize_path_list ("TEXPOOL", TEXPOOL); */
592 /* path_dirs[TEXPOOLPATH] = initialize_path_list (s, TEXPOOL); */
593 path_dirs[TEXPOOLPATH] = initialize_path_list (s, t);
594 /* if (t != TEXPOOL) free (t); */
597 if (path_bits & TFMFILEPATHBIT) {
599 /* Introduce encoding specific TEXFONTS env variable 97/April/2 */
600 if ((u = grabenv("ENCODING")) != NULL) { /* get ENCODING=... */
601 encoding_name = u; /* remember for error mess */
602 /* sprintf(log_line, "\nENCODING=%s\n", u); */
603 /* ENCODING is defined, now see if matching env variable */
604 if ((t = grabenv(u)) != NULL) { /* get TEXNANSI=c:\yandy\tfm; ... */
605 /* sprintf(loglein, "\nset %s=%s\n", u, t); */
606 /* prevent problems with TEXNANSI=1 and such mistakes! */
607 /* should have a drive letter and not be a number */
608 if (strchr(t, ':') != NULL &&
609 sscanf(t, "%d", &n) == 0) {
610 s = u; /* look here instead of TEXFONTS=... */
611 /* sprintf(log_line, "\nUSE %s\n", u); */
616 t = TEXFONTS; /* #define TEXFONTS TEXPATH "tfm" */
617 /* if (getenv(s) == NULL) { */
618 if (grabenv(s) == NULL) { /* 1994/May/19 */
619 strcpy(buffer, texpath); /* see if texpath\tfm is directory */
620 strcat(buffer, PATH_SEP_STRING);
621 strcat(buffer, "tfm");
623 sprintf(log_line, "Checking `%s' = %s %s %s\n",
624 buffer, texpath, PATH_SEP_STRING, "tfm"); /* 95/Jan/25 */
625 show_line(log_line, 0);
627 /* if (dir_p(buffer)) t = _strdup(buffer); */
628 if (dir_p(buffer)) t = xstrdup(buffer); /* 96/Jan/20 */
630 s = "TEXTFMS"; /* added PC-TeX version 94/Jan/6 */
631 if (getenv(s) == NULL) s = "TEXTFM"; /* em-TeX uses TEXTFM ... */
634 sprintf(log_line, "\nSetting up %s (default %s) ", "TEXFONTS", t);
635 show_line(log_line, 0);
638 /* path_dirs[TFMFILEPATH] = initialize_path_list ("TEXFONTS", TEXFONTS); */
639 /* path_dirs[TFMFILEPATH] = initialize_path_list (s, TEXFONTS); */
640 path_dirs[TFMFILEPATH] = initialize_path_list (s, t);
641 /* if (t != TEXFONTS) free (t); */
644 if (path_bits & TEXINPUTPATHBIT) {
645 if (format_specific) { /* 1994/Oct/25 */
646 s = format_name; /* try specific */
647 if (grabenv(s) == NULL) s = "TEXINPUTS"; /* no format specific */
649 else s = "TEXINPUTS"; /* normal case */
650 /* if (getenv(s) == NULL) */
651 if (grabenv(s) == NULL) { /* 1994/May/19 */
652 s = "TEXINPUT"; /* added PC-TeX vers 94/Jan/6 */
654 sprintf(log_line, "\nSetting up %s ", "TEXINPUTS");
655 show_line(log_line, 0);
658 /* path_dirs[TEXINPUTPATH] = initialize_path_list ("TEXINPUTS", TEXINPUTS); */
659 path_dirs[TEXINPUTPATH] = initialize_path_list (s, TEXINPUTS);
664 char last_filename[PATH_MAX] = ""; /* last full path / file name found C */
665 char last_name[PATH_MAX] = ""; /* last file name searched for C */
666 int last_path_index = -1; /* last path_index */
669 /* Look for NAME, a C string (no longer Pascal), in the colon-separated list
670 of directories given by `path_dirs[PATH_INDEX]'. If the search is
671 successful, leave the full pathname in NAME (which therefore must
672 have enough room for such a pathname), padded with blanks.
673 Otherwise, or if NAME is an absolute or relative pathname, just leave
676 /* changed to take C string 97/June/5 - used to take Pascal strings */
677 /* now expects null terminated strings */
679 bool test_read_access (unsigned char *name, int path_index)
681 #ifdef BUILDNAMEDIRECT
682 char buffer[PATH_MAX]; /* for constructing name 1996/Jan/20 */
683 int foundflag; /* true if path found */
688 if (open_trace_flag) {
689 sprintf(log_line, "Test read access for `%s' ", name); /* C */
690 show_line(log_line, 0);
693 if (*name == '\0') return FALSE; /* sanity check */
696 /* If file name and path_index matches - and saved filename exists */
697 /* then use cached full path / file name 96/Nov/16 */
698 if (cache_file_flag) {
699 if (path_index == last_path_index &&
700 strcmp((const char *)name, last_name) == 0 && *last_filename != '\0') {
701 if (open_trace_flag) {
702 sprintf(log_line, "\nFOUND `%s' (%d) IN CACHE: `%s' ",
703 name, path_index, last_filename);
704 /* name+1, path_index, last_filename); */
705 show_line(log_line, 0);
707 strcpy((char *)name, last_filename);
710 last_path_index = path_index;
711 strcpy(last_name, (const char *)name);
712 *last_filename = '\0'; /* in case not found */
716 /* Look for it. */ /* only call to find_path_filename in pathsrch.c */
717 #ifdef BUILDNAMEDIRECT
718 foundflag = xfind_path_filename (buffer, (char *)name, path_dirs[path_index]);
720 /* this returns either a newly allocated string or name */
721 /* will need to free it later again ... */
722 foundname = find_path_filename (name, path_dirs[path_index]);
724 /* If we didn't find it, and we're looking for a font, maybe it's
725 an alias defined in a mapping file. */
726 /* if (!foundname && path_index == TFMFILEPATH) */
727 #ifdef BUILDNAMEDIRECT
728 if (foundflag == 0 && path_index == TFMFILEPATH)
730 if (foundname == NULL && path_index == TFMFILEPATH)
734 static map_type fontmap = NULL; /* GLOBAL, so won't recreate */
736 /* fault in the mapping if necessary. */
737 if (fontmap == NULL) {
739 sprintf(log_line, "Loading in texfonts.map file for %s\n", name);
740 show_line(log_line, 0);
742 fontmap = map_create (path_dirs[path_index]);
745 /* Now look for our filename in the mapping. */
746 mapped_name = map_lookup (fontmap, (char *) name);
748 /* Found a possibility. Look for the new name. */
749 #ifdef BUILDNAMEDIRECT
750 foundflag = xfind_path_filename (buffer, mapped_name, path_dirs[path_index]);
752 foundname = find_path_filename (mapped_name, path_dirs[path_index]);
754 /* NOTE: mapped_name is NOT an allocated string to be freed ... */
758 if (open_trace_flag) {
759 show_line("\n", 0); /* improve trace format out 94/Jan/8 */
762 if (open_trace_flag) {
763 #ifdef BUILDNAMEDIRECT
764 if (foundflag != 0) {
765 sprintf(log_line, "`%s' in test_read_access\n", buffer);
766 show_line(log_line, 0);
769 if (foundname != NULL) {
770 sprintf(log_line, "`%s' in test_read_access\n", foundname);
771 show_line(log_line, 0);
776 /* If we found it somewhere, save it. */
777 #ifdef BUILDNAMEDIRECT
778 if (foundflag != 0) {
779 strcpy ((char *)name, buffer);
781 if (cache_file_flag) {
782 strcpy(last_filename, buffer); /* full path */
787 if (foundname != NULL) {
788 strcpy (name, foundname);
790 if (cache_file_flag) {
791 strcpy(last_filename, foundname); /* full path */
792 last_namelength = strlen(buffer);
798 #ifdef BUILDNAMEDIRECT
801 if (foundname == NULL) return FALSE;
803 if (foundname != name) free (foundname); /* copied, now free ??? 96/Jan/10 */
809 /********************************************************************/
811 #define STREQ(s1, s2) (strcmp (s1, s2) == 0)
815 /* Fontname mapping. We use a straightforward hash table. */
819 /* The hash function. We go for simplicity here. */
821 static unsigned map_hash (char * key)
825 /* There are very few font names which are anagrams of each other
826 so no point in weighting the characters. */
827 while (*key != 0) n += *key++;
832 /* Look up STR in MAP. Return the corresponding `value' or NULL. */
834 static char *map_lookup_str (map_type map, char *key)
837 unsigned n = map_hash (key);
839 for (p = map[n]; p != NULL; p = p->next)
840 if (STREQ (key, p->key)) return p->value;
846 /* Look up KEY in MAP; if it's not found, remove any suffix from KEY and
849 char *map_lookup (map_type map, char *key)
851 string suffix = find_suffix (key);
852 string ret = map_lookup_str (map, key);
855 /* OK, the original KEY didn't work. Let's check for the KEY without
856 an extension -- perhaps they gave foobar.tfm, but the mapping only
859 string base_key = remove_suffix (key);
860 ret = map_lookup_str (map, base_key);
861 free (base_key); // it's safe to free copy
865 /* Append the same suffix we took off, if necessary. */
866 /* what if suffix is NULL ??? */ /* what if we didn't take off suffix ??? */
868 if (ret && suffix) { /* 1994/March/18 */
869 ret = extend_filename (ret, suffix);
870 // the above creates a newly allocated string ... should free old ?
871 // except extend_filename may return the old one ?
876 /* If KEY is not already in MAP, insert it and VALUE. */
877 /* This was very buggy (when hash codes collided) - rewritten 94/March/18 */
879 void map_insert (map_type map, char *key, char *value)
881 unsigned n = map_hash (key);
882 map_element_type **ptr = &map[n];
883 /* map_element_type ***trailer = &p; */
885 while (*ptr != NULL && ! STREQ (key, (*ptr)->key)) {
886 /* *p = (*p)->next; */
887 ptr = &((*ptr)->next);
892 /* **trailer = XTALLOC (MAP_SIZE, map_element_type); *//* 94/March/19 */
893 *ptr = (map_element_type *) xmalloc (sizeof(map_element_type));
894 /* (**trailer)->key = xstrdup (key); */
895 (*ptr)->key = xstrdup (key);
896 /* (**trailer)->value = xstrdup (value); */
897 (*ptr)->value = xstrdup (value);
898 /* (**trailer)->next = NULL; */
903 /* Open and read the mapping file FILENAME, putting its entries into
904 MAP. Comments begin with % and continue to the end of the line. Each
905 line of the file defines an entry: the first word is the real
906 filename (e.g., `ptmr'), the second word is the alias (e.g.,
907 `Times-Roman'), and any subsequent words are ignored. .tfm is added
908 if either the filename or the alias have no extension. This is the
909 same order as in Dvips' psfonts.map; unfortunately, we can't have TeX
910 read that same file, since most of the real filenames start with an
911 `r', because of the virtual fonts Dvips uses. */
913 /* Modified 97/May/17 to avoid malloc for each line read */
919 int map_file_parse (map_type map, char *map_filename)
922 unsigned map_lineno = 0;
925 char line[MAXLINE]; /* 97/May/17 */
929 sprintf(log_line, "Opening %s\n", map_filename); /* 97/May/17 */
930 show_line(log_line, 0);
932 // f = xfopen (map_filename, FOPEN_R_MODE);
933 f = fopen (map_filename, FOPEN_R_MODE);
935 perrormod(map_filename); // should not happen, since we tested
939 while ((l = read_line (f)) != NULL)
941 while ((l = read_a_line (f, line, sizeof(line))) != NULL) /* 97/May/17 */
947 /* comment_loc = strrchr (l, '%'); */
948 comment_loc = strchr (l, '%'); /* 96/Nov/16 */
949 /* if (comment_loc == NULL) comment_loc = strrchr (l, ';'); */
950 if (comment_loc == NULL) comment_loc = strchr (l, ';'); /* fixed */
952 /* Ignore anything after a % or; */
953 /* if (comment_loc) *comment_loc = 0; */
954 if (comment_loc != NULL) *comment_loc = '\0';
958 /* If we don't have any filename, that's ok, the line is blank. */
959 filename = strtok (l, " \t");
961 if (filename != NULL) {
962 string alias = strtok (NULL, " \t");
964 /* But if we have a filename and no alias, something's wrong. */
965 if (alias == NULL || *alias == 0) {
967 " Have file name `%s', but no mapping (line %u in file %s).\n",
968 filename, map_lineno, map_filename);
969 show_line(log_line, 1);
972 /* We've got everything. Insert the new entry. */
973 map_insert (map, alias, filename);
980 // xfclose (f, map_filename);
981 (void) fclose (f); // we don't care about errors at this stage
985 void unshroud_string (char *, char *, int); /* in texmf.c */
987 /* Look for the file `texfonts.map' in each of the directories in
988 DIR_LIST. Entries in earlier files override later files. */
990 /* This is probably quite silly - but what the hell lets leave it in */
992 map_type map_create (string *dir_list)
994 map_type map = (map_type) xcalloc (MAP_SIZE, sizeof (map_element_type *));
997 char filename[PATH_MAX];
999 /* We don't bother with the filename truncation that `readable' in
1000 `pathsrch.c' does, since we ourselves are giving the filename,
1001 and I don't think it's worth worrying about too-long
1002 intermediate directory names in the path. */
1003 strcpy (filename, *dir_list);
1004 /* strcat (filename, "texfonts.map"); */ /* 1993/Nov/20 */
1005 unshroud_string (filename+strlen(filename),
1006 "ufygpout/nbq", PATH_MAX - strlen(filename));
1008 /* testing access first so xfopen won't fail... */
1009 /* maybe do this another way to avoid using `access' ? */
1011 if (file_p (filename) != NULL) { /* use file_p the new way */
1012 (void) map_file_parse (map, filename);
1016 /* if (access (filename, R_OK) == 0) */ /* use access the old way */
1017 if (_access (filename, R_OK) == 0) { /* 1999/Jan/8 ??? */
1018 /* if (readable (filename) != NULL) */
1019 (void) map_file_parse (map, filename);
1027 /**********************************************************************/
1029 /* #pragma optimize ("g", off) *//* try and avoid compiler bug here _dos_find */
1031 /* NOTE: _dos_find... prevents running under Windows NT ??? */
1032 /* This is called if file_method != 0 */ /* which is currently the default */
1035 /* see whether a file exists, is readable and is not a directory */
1036 /* 1994/Feb/13 may be faster than `access' in `readable' */
1037 /* returns NULL or the name filename passed in ??? */
1039 char *file_p (string fn)
1041 struct _finddata_t fi;
1045 if (open_trace_flag) {
1046 sprintf(log_line, "Is `%s' a readable file? ", fn);
1047 show_line(log_line, 0);
1050 /* allow for `normal' (_A_NORMAL) as well as `read-only' files */
1052 hFind = _findfirst (fn, &fi);
1059 /* check whether found and whether *not* a sub-directory */
1061 if ((fi.attrib & _A_SUBDIR) == 0) {
1062 if (open_trace_flag) {
1063 sprintf(log_line, "`%s' IS a readable file. ", fn);
1064 show_line(log_line, 0);
1066 return fn; /* true - its a file, not a dir */
1069 if (open_trace_flag) {
1070 sprintf(log_line, "`%s' is a subdirectory. ", fn);
1071 show_line(log_line, 0);
1073 return NULL; /* false - directory */
1077 if (open_trace_flag) {
1078 sprintf(log_line, "`%s' is NOT a readable file. ", fn);
1079 show_line(log_line, 0);
1081 return NULL; /* false - not found or no read access */
1087 /* #pragma optimize ("g",) */ /* try and avoid compiler bug here _dos_find */
1088 /* #pragma optimize ("g",)*/ /* try and avoid compiler bug here _dos_find */
1089 // #pragma optimize ("", on) /* 96/Sep/15 */
1092 /**************************************************************************/
1094 /* S_IFMT is file type mask 0170000 and S_IFDIR is directory 0040000 */
1096 #pragma optimize ("g", off) /* try and avoid compiler bug here _dos_find */
1098 /* NOTE: _dos_find... prevents running under Windows NT ??? */
1099 /* and presently dir_method = true so we do use this _dos_find_first */
1101 bool dir_p (string fn)
1105 struct _finddata_t fi;
1108 char tmpfn[FILENAME_MAX]; /* long enough ??? */
1110 strcpy (tmpfn, fn); /* make copy so can modify */
1111 if (open_trace_flag)
1113 sprintf(log_line, "Is `%s' a directory? ", tmpfn);
1114 show_line(log_line, 0);
1117 s = tmpfn + strlen(tmpfn) - 1;
1118 if (*s == '\\' || *s == '/') *s = '\0'; /* get rid of trailing path sep */
1120 /* quick test for "." and ".." case - avoid confusion later */
1121 if (strcmp (tmpfn, ".") == 0 || strcmp(tmpfn, "..") == 0) return 1;
1123 if (dir_method) { /* use _findfirst *first* if requested */
1124 hFind = _findfirst(tmpfn, &fi);
1132 /* _findfirst succeeded --- now test attributes of what was found */
1133 if (fi.attrib & _A_SUBDIR) {
1134 if (open_trace_flag) {
1135 sprintf(log_line, "Directory `%s' DOES exist ", fn);
1136 show_line(log_line, 0);
1138 return 1; /* true - it is a sub-directory */
1141 if (open_trace_flag) {
1142 sprintf(log_line, "`%s' is a FILE, not a DIRECTORY ", fn);
1143 show_line(log_line, 0);
1145 return 0; /* false - its a file, not a dir */
1149 /* _findfirst failed --- possible causes: missing *or* top-level */
1150 /* crude check first top level directory ? - assume form `c:' */
1151 if (*(tmpfn+1) != ':' || *(tmpfn+2) != '\0') {
1152 /* it is *not* top level and _findfirst failed - give up */
1153 if (open_trace_flag) {
1154 sprintf(log_line, "Directory `%s' does NOT exist ", fn);
1155 show_line(log_line, 0);
1157 return 0; /* false - it is not a directory */
1159 /* else drop through to old method */
1161 /* top-level dir, so revert to the old method after all ... */
1162 /* return dir_p_1 (fn); */
1163 /* or try _findfirst after appending PATH_SEP and nul ? */
1164 /* } */ /* drop through */
1168 /* either: dropped through (top-level of driver) or dir_method is false */
1169 /* use the old method --- fopen of nul in supposed directory */
1170 /* NOTE: nul device exists in all dirs */ /* Possible OS/2 and NDOS problem */
1171 strcat (tmpfn, PATH_SEP_STRING "nul");
1172 /* if ((test = fopen (tmpfn, "r")) == NULL) */
1173 if (share_flag == 0) test = fopen (tmpfn, "r");
1174 else test = _fsopen(tmpfn, "r", share_flag); /* 1994/July/12 */
1176 if (open_trace_flag) {
1177 sprintf(log_line, "Directory `%s' does NOT exist ", tmpfn);
1178 show_line(log_line, 0);
1180 return 0; /* false */
1183 (void) fclose(test); /* have to remember to close it again */
1184 if (open_trace_flag) {
1185 sprintf(log_line, "Directory `%s' DOES exist ", tmpfn);
1186 show_line(log_line, 0);
1188 return 1; /* true */
1192 /* #pragma optimize ("g",)*/ /* try and avoid compiler bug here _dos_find */
1193 /* #pragma optimize ("g") */ /* try and avoid compiler bug here _dos_find */
1194 #pragma optimize ("", on) /* 96/Sep/12 */
1196 /* NOTE: calling _stat makes packed EXE file 3,400 bytes larger ! */
1198 /* we don't want _fsopen instead of fopen here because only for dir\nul ??? */
1200 /********************************************************************************/
1204 /* If FILENAME is absolute or explicitly relative (i.e., starts with
1205 `/', `./', or `../'), or if DIR_LIST is null, we return whether
1206 FILENAME is readable as-is. Otherwise, we test if FILENAME is in any of
1207 the directories listed in DIR_LIST. (The last entry of DIR_LIST must
1208 be null.) We return the complete path if found, NULL else.
1210 In the interests of doing minimal work here, we assume that each
1211 element of DIR_LIST already ends with a `/'.
1213 DIR_LIST is most conveniently made by calling `initialize_path_list'.
1214 This is a separate routine because we allow recursive searching, and
1215 it may take some time to discover the list of directories.
1216 We do not want to incur that overhead every time we want to look for
1219 (Actually, `/' is not hardwired into this routine; we use PATH_SEP,
1222 /* xfind_path_filename is used now */
1224 /* Called only from test_read_access(...) in ourpaths.c */
1226 #ifdef BUILDNAMEDIRECT
1228 /* this string allocation / concatination is silly - use fixed buffer! */
1230 int xfind_path_filename (string buffer, string filename, string * dir_list)
1232 string found_name = NULL;
1234 if (buffer == filename) {
1235 show_line("buffer == filename\n", 1);
1238 *buffer = '\0'; /* "" in case we fail */
1240 if (open_trace_flag) {
1241 sprintf(log_line, "Find path for `%s' ", filename);
1242 show_line(log_line, 0);
1245 /* ignore current directory for TFM files ? */ /* 1994/Jan/24 */
1246 if (!current_tfm && strstr(filename, ".tfm") != NULL &&
1247 strcmp(*dir_list, "./") == 0) {
1248 if (open_trace_flag) {
1249 sprintf(log_line, "Ignoring `.' for %s ", filename);
1250 show_line(log_line, 0);
1252 dir_list++; /* step over first entry in dir list */
1255 if (trace_flag && open_trace_flag) { /* debugging trace 1994/Jan/8 */
1259 sprintf(log_line, "Find path for `%s' ", filename);
1260 show_line(log_line, 0);
1261 show_line("- IN: ", 0);
1262 while (*pstrs != NULL) {
1263 sprintf(log_line, "%s ", *pstrs);
1264 show_line(log_line, 0);
1270 /* Do this before testing for absolute-ness, as a leading ~ will be an
1271 absolute pathname. */ /* forget this for DOS ! */
1273 filename = expand_tilde (filename);
1277 /* is this always safe? That is, is filename writable and its OK to modify */
1278 /* unixify(filename); */ /* done `in place' */
1279 if (deslash) unixify(filename); /* made conditional 94/Feb/24 */
1282 /* following addded in attempt to catch `nul' */ /* 94/Jan/6 bkph */
1283 /* could also try and catch `nul.tex' first - but who cares about speed ? */
1284 /* needed to add this since `access' gets the wrong answer */
1285 /* that is, `nul' is a file that can be opened, but `access' says not so */
1286 if (strcmp(filename, "nul") == 0) strcpy(buffer, filename);
1287 /* If FILENAME is absolute or explicitly relative, or if DIR_LIST is
1288 null, only check if FILENAME is readable. */
1289 /* if (absolute_p (filename) || dir_list == NULL) */ /* 94/Jan/6 */
1290 else if (absolute_p (filename) || dir_list == NULL) {
1291 if (file_method) found_name = file_p (filename);
1292 else found_name = readable (filename);
1293 if (found_name != NULL) strcpy(buffer, found_name);
1294 else *buffer = '\0';
1296 else { /* Test if FILENAME is in any of the directories in DIR_LIST. */
1300 while (*dir_list != NULL) {
1301 /* if item is current directory, look in source file directory first */
1302 /* provided usesourcedirectory flag is set and workingdirectory in use */
1305 if (strcmp(s, "./") == 0) {
1306 if (firsttime && usesourcedirectory && workingdirectory &&
1307 source_direct != NULL && *source_direct != '\0') {
1310 sprintf(log_line, "Using %s dir %s %s\n", "source", s, "X");
1311 show_line(log_line, 0);
1313 sourceflag = 1; /* avoid increment of list below */
1314 firsttime = 0; /* special stuff only first time */
1316 else if (trace_flag) {
1317 sprintf(log_line, "Using %s dir %s %s\n", "current", s, "X");
1318 show_line(log_line, 0);
1322 sprintf(log_line, "XCONCAT %s %s in find_path_filename\n",
1324 show_line(log_line, 0);
1326 /* filename = concat (*dir_list, save_filename); */
1327 (void) xconcat (buffer, s, filename);
1328 if (file_method) found_name = file_p (buffer); /* new way */
1329 else found_name = readable (buffer); /* slow old way */
1330 if (found_name == NULL) {
1332 if (! sourceflag) /* 98/Sep/29 repeat in current dir */
1333 dir_list++; /* try next */
1336 if (found_name != buffer)
1337 strcpy(buffer, found_name); /* success */
1342 return (*buffer != '\0'); /* true if not empty string */
1347 /* We are dealing with a C string here for filename presumably ... */
1349 /* Also, this returns a string that was allocated --- */
1350 /* unless it happens to equal the filename sent in ... */
1351 /* this needs to be freed later - unless it happens to be ... */
1353 string find_path_filename (string filename, string * dir_list)
1355 string found_name = NULL;
1357 if (open_trace_flag) {
1358 // printf("Find path for `%s' ", filename);
1359 sprintf(log_line, "Find path for `%s' ", filename);
1360 show_line(log_line, 0);
1363 /* ignore current directory for TFM files ? */ /* 1994/Jan/24 */
1365 strstr(filename, ".tfm") != NULL &&
1366 strcmp(*dir_list, "./") == 0) {
1367 if (open_trace_flag) {
1368 sprintf(log_line, "Ignoring `.' for %s ", filename);
1369 show_line(log_line, 0);
1371 dir_list++; /* step over first entry in dir list */
1374 if (trace_flag && open_trace_flag) { /* debugging trace 1994/Jan/8 */
1378 sprintf(log_line, "Find path for `%s' ", filename);
1379 show_line(log_line, 0);
1380 show_line("- IN: ", 0);
1381 while (*pstrs != NULL) {
1382 // printf("%s ", *pstrs);
1383 sprintf(log_line, "%s ", *pstrs);
1384 show_line(log_line, 0);
1390 /* Do this before testing for absolute-ness, as a leading ~ will be an
1391 absolute pathname. */ /* forget this for DOS ! */
1393 filename = expand_tilde (filename);
1397 /* is this always safe? That is, is filename writable and its OK to modify */
1398 /* unixify(filename); */ /* done `in place' */
1399 if (deslash) unixify(filename); /* made conditional 94/Feb/24 */
1401 /* following addded in attempt to catch `nul' */ /* 94/Jan/6 bkph */
1402 /* could also try and catch `nul.tex' first - but who cares about speed ? */
1403 /* needed to add this since `access' gets the wrong answer */
1404 /* that is, `nul' is a file that can be opened, but `access' says not so */
1405 if (strcmp(filename, "nul") == 0) found_name = filename;
1406 /* If FILENAME is absolute or explicitly relative, or if DIR_LIST is
1407 null, only check if FILENAME is readable. */
1408 /* if (absolute_p (filename) || dir_list == NULL) */ /* 94/Jan/6 */
1409 else if (absolute_p (filename) || dir_list == NULL) {
1410 if (file_method) found_name = file_p (filename); /* new way 94/Feb/13 */
1411 else found_name = readable (filename); /* slow old way */
1413 else { /* Test if FILENAME is in any of the directories in DIR_LIST. */
1414 string save_filename = filename;
1418 while (*dir_list != NULL) {
1419 /* if item is current directory, look in source file directory first */
1420 /* provided usesourcedirectory flag is set and workingdirectory in use */
1423 if (strcmp(s, "./") == 0) {
1424 if (firsttime && usesourcedirectory && workingdirectory &&
1425 source_direct != NULL && *source_direct != '\0') {
1428 sprintf(log_line, "Using %s dir %s %s\n", "source", s, "F");
1429 show_line(log_line, 0);
1431 sourceflag = 1; /* avoid increment of list below */
1432 firsttime = 0; /* special stuff only first time */
1434 else if (trace_flag) {
1435 sprintf(log_line, "Using %s dir %s %s\n", "current", s, "F");
1436 show_line(log_line, 0);
1440 sprintf(log_line, "CONCAT %s %s in find_path_filename\n",
1441 s, save_filename); /* 1996/Jan/20 */
1442 show_line(log_line, 0);
1444 filename = concat (s, save_filename);
1445 /* found_name = readable (filename); */
1446 if (file_method) found_name = file_p (filename); /* new way */
1447 else found_name = readable (filename); /* slow old way */
1448 if (found_name == NULL) {
1449 free (filename); /* if this is not it, free it again */
1450 if (! sourceflag) /* 98/Sep/29 repeat in current dir */
1454 if (found_name != filename)
1455 free (filename); /* don't free if is the one passed in */
1460 return found_name; /* return the allocated name - free later */
1464 /* If NAME is readable, return it. If the error is ENAMETOOLONG,
1465 truncate any too-long path components and return the result (unless
1466 there were no too-long components, i.e., a overall too-long name
1467 caused the error, in which case return NULL). On any other error,
1470 POSIX invented this brain-damage of not necessarily truncating
1471 pathname components; the system's behavior is defined by the value of
1472 the symbol _POSIX_NO_TRUNC, but you can't change it dynamically! */
1474 /* Using access (and dir_p) is considerably slower than using dosfind */
1475 /* NOTE: this is only called from find_path_file,
1476 and then ONLY if file_method is false (-G) */
1478 /* returns NULL or the file name passed in ??? */
1480 /* static string readable (name) */
1481 string readable (string name)
1485 if (open_trace_flag) {
1486 sprintf(log_line, "is %s readable? ", name);
1487 show_line(log_line, 0);
1490 /* Check first whether we have read access, then */
1491 /* need to test if directory, since access always says OK for directory */
1492 /* BUT: readable is called only from find_path_file */
1493 /* So we never call this with directory, so why waste time ? bkph */
1494 /* BUT: can be caught out by idiot with a directory called myfile.tex ? */
1496 if (_access (name, R_OK) == 0) {
1497 if (test_dir_access) { /* check only if we are asked to ... */
1499 if (open_trace_flag) {
1500 sprintf(log_line, "tested read access of directory `%s' ", name);
1501 show_line(log_line, 0);
1509 /* if (_access (name, R_OK) == 0 && !dir_p (name)) ret = name; */
1511 else if (errno == ENAMETOOLONG) {
1512 ret = truncate_pathname (name);
1513 /* Perhaps some other error will occur with the truncated name, so
1514 let's call access again. */
1515 if (!(_access (ret, R_OK) == 0 && !dir_p (ret))) { /* Failed. */
1521 else if (errno == EACCES) {
1522 if (trace_flag) show_line("Access denied!\n", 0);
1525 else if (errno == ENOENT) {
1526 if (trace_flag) show_line("File or path name not found!\n", 1);
1531 sprintf(log_line, "Unknown access error %d!\n", errno);
1532 show_line(log_line, 0);
1540 /* Truncate any too-long path components in NAME, returning the result. */
1542 string truncate_pathname (string name)
1544 unsigned c_len = 0; /* Length of current component. */
1545 unsigned ret_len = 0; /* Length of constructed result. */
1546 string ret = (string) xmalloc (PATH_MAX + 1);
1548 for (; *name; name++)
1550 if (*name == PATH_SEP) /* not in DOS */
1551 { /* At a directory delimiter, reset component length. */
1554 else if (c_len > NAME_MAX)
1555 { /* If past the max for a component, ignore this character. */
1559 /* If we've already copied the max, give up. */
1560 if (ret_len == PATH_MAX)
1566 /* Copy this character. */
1567 ret[ret_len++] = *name;
1574 #endif /* end of ifdef ENAMETOOLONG */
1577 /* Return true if FILENAME is absolute or explicitly relative, else false. */
1578 /* Absolute: in DOS name starts with PATH_SEP, or with DRIVE letter and colon */
1579 /* Explicitly relative: starts with ./ or ../ */
1580 /* kpathsea/absolute.c */
1581 // static bool absolute_p (string filename) {
1582 bool absolute_p (string filename)
1585 bool explicit_relative;
1588 /* absolute = (*filename == PATH_SEP) */ /* 1994/Mar/1 */
1589 absolute = (*filename == PATH_SEP || *filename == '\\')
1590 || ((filename[1] == ':') && ISALPHA (*filename));
1591 /* || ISALPHA (*filename) && filename[1] == ':'; */
1593 absolute = (*filename == PATH_SEP);
1595 if (absolute) return true; /* don't waste any more time */
1598 explicit_relative = (*filename == '.'
1599 && ((filename[1] == PATH_SEP || filename[1] == '\\')
1600 || (filename[1] == '.' &&
1601 (filename[2] == PATH_SEP || filename[2] == '\\'))));
1603 explicit_relative = (*filename == '.' && (filename[1] == PATH_SEP
1604 || (filename[1] == '.' && filename[2] == PATH_SEP)));
1607 return explicit_relative;
1608 /* return absolute || explicit_relative; */ /* slight rewrite 1994/Feb/13 */
1612 /* note: this strips off trailing white space in actual environment var ... */
1613 void striptrailing (string env_value, string env_name, string default_path)
1616 if (env_name == NULL) { /* 1994/Feb/24 */
1618 sprintf(log_line, "WARNING: no env_name noted, using default %s\n",
1620 show_line(log_line, 0);
1624 if (env_value == NULL) {
1626 sprintf(log_line, "WARNING: %s not defined in environment, using default %s\n",
1627 env_name, default_path);
1628 show_line(log_line, 0);
1632 if (strlen(env_value) == 0) return;
1633 s = env_value + strlen(env_value) - 1;
1634 /* while (*s <= ' ') *s-- = '\0'; */
1635 while (s >= env_value && *s <= ' ')*s-- = '\0'; /* 94/Feb/24 */
1639 /* convert /! and /!! to / and // 97/Mar/22 */
1643 void convertexclam (string env_value)
1646 if (env_value == NULL) return;
1648 if (strchr(s, '!') == NULL) return;
1649 while ((s = strchr(s, '!')) != NULL)
1653 if (*(s+2) == PATH_DELIMITER || *(s+2) == '\0')
1655 if (s > env_value && *(s-1) == PATH_SEP)
1657 *s = PATH_SEP; /* convert the first ! */
1658 strcpy(s+1, s+2); /* flush the second ! */
1663 { /* single ! */ /* assume already unixified */
1664 if (*(s+1) == PATH_DELIMITER || *(s+1) == '\0')
1666 if (s > env_value && *(s-1) == PATH_SEP)
1667 strcpy(s, s+1); /* just flush the ! */
1674 sprintf(log_line,"Now is %s\n", env_value);
1675 show_line(log_line, 0);
1680 /* Return a NULL-terminated array of directory names, each name ending
1681 with PATH_SEP, created by parsing the PATH_DELIMITER-separated list
1682 in the value of the environment variable ENV_NAME, or DEFAULT_PATH if
1683 the env var is not set.
1685 A leading or trailing PATH_DELIMITER in the value of ENV_NAME is replaced
1688 Any element of the path that ends with double PATH_SEP characters
1689 (e.g., `foo//') is replaced by all its subdirectories.
1691 If ENV_NAME is null, only parse DEFAULT_PATH. If both are null, do
1692 nothing and return NULL. */
1694 string * initialize_path_list (string env_name, string default_path)
1698 unsigned dir_count = 0;
1701 struct _finddata_t findt;
1702 /* _finddata_t structure *can* be reused, unlike _find_t 95/Jan/31 */
1703 /* so save on stack space, by having one copy here, not one per expand_subdir*/
1705 /* env_value = env_name ? getenv (env_name) : NULL; */
1706 env_value = env_name ? grabenv (env_name) : NULL; /* 1994/May/19 */
1708 /* need to convert \ to / as soon as possible to avoid confusion */
1709 /* we may be modifying DOS environment variable here ... is it always safe ? */
1711 if (deslash) unixify (env_value); /* 1994/Feb/24 */
1714 if (env_name) { /* only if env_name is non-null 94/Feb/24 */
1715 sprintf(log_line, "\nSet %s=", env_name);
1716 show_line(log_line, 0);
1717 if (env_value) { /* only if env_name value is set */
1718 show_line(env_value, 0);
1724 /* strip off trailing white space which would confuse things - bkph */
1725 striptrailing (env_value, env_name, default_path);
1726 convertexclam (env_value); /* 97/Mar/22 */
1728 orig_path = expand_default (env_value, default_path);
1730 if (orig_path == NULL || *orig_path == 0) return NULL;
1732 /* need to convert \ to / as soon as possible to avoid confusion */
1734 if (deslash) unixify (orig_path); /* redundant ? */
1737 /* If we've already seen this PATH_DELIMITER-separated list, then just get
1738 it back instead of going back to the filesystem. */
1739 dir_list = find_dir_list (orig_path);
1740 if (dir_list != NULL) return dir_list;
1742 /* Be sure `path' is in writable memory. */ /* if not, copy it */
1743 path = (orig_path == env_value || orig_path == default_path
1744 ? xstrdup (orig_path) : orig_path);
1746 /* Find each element in the path in turn. */
1748 /* if (!switchflag) */
1749 if (current_flag) { /* suppress adding current directory - debugging */
1751 sprintf(log_line, "Adding directory `%s'\n", "."); /* 95/Jan/24 */
1752 show_line(log_line, 0);
1754 add_directory(&dir_list, &dir_count, ".");
1757 for (dir = strtok (path, PATH_DELIMITER_STRING); dir != NULL;
1758 dir = strtok (NULL, PATH_DELIMITER_STRING)) {
1763 sprintf(log_line, "dir %s\n", dir);
1764 show_line(log_line, 0);
1767 /* If the path starts with ~ or ~user, expand it. Do this
1768 before calling `expand_subdir' or `add_directory', so that
1769 1) we don't expand the same ~ for every subdirectory; and
1770 2) pathnames in `expand_subdir' don't have a `~' in them
1771 (since the system won't grok `~/foo' as a directory). */
1773 dir = expand_tilde (dir);
1777 /* If `dir' is the empty string, ignore it. */
1778 if (len == 0) continue;
1780 /* If `dir' ends in double PATH_SEP, do subdirectories (and remove
1781 the second PATH_SEP, so the final pathnames we return don't look
1782 like foo//bar). Because we obviously want to do subdirectories
1783 of `dir', we don't check if it is a leaf. This means that if
1784 `dir' is `foo//', and `foo' contains only symlinks (so our leaf
1785 test below would be true), the symlinks are chased. */
1787 /* modified to treat single PATH_SEP as expand subdirs without recursion */
1788 /* modified to treat double PATH_SEP as expand subdirs *with* recursion */
1791 if (len > 2 && /* 1994/Mar/1 */
1792 (dir[len - 1] == PATH_SEP || dir[len - 1] == '\\')
1793 && (dir[len - 2] == PATH_SEP || dir[len - 2] == '\\'))
1795 if (len > 2 && dir[len - 1] == PATH_SEP && dir[len - 2] == PATH_SEP)
1798 if (open_trace_flag) {
1799 sprintf(log_line, "Double backslash on `%s' ", dir); /* bkph */
1800 show_line(log_line, 0);
1806 sprintf(log_line, "Adding directory `%s'\n", dir);
1807 show_line(log_line, 0);
1809 add_directory (&dir_list, &dir_count, dir);
1810 /* local variable 'findt' used without having been initialized ? &findt ? */
1811 expand_subdir (&dir_list, &dir_count, dir, findt, 1); /* 95/Jan/31 */
1814 /* following is new to find only directories to one level 1994/Jan/24 */
1816 else if (len > 1 && /* 1994/Mar/1 */
1817 (dir[len - 1] == PATH_SEP || dir[len - 1] == '\\'))
1819 else if (len > 1 && dir[len - 1] == PATH_SEP)
1822 if (open_trace_flag) {
1823 sprintf(log_line, "Single backslash on `%s' ", dir); /* bkph */
1824 show_line(log_line, 0);
1827 /* dir[len - 1] = 0; */
1830 sprintf(log_line, "Adding directory `%s'\n", dir);
1831 show_line(log_line, 0);
1833 add_directory (&dir_list, &dir_count, dir);
1834 expand_subdir (&dir_list, &dir_count, dir,
1835 findt, 0); /* 95/Jan/31 */
1838 else { /* Don't bother to add the directory if it doesn't exist. */
1841 sprintf(log_line, "Adding directory `%s'\n", dir);
1842 show_line(log_line, 0);
1844 add_directory (&dir_list, &dir_count, dir);
1851 show_line("Adding terminating null\n", 0);
1855 /* Add the terminating null entry to `dir_list'. */
1857 XRETALLOC (dir_list, dir_count, string);
1858 dir_list[dir_count - 1] = NULL;
1860 /* Save the directory list we just found. */
1861 save_dir_list (orig_path, dir_list);
1866 /* Subroutines for `initialize_path_list'. */
1868 /* Add a newly-allocated copy of DIR to the end of the array pointed to
1869 by DIR_LIST_PTR. Increment DIR_COUNT_PTR. Append a `/' to DIR if
1870 necessary. We assume DIR is a directory, to avoid an unnecessary
1873 void add_directory (string **dir_list_ptr, unsigned *dir_count_ptr, string dir)
1875 if (dir == NULL) return; /* paranoia 1995/Jan/24 */
1876 /* If `dir' does not end with a `/', add it. We can't just
1877 write it in place, since that would overwrite the null that
1878 strtok may have put in. So we ALWAYS make a copy of DIR. */
1880 dir = (dir[strlen (dir) - 1] == PATH_SEP || /* 1994/Mar/1 */
1881 dir[strlen (dir) - 1] == '\\') ?
1882 xstrdup (dir) : concat (dir, PATH_SEP_STRING);
1884 dir = (dir[strlen (dir) - 1] == PATH_SEP ? xstrdup (dir)
1885 : concat (dir, PATH_SEP_STRING));
1888 if (deslash) unixify (dir); /* redundant ? bkph */
1893 sprintf(log_line, "Adding directory `%s'\n", dir);
1894 show_line(log_line, 0);
1897 // if (open_trace_flag) {
1898 // sprintf(log_line, "Adding directory `%s' ", dir);
1899 // show_line(log_line, 0);
1903 /* Add `dir' to the list of the directories. */
1905 XRETALLOC (*dir_list_ptr, *dir_count_ptr, string);
1906 (*dir_list_ptr)[*dir_count_ptr - 1] = dir;
1909 void lowercase (char *s)
1912 *s++ = (char) tolower(*s);
1916 /* These routines, while not strictly needed to be exported, are
1917 plausibly useful to be called by outsiders. */
1919 /* Replace a leading or trailing PATH_DELIMITER in ENV_PATH with
1920 DEFAULT_PATH. If neither is present, return ENV_PATH if that is
1921 non-null, else DEFAULT_PATH. */
1923 string expand_default (string env_path, string default_path)
1927 if (env_path == NULL) expansion = default_path;
1928 else if (*env_path == PATH_DELIMITER)
1929 expansion = concat (default_path, env_path);
1930 else if (env_path[strlen (env_path) - 1] == PATH_DELIMITER)
1931 expansion = concat (env_path, default_path);
1932 else expansion = env_path;
1934 if (trace_flag) { /* 1994/Jan/8 */
1935 if (env_path == NULL) {
1936 sprintf(log_line, "Using the default %s\n", expansion);
1937 show_line(log_line, 0);
1939 else if (expansion == env_path) {
1940 sprintf(log_line, "Using %s (default was %s)\n", expansion, default_path);
1941 show_line(log_line, 0);
1943 else { /* expansion != env_path */
1944 sprintf(log_line, "Expanded %s (default was %s) to %s\n",
1945 env_path, default_path, expansion);
1946 show_line(log_line, 0);
1954 /* Expand a leading ~ or ~user, Unix-style, unless we are some weirdo
1955 operating system. */
1957 string expand_tilde (string name)
1959 #if defined (MSDOS) || defined (VMS) || defined (VMCMS)
1965 /* If no leading tilde, do nothing. */
1969 /* If `~' or `~/', use $HOME if it exists, or `.' if it doesn't. */
1970 else if (name[1] == PATH_SEP || name[1] == 0) /* not in DOS */
1972 home = getenv ("HOME"); /* not in DOS */
1977 = name[1] == 0 ? home : concat3 (home, PATH_SEP_STRING, name + 2);
1980 /* If `~user' or `~user/', look up user in the passwd database. */
1986 while (name[c] != PATH_SEP && name[c] != 0) /* not in DOS */
1989 user = (string) xmalloc (c);
1990 strncpy (user, name + 1, c - 1);
1993 /* We only need the cast here for those (old deficient) systems
1994 which do not declare `getpwnam' in <pwd.h>. */
1995 p = (struct passwd *) getpwnam (user);
1997 /* If no such user, just use `.'. */
1998 home = p == NULL ? "." : p->pw_dir;
2000 expansion = name[c] == 0 ? home : concat (home, name + c);
2004 #endif /* not (DOS or VMS or VM/CMS) */
2008 // structure used for manipulation dir lists
2015 /* Routines to save and retrieve a directory list keyed by the original
2016 PATH_DELIMITER-separated path. This is useful because 1) it can take a
2017 significant amount of time to discover all the subdirectories of a
2018 given directory, and 2) many paths all have the same basic default,
2019 and thus would recompute the directory list. */
2021 static saved_path_entry *saved_paths = NULL;
2022 static unsigned saved_paths_length = 0;
2024 /* We implement the data structure as a simple linear list, since it's
2025 unlikely to ever be more than a dozen or so elements long. We don't
2026 bother to check here if PATH has already been saved; we always add it
2029 void save_dir_list (string path, string *dir_list)
2031 // saved_paths_length++;
2032 XRETALLOC (saved_paths, saved_paths_length+1, saved_path_entry);
2033 saved_paths[saved_paths_length].path = path;
2034 saved_paths[saved_paths_length].dir_list = dir_list;
2035 saved_paths_length++;
2038 /* When we retrieve, just check the list in order. */
2040 string *find_dir_list (string path)
2046 sprintf(log_line, "Find Dir List for path: %s\n", path);
2047 show_line(log_line, 0);
2051 for (p = 0; p < saved_paths_length; p++) {
2052 if (strcmp (saved_paths[p].path, path) == 0)
2053 return saved_paths[p].dir_list;
2058 /* Unixify filename and path (turn \ into /) --- assumes null terminated */
2060 char *unixify (char * t)
2063 if (s == NULL) return s; /* paranoia -- 1993/Apr/10 */
2066 while (*s != '\0') { /* paranoia -- 1997/Oct/23 */
2067 /* if (*s == '\\') *s = '/'; */
2068 if (*s == '\\') *s = PATH_SEP;
2074 sprintf(log_line, "Unixified name: %s\n", t);
2075 show_line(log_line, 0);
2082 /****************************************************************************/
2084 /* moved here to avoid problems with pragma */
2086 /* struct _finddata_t findt; */ /* make global, can be reused unlike _find_t */
2087 /* avoids heavy stack usage in tree search */
2088 /* but ties up some global fixed space ... */
2090 #pragma optimize ("g", off) /* try and avoid compiler bug here _dos_find */
2092 /* Add DIRNAME to DIR_LIST and look for subdirectories, possibly recursively.
2093 We assume DIRNAME is the name of a directory. */
2095 /* NOTE: _dos_find... prevents running under Windows NT as console app ??? */
2096 /* Yes, so lets flush it! use _findfirst, _findnext, _findclose instead */
2098 /* called only from initialize_path_list (and recursively) */
2099 /* kpathsea/elt-dirs.c */
2100 /* renamed to do_subdir */
2101 void expand_subdir (string **dir_list_ptr, unsigned *dir_count_ptr, string dirname,
2102 struct _finddata_t findt, integer recurseflag)
2105 /* struct _finddata_t findt; */
2109 /* char buffer[PATH_MAX]; */ /* pretty long? potential recursion problem? */
2110 char buffer[FILENAME_MAX]; /* this is DOS and Windows NT after all ... */
2115 sprintf(log_line, "\nExpanding sub dir %s ", dirname);
2116 show_line(log_line, 0);
2120 strcpy(buffer, dirname);
2121 len = strlen(dirname);
2124 /* if (buffer[len-1] == PATH_SEP) strcat(buffer, "*.*"); */
2125 if (buffer[len-1] == PATH_SEP || buffer[len-1] == '\\')
2126 strcat(buffer, "*.*"); /* 1994/Mar/1 */
2127 else strcat(buffer, PATH_SEP_STRING "*.*");
2129 if (buffer[len-1] == PATH_SEP) strcat(buffer, "*");
2130 else strcat(buffer, PATH_SEP_STRING "*");
2133 /* Note: the _A_SUBDIR means we get ordinary files PLUS sub-directories */
2134 if (open_trace_flag) {
2135 sprintf(log_line, "\nDIRNAME `%s' ", dirname);
2136 show_line(log_line, 0);
2138 /* we'll need to step over `.' and `..' up front of directory list */
2139 hFind = _findfirst(buffer, &findt);
2140 if (hFind > 0) ret = 0;
2142 /* _dos_findnext( &findt ); */
2143 /* while(_dos_findnext(&findt)== 0) { */
2145 /* if (open_trace_flag) */
2146 if (open_trace_flag && trace_flag) {
2147 sprintf(log_line, "NEXT `%s' (%0x) ", findt.name, findt.attrib);
2148 show_line(log_line, 0);
2150 /* if (strchr(findt.name, '.') != NULL) continue; *//* not needed */
2151 if (findt.name[0] != '.' && /* ignore "." and ".." */
2152 findt.attrib & _A_SUBDIR){ /* only look at SUBDIRs */
2153 if (open_trace_flag) {
2154 sprintf(log_line, "\nDIRNAME `%s' ", dirname);
2155 show_line(log_line, 0);
2158 potential = concat3(dirname,
2159 (dirname[len-1] == PATH_SEP || dirname[len-1] == '\\')
2160 ? "" : PATH_SEP_STRING, findt.name);
2162 potential = concat3(dirname, dirname[len-1] == PATH_SEP
2163 ? "" : PATH_SEP_STRING, findt.name);
2165 lowercase (potential); /* make look nicer ? */
2166 if (open_trace_flag) {
2167 sprintf(log_line, "POTENTIAL `%s' ", potential);
2168 show_line(log_line, 0);
2171 sprintf(log_line, "Adding directory `%s'\n", potential); /* 95/Jan/24 */
2172 show_line(log_line, 0);
2174 add_directory(dir_list_ptr, dir_count_ptr, potential);
2176 expand_subdir(dir_list_ptr, dir_count_ptr,
2177 potential, findt, 1); /* 95/Jan/31 */
2179 } /* end of findt.attrib & _A_SUBDIR != 0 */
2180 ret = _findnext(hFind, &findt);
2183 if (hFind > 0) _findclose (hFind);
2187 _dos_findclose(&findt);
2190 #else /* end of MSDOS (way up there) */
2192 /* This is how we do this if we are NOT using DOS */
2196 char potential[PATH_MAX];
2199 /* We will be looking at its contents. */
2200 dir = opendir (dirname);
2204 /* Compute the length of DIRNAME, since it's loop-invariant. */
2205 length = strlen (dirname);
2207 /* Construct the part of the pathname that doesn't change. */
2208 strcpy (potential, dirname);
2209 if (potential[length - 1] != PATH_SEP) /* not in DOS */
2211 potential[length] = PATH_SEP;
2212 potential[length + 1] = 0;
2216 /* about to use _stat --- shouldn't get here when using MSDOS anyway */
2218 while ((e = readdir (dir)) != NULL)
2219 { /* If it's . or .., never mind. */
2220 if (!(e->d_name[0] == '.'
2221 && (e->d_name[1] == 0
2222 || (e->d_name[1] == '.' && e->d_name[2] == 0))))
2223 { /* If it's not a directory, we will skip it on the
2225 strcat (potential, e->d_name);
2227 /* If we can't _stat it, or if it isn't a directory, continue. */
2228 if (_stat (potential, &st) == 0 && S_ISDIR (st.st_mode))
2229 { /* It's a subdirectory; add `potential' to the list. */
2231 sprintf(log_line, "Adding directory `%s'\n", potential); /* 95/Jan/24 */
2232 show_line(log_line, 0);
2234 add_directory (dir_list_ptr, dir_count_ptr, potential);
2236 /* If it's not a leaf, quit. Assume that leaf
2237 directories have two links (one for . and one for ..).
2238 This means that symbolic links to directories do not affect
2239 the leaf-ness. This is arguably wrong, but the only
2240 alternative I know of is to _stat every entry in the
2241 directory, and that is unacceptably slow. */
2242 if (st.st_nlink > 2)
2243 { /* All criteria are met; find subdirectories. */
2244 expand_subdir (dir_list_ptr, dir_count_ptr, potential,
2245 findt, 1); /* 95/Jan/31 */
2249 /* ``Remove'' the directory entry name. */
2250 potential[length] = 0;
2255 #endif /* end of *not* DOS case */
2258 // #pragma optimize ("", on) /* 96/Sep/12 */
2260 /************************************************************************/
2264 /* This version of `getopt' appears to the caller like standard Unix `getopt'
2265 but it behaves differently for the user, since it allows the user
2266 to intersperse the options with the other arguments.
2268 As `getopt' works, it permutes the elements of ARGV so that,
2269 when it is done, all the options precede everything else. Thus
2270 all application programs are extended to handle flexible argument
2273 /* For communication from `getopt' to the caller.
2274 When `getopt' finds an option that takes an argument,
2275 the argument value is returned here.
2276 Also, when `ordering' is RETURN_IN_ORDER,
2277 each non-option ARGV-element is returned here. */
2281 /* Index in ARGV of the next element to be scanned.
2282 This is used for communication to and from the caller
2283 and for communication between successive calls to `getopt'.
2285 On entry to `getopt', zero means this is the first call; initialize.
2287 When `getopt' returns EOF, this is the index of the first of the
2288 non-option elements that the caller should itself scan.
2290 Otherwise, `optind' communicates from one call to the next
2291 how much of ARGV has been scanned so far. */
2295 /* The next char to be scanned in the option-element
2296 in which the last option character we returned was found.
2297 This allows us to pick up the scan where we left off.
2299 If this is zero, or a null string, it means resume the scan
2300 by advancing to the next ARGV-element. */
2302 static char *nextchar;
2304 /* Callers store zero here to inhibit the error message
2305 for unrecognized options. */
2309 /* Describe how to deal with options that follow non-option ARGV-elements.
2311 If the caller did not specify anything,
2312 the default is REQUIRE_ORDER if the environment variable
2313 POSIXLY_CORRECT is defined, PERMUTE otherwise.
2315 REQUIRE_ORDER means don't recognize them as options;
2316 stop option processing when the first non-option is seen.
2317 This is what Unix does.
2318 This mode of operation is selected by either setting the environment
2319 variable POSIXLY_CORRECT, or using `+' as the first character
2320 of the list of option characters.
2322 PERMUTE is the default. We permute the contents of ARGV as we scan,
2323 so that eventually all the non-options are at the end. This allows options
2324 to be given in any order, even with programs that were not written to
2327 RETURN_IN_ORDER is an option available to programs that were written
2328 to expect options and other ARGV-elements in any order and that care about
2329 the ordering of the two. We describe each non-option ARGV-element
2330 as if it were the argument of an option with character code 1.
2331 Using `-' as the first character of the list of option characters
2332 selects this mode of operation.
2334 The special argument `--' forces an end of option-scanning regardless
2335 of the value of `ordering'. In the case of RETURN_IN_ORDER, only
2336 `--' can cause `getopt' to return EOF with `optind' != ARGC. */
2340 REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
2344 #define my_index strchr
2345 #define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
2348 /* Handle permutation of arguments. */
2350 /* Describe the part of ARGV that contains non-options that have
2351 been skipped. `first_nonopt' is the index in ARGV of the first of them;
2352 `last_nonopt' is the index after the last of them. */
2354 static int first_nonopt;
2355 static int last_nonopt;
2357 /* Exchange two adjacent subsequences of ARGV.
2358 One subsequence is elements [first_nonopt,last_nonopt)
2359 which contains all the non-options that have been skipped so far.
2360 The other is elements [last_nonopt,optind), which contains all
2361 the options processed since those non-options were skipped.
2363 `first_nonopt' and `last_nonopt' are relocated so that they describe
2364 the new indices of the non-options in ARGV after they are moved. */
2365 /* kpathsea/getopt.c */
2366 static void exchange (char **argv)
2368 int nonopts_size; /* paranoia - bkph */
2369 char **temp; /* paranoia - bkph */
2370 /* int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); */
2371 nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
2372 /* char **temp = (char **) _alloca (nonopts_size); */
2373 temp = (char **) _alloca (nonopts_size);
2375 /* Interchange the two blocks of data in ARGV. */
2377 my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
2378 my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
2379 (optind - last_nonopt) * sizeof (char *));
2380 my_bcopy ((char *) temp,
2381 (char *) &argv[first_nonopt + optind - last_nonopt],
2384 /* Update records for the slots the non-options now occupy. */
2386 first_nonopt += (optind - last_nonopt);
2387 last_nonopt = optind;
2391 char *get_env_shroud (char *); /* in texmf.c */
2393 /* Scan elements of ARGV (whose length is ARGC) for option characters
2396 If an element of ARGV starts with '-', and is not exactly "-" or "--",
2397 then it is an option element. The characters of this element
2398 (aside from the initial '-') are option characters. If `getopt'
2399 is called repeatedly, it returns successively each of the option characters
2400 from each of the option elements.
2402 If `getopt' finds another option character, it returns that character,
2403 updating `optind' and `nextchar' so that the next call to `getopt' can
2404 resume the scan with the following option character or ARGV-element.
2406 If there are no more option characters, `getopt' returns `EOF'.
2407 Then `optind' is the index in ARGV of the first ARGV-element
2408 that is not an option. (The ARGV-elements have been permuted
2409 so that those that are not options now come last.)
2411 OPTSTRING is a string containing the legitimate option characters.
2412 If an option character is seen that is not listed in OPTSTRING,
2413 return '?' after printing an error message. If you set `opterr' to
2414 zero, the error message is suppressed but we still return '?'.
2416 If a char in OPTSTRING is followed by a colon, that means it wants an arg,
2417 so the following text in the same ARGV-element, or the text of the following
2418 ARGV-element, is returned in `optarg'. Two colons mean an option that
2419 wants an optional arg; if there is text in the current ARGV-element,
2420 it is returned in `optarg', otherwise `optarg' is set to zero.
2422 If OPTSTRING starts with `-' or `+', it requests different methods of
2423 handling the non-option ARGV-elements.
2424 See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
2426 Long-named options begin with `--' instead of `-'.
2427 Their names may be abbreviated as long as the abbreviation is unique
2428 or is an exact match for some defined option. If they have an
2429 argument, it follows the option name in the same ARGV-element, separated
2430 from the option name by a `=', or else the in next ARGV-element.
2431 When `getopt' finds a long-named option, it returns 0 if that option's
2432 `flag' field is nonzero, the value of the option's `val' field
2433 if the `flag' field is zero.
2435 The elements of ARGV aren't really const, because we permute them.
2436 But we pretend they're const in the prototype to be compatible
2439 LONGOPTS is a vector of `struct option' terminated by an
2440 element containing a name which is zero.
2442 LONGIND returns the index in LONGOPT of the long-named option found.
2443 It is only valid when a long-named option has been found by the most
2446 If LONG_ONLY is nonzero, '-' as well as '--' can introduce
2447 long-named options. */
2448 /* kpathsea/getopt.c */
2449 int _getopt_internal (int argc, char *const *argv, const char *optstring,
2450 const struct option *longopts, int *longind, int long_only)
2453 char *commandlineflag = "command line flag";
2457 /* Initialize the internal data when the first call is made.
2458 Start processing options with ARGV-element 1 (since ARGV-element 0
2459 is the program name); the sequence of previously skipped
2460 non-option ARGV-elements is empty. */
2463 first_nonopt = last_nonopt = optind = 1;
2467 /* Determine how to handle the ordering of options and nonoptions. */
2469 if (optstring[0] == '-') {
2470 ordering = RETURN_IN_ORDER;
2473 else if (optstring[0] == '+') {
2474 ordering = REQUIRE_ORDER;
2477 /* else if (getenv ("POSIXLY_CORRECT") != NULL) */
2478 else if (get_env_shroud ("QPTJYMZ`DPSSFDU") != NULL)
2479 ordering = REQUIRE_ORDER;
2484 if (nextchar == NULL || *nextchar == '\0') {
2485 if (ordering == PERMUTE) {
2486 /* If we have just processed some options following some non-options,
2487 exchange them so that the options come first. */
2489 if (first_nonopt != last_nonopt && last_nonopt != optind)
2490 exchange ((char **) argv);
2491 else if (last_nonopt != optind)
2492 first_nonopt = optind;
2494 /* Now skip any additional non-options
2495 and extend the range of non-options previously skipped. */
2497 while (optind < argc
2498 && (argv[optind][0] != '-' || argv[optind][1] == '\0')
2501 last_nonopt = optind;
2504 /* Special ARGV-element `--' means premature end of options.
2505 Skip it like a null option,
2506 then exchange with previous non-options as if it were an option,
2507 then skip everything else like a non-option. */
2509 if (optind != argc && !strcmp (argv[optind], "--")) {
2512 if (first_nonopt != last_nonopt && last_nonopt != optind)
2513 exchange ((char **) argv);
2514 else if (first_nonopt == last_nonopt)
2515 first_nonopt = optind;
2521 /* If we have done all the ARGV-elements, stop the scan
2522 and back over any non-options that we skipped and permuted. */
2524 if (optind == argc) {
2525 /* Set the next-arg-index to point at the non-options
2526 that we previously skipped, so the caller will digest them. */
2527 if (first_nonopt != last_nonopt)
2528 optind = first_nonopt;
2532 /* If we have come to a non-option and did not permute it,
2533 either stop the scan or describe it to the caller and pass it by. */
2535 if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) {
2536 if (ordering == REQUIRE_ORDER)
2538 optarg = argv[optind++];
2542 /* We have found another option-ARGV-element.
2543 Start decoding its characters. */ /* unusual use of bool */
2545 nextchar = (argv[optind] + 1
2546 + (longopts != NULL && argv[optind][1] == '-'));
2549 if (longopts != NULL
2550 && ((argv[optind][0] == '-'
2551 && (argv[optind][1] == '-' || long_only)))) {
2552 const struct option *p;
2556 const struct option *pfound = NULL;
2557 int indfound=0; /* keep compiler quiet */
2559 while (*s && *s != '=')
2562 /* Test all options for either exact match or abbreviated matches. */
2563 for (p = longopts, option_index = 0; p->name;
2564 p++, option_index++)
2565 if (!strncmp (p->name, nextchar, s - nextchar))
2567 /* if (s - nextchar == strlen (p->name)) */
2568 if (s - nextchar == (int) strlen (p->name)) { /* avoid warning bkph */
2569 /* Exact match found. */
2571 indfound = option_index;
2575 else if (pfound == NULL) {
2576 /* First nonexact match found. */
2578 indfound = option_index;
2581 /* Second nonexact match found. */
2585 if (ambig && !exact) {
2588 "%s `%s' is ambiguous\n", commandlineflag, argv[optind]);
2589 show_line(log_line, 1);
2591 nextchar += strlen (nextchar);
2596 if (pfound != NULL) {
2597 option_index = indfound;
2600 /* Don't test has_arg with >, because some C compilers don't
2601 allow it to be used on enums. */
2602 if (pfound->has_arg)
2606 if (argv[optind - 1][1] == '-') { /* --option */
2609 "%s `--%s' does not take an argument\n",
2610 commandlineflag,pfound->name);
2611 show_line(log_line, 1);
2613 else { /* +option or -option */
2616 "%s `%c%s' does not take an argument\n",
2617 commandlineflag, argv[optind - 1][0], pfound->name);
2618 show_line(log_line, 1);
2621 nextchar += strlen (nextchar);
2625 else if (pfound->has_arg == 1) {
2627 optarg = argv[optind++];
2632 "%s `%s' requires an argument\n",
2633 commandlineflag, argv[optind - 1]);
2634 show_line(log_line, 1);
2636 nextchar += strlen (nextchar);
2640 nextchar += strlen (nextchar);
2641 if (longind != NULL)
2642 *longind = option_index;
2645 *(pfound->flag) = pfound->val;
2650 /* Can't find it as a long option. If this is not getopt_long_only,
2651 or the option starts with '--' or is not a valid short
2652 option, then it's an error.
2653 Otherwise interpret it as a short option. */
2654 if (!long_only || argv[optind][1] == '-'
2655 || my_index (optstring, *nextchar) == NULL) {
2657 if (argv[optind][1] == '-') { /* --option */
2659 "don't understand %s `--%s'\n",
2660 commandlineflag, nextchar);
2661 show_line(log_line, 1);
2663 else { /* +option or -option */
2665 "don't understand %s `%c%s'\n",
2666 commandlineflag, argv[optind][0], nextchar);
2667 show_line(log_line, 1);
2670 nextchar = (char *) "";
2676 /* Look at and handle the next option-character. */
2679 char c = *nextchar++;
2680 char *temp = my_index (optstring, c);
2682 /* Increment `optind' when we start to process its last character. */
2683 if (*nextchar == '\0')
2686 /* if (temp == NULL || c == ':') */
2687 if (temp == NULL || c == ARGSEP) {
2689 if (c < 040 || c >= 0177) {
2692 "Unrecognized %s (0%o)\n", commandlineflag, c);
2693 show_line(log_line, 1);
2698 "Unrecognized %s `-%c'\n", commandlineflag, c);
2699 show_line(log_line, 1);
2704 /* if (temp[1] == ':') */
2705 if (temp[1] == ARGSEP) {
2706 /* if (temp[2] == ':') */
2707 if (temp[2] == ARGSEP) {
2708 /** This is an option that accepts an argument optionally. */
2709 if (*nextchar != '\0') {
2718 /* This is an option that requires an argument. */
2719 if (*nextchar != '\0') {
2721 /* If we end this ARGV-element by taking the rest as an arg,
2722 we must advance to the next element now. */
2725 else if (optind == argc) {
2728 "%s `-%c' requires an argument\n",
2729 commandlineflag, c);
2730 show_line(log_line, 1);
2735 /* We already incremented `optind' once;
2736 increment it again when taking next ARGV-elt as argument. */
2737 optarg = argv[optind++];
2744 /* kpathsea/getopt1.c */
2745 int getopt (int argc, char *const *argv, const char *optstring)
2747 return _getopt_internal (argc, argv, optstring,
2748 (const struct option *) 0,
2753 #pragma optimize ("", on)
2755 /* this uses output to stderr quite a bit for errors on command line */
2756 /* clean up command line option error output */
2757 /* disallow all the weird combinations like -- */
2759 //////////////////////////////////////////////////////////////////////