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
43 #include <io.h> // needed for _finddata_t
44 #include <ctype.h> // needed for isascii and isalpha
46 //////////////////////////////////////////////////////////////////////////////////
48 #define NAME_MAX 255 // max size of name component
50 #define ISALPHA(c) (isascii (c) && isalpha(c))
53 #define PATH_SEP_STRING "/"
54 #define PATH_DELIMITER ';'
55 #define PATH_DELIMITER_STRING ";"
57 // default paths to look for things
59 #define TEXPATH "C:/yandy/yandytex/"
61 #define TEXFORMATS TEXPATH "fmt"
62 #define TEXPOOL TEXPATH "pool"
63 #define TEXFONTS TEXPATH "tfm"
64 #define TEXINPUTS TEXPATH "tex//;" "C:/tex;" "C:/texinput"
66 // structure used by fontmap
68 typedef struct map_element_struct
72 struct map_element_struct *next;
75 typedef map_element_type **map_type;
77 extern bool usesourcedirectory; /* in local.c */
79 extern bool workingdirectory; /* in local.c */
81 bool absolute_p (string filename);
82 string readable (string name);
83 string truncate_pathname (string name);
84 char *file_p(string fn);
85 bool dir_p(string fn);
86 string *find_dir_list (string path);
87 void add_directory (string **dir_list_ptr, unsigned *dir_count_ptr, string dir);
88 void expand_subdir (string **dir_list_ptr, unsigned *dir_count_ptr, string dirname,
89 struct _finddata_t findt, integer recurseflag);
90 void save_dir_list (string path, string *dir_list);
91 string *initialize_path_list (string env_name, string default_path);
92 int xfind_path_filename (string buffer, string filename, string * dir_list);
93 string expand_default (string env_path, string default_path);
94 map_type map_create (string *dir_list);
95 char *map_lookup (map_type map, char *key);
97 // the following do *not* use MALLOC
99 extern char * xconcat (char *buffer, char *s1, char *s2); /* openinou.c */
100 extern char * xconcat3 (char *buffer, char *s1, char *s2, char *s3); /* openinou.c */
102 /////////////////////////////////////////////////////////////////////////
104 // used only in jump_out in tex0.c, and in texbody in itex.c
105 // and main in texmf.c and a few other abort situations in texmf.c
107 void uexit (int unix_code)
115 final_code = EXIT_SUCCESS;
116 else if (unix_code == 1)
117 final_code = EXIT_FAILURE;
119 final_code = unix_code;
121 show_line("Jump Buffer already used\n", 1);
125 // I removed the longjmp.
126 //longjmp(jumpbuffer, code+1); // 1999/Nov/7
130 // round a double being careful about very large and very small values
131 // used only in tex0.c, a Pascal function
133 integer zround (double r)
136 /* R can be outside the range of an integer if glue is stretching or
137 shrinking a lot. We can't do any better than returning the largest
138 or smallest integer possible in that case. It doesn't seem to make
139 any practical difference. */
142 else if (r < LONG_MIN)
151 /****************************************************************************/
153 // malloc with error checking and error message
155 address xmalloc (unsigned size)
157 address new_mem = (address) malloc (size);
158 if (new_mem == NULL) {
159 sprintf(log_line, "malloc: Unable to honor request for %u bytes.\n", size);
160 show_line(log_line, 1);
165 sprintf(log_line, "XMALLOC %d\n", size); /* 1996/Jan/20 */
166 show_line(log_line, 0);
172 // calloc with error checking - used by map_create
174 address xcalloc (unsigned nelem, unsigned elsize)
176 address new_mem = (address) calloc (nelem, elsize);
177 if (new_mem == NULL) {
178 sprintf(log_line, "Unable to honor request for %u elements of size %u.\n", nelem, elsize);
179 show_line(log_line, 1);
185 /* Return a copy of s in new storage. */ /* xmalloc does error checking */
187 string xstrdup (string s)
189 string pnew_string = (string) xmalloc (strlen (s) + 1);
192 sprintf(log_line, "XSTRDUP %d %s\n", strlen(s)+1, s);
193 show_line(log_line, 0);
196 return strcpy (pnew_string, s);
199 /* only used by line.c (which in turn is only used by fontmap.c) */
201 address xrealloc (address old_ptr, unsigned size)
205 if (old_ptr == NULL) new_mem = xmalloc (size);
207 new_mem = (address) realloc (old_ptr, size);
208 if (new_mem == NULL) {
209 sprintf(log_line, "Unable to honor request for %u bytes.\n", size);
210 show_line(log_line, 1);
217 // returns newly allocated string
219 string concat (string s1, string s2)
224 sprintf(log_line, "CONCAT %s and %s ", s1, s2);
225 show_line(log_line, 0);
228 answer = (string) xmalloc (strlen (s1) + strlen (s2) + 1);
233 sprintf(log_line, "=> %s\n", answer);
234 show_line(log_line, 0);
240 // returns newly allocated string
242 string concat3 (string s1, string s2, string s3)
247 sprintf(log_line, "CONCAT3 %s, %s, and %s ", s1, s2, s3);
248 show_line(log_line, 0);
251 answer = (string) xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
257 sprintf(log_line, "=> %s\n", answer);
258 show_line(log_line, 0);
264 /***********************************************************************/
266 // following used only in itex.c on pool file
268 /* Return true if we're at the end of FILE, else false. This implements
269 Pascal's `eof' builtin. */
270 /* It differs from C feof in that the latter is not true at end of file
271 unless an attempt has actually been made to read past EOF */
273 bool test_eof (FILE *file)
276 /* Maybe we're already at the end? */
279 if ((c = getc (file)) == EOF)
281 /* We weren't at the end. Back up. */
282 (void) ungetc (c, file);
286 /* Return true on end-of-line in FILE or at the end of FILE, else false. */
288 bool eoln (FILE *file)
295 (void) ungetc (c, file);
296 // return c == '\n' || c == EOF;
297 return c == '\n' || c == '\r' || c == EOF; // ???
298 /* Type mismatch (return) (int/enum) ? */
301 /***********************************************************************/
303 // following used only by fontmap.c and openinou.c
305 // #define FATAL_PERROR(s) do { perrormod (s); exit (errno); } while (0)
307 // perrormod puts error message on stdout instead of stderr
309 /* These routines just check the return status from standard library
310 routines and abort if an error happens. */
312 // xfopen used in open_input in openinou.c
314 FILE * xfopen (char *filename, char * fmode)
317 /* if share_flag is non-zero and we are opening for reading use fsopen */
318 /* f = fopen (filename, mode); */
319 if (share_flag == 0 || *fmode != 'r')
320 f = fopen (filename, fmode);
322 f = _fsopen (filename, fmode, share_flag);
324 // FATAL_PERROR (filename);
331 // xfclose not used ...
333 int xfclose (FILE *f, char *filename)
335 if (ferror(f) != 0 || fclose(f) != 0) {
336 // FATAL_PERROR (filename);
343 /********************************************************************************/
345 // following used only in map_lookup
347 // return pointer to start of extension --- or NULL if there isn't one
349 string find_suffix (string name)
354 dot_pos = strrchr (name, '.');
356 if ((slash_pos = strrchr (name, PATH_SEP)) != NULL);
357 else if ((slash_pos = strrchr (name, '\\')) != NULL);
358 else if ((slash_pos = strrchr (name, ':')) != NULL);
359 else slash_pos = name;
361 slash_pos = strrchr (name, PATH_SEP);
364 /* If the name is `foo' or `/foo.bar/baz', we have no extension. */
365 return dot_pos == NULL || dot_pos < slash_pos ? NULL : dot_pos + 1;
368 // remove extension of file name - returns copy or NULL
370 string remove_suffix (string s)
373 string suffix = find_suffix (s);
376 suffix--; /* Back up to before the dot. */
377 ret = (char *) xmalloc (suffix - s + 1);
378 strncpy (ret, s, suffix - s);
379 ret[suffix - s] = '\0';
386 // add extension to file name unless it already has one
387 // returns copy or the old one (warning: danger when freeing)
389 string extend_filename (string name, string default_suffix)
392 string suffix = find_suffix (name);
394 new_s = (suffix == NULL ? concat3 (name, ".", default_suffix) : name);
398 /****************************************************************************************/
402 #define BLOCK_SIZE 64
404 // this returns newly allocated character string
406 char *read_line (FILE *f)
409 unsigned int limit = BLOCK_SIZE;
410 unsigned int loc = 0;
411 char * line = (char *) xmalloc (limit);
413 /* while ((c = getc (f)) != EOF && c != '\n') */
414 while ((c = getc (f)) != EOF && c != '\n' && c != '\r') {
415 line[loc] = (char) c;
417 /* By testing after the assignment, we guarantee that we'll always
418 have space for the null we append below. We know we always
419 have room for the first char, since we start with BLOCK_SIZE. */
422 line = (char *) xrealloc (line, limit);
426 /* If we read anything, return it. This can't represent a last
427 ``line'' which doesn't end in a newline, but so what. */
428 /* This is Tom Rokicki's mistake -- lets fix it ! 1994/March/18 */
430 /* Terminate the string. We can't represent nulls in the file,
431 either. Again, it doesn't matter. */
434 else if (loc > 0) { /* c == EOF, but line not empty 1994/March/18 */
437 else { /* Real EOF --- at end of file. */
446 /* Modified version 97/May/17 to avoid malloc for every line read ... */
448 char * read_a_line (FILE *f, char *line, int limit)
453 /* while ((c = getc (f)) != EOF && c != '\n') */
454 while ((c = getc (f)) != EOF) {
455 if (c == '\n' || c == '\r') {
457 else continue; /* ignore \r\n and blank lines */
459 line[loc] = (char) c;
461 if (loc == limit-1) { /* very unlikely */
462 sprintf(log_line, " ERROR: line too long\n");
463 show_line(log_line, 1);
470 if (c != EOF || loc > 0) { /* normal line or EOF at end of line */
471 line[loc] = '\0'; /* terminate */
472 return line; /* and return */
474 else return(NULL); /* true EOF */
477 /****************************************************************************************/
479 /* from ourpaths.c */
481 #define BUILDNAMEDIRECT /* avoid malloc for string concat */
483 #define CACHEFILENAME /* cache last full path/file name 96/Nov/16 */
484 /* to speed up LaTeX 2e which opens files twice */
486 /* `path_dirs' is initialized in `set_paths', to a null-terminated array
487 of directories to search for. */
489 static string *path_dirs[LAST_PATH];
491 /* This sets up the paths, by either copying from an environment variable
492 or using the default path, which is defined as a preprocessor symbol
493 (with the same name as the environment variable) in `site.h'. The
494 parameter PATH_BITS is a logical or of the paths we need to set. */
496 void set_paths (int path_bits)
498 int n; /* 97/Apr/2 */
499 char *s, *t, *u; /* 94/Jan/6 */
500 char buffer[PATH_MAX];
502 /* eliminated lots of junk not needed for TeX itself 93/Nov/20 bkph */
504 /* added code to look for pool file in format directory also */
505 /* added code to look for fmt directory in tex directory also */
506 /* added code to look for tfm directory in tex directory also */
507 /* added code to look for some PC TeX flavour environment names 94/Jan/6 */
508 /* which, in case of formats, could lead to I'm stymied errors ... */
510 if (path_bits & TEXFORMATPATHBIT) {
513 if (grabenv(s) == NULL) { /* see if env var defined 94/May/19*/
514 strcpy(buffer, texpath); /* not, see if texpath\fmt is directory */
515 strcat(buffer, PATH_SEP_STRING);
516 strcat(buffer, "fmt");
518 sprintf(log_line, "Checking `%s' = %s %s %s\n", buffer, texpath, PATH_SEP_STRING, "fmt"); /* 95/Jan/25 */
519 show_line(log_line, 0);
521 if (dir_p(buffer)) t = xstrdup(buffer); /* 96/Jan/20 */
523 s = "TEXFMTS"; /* added PC-TeX version 94/Jan/6 */
524 if (getenv(s) == NULL) s = "TEXFMT"; /* em-TeX ... */
527 sprintf(log_line, "\nSetting up %s (default %s) ", "TEXFORMATS", t);
528 show_line(log_line, 0);
531 /* path_dirs[TEXFORMATPATH] = initialize_path_list ("TEXFORMATS", TEXFORMATS); */
532 /* path_dirs[TEXFORMATPATH] = initialize_path_list (s, TEXFORMATS); */
533 path_dirs[TEXFORMATPATH] = initialize_path_list (s, t);
534 /* if (t != TEXFORMATS) free (t); */
537 if (path_bits & TEXPOOLPATHBIT) {
540 if (grabenv(s) == NULL) { /* 1994/May/19 */
541 s = "TEXFORMATS"; /* try in format directory next */
542 if (grabenv(s) == NULL) { /* see if environment var defined */
543 strcpy(buffer, texpath); /* no, see if texpath\fmt is direct */
544 strcat(buffer, PATH_SEP_STRING);
545 strcat(buffer, "fmt");
547 sprintf(log_line, "Checking `%s' = %s %s %s\n", buffer, texpath, PATH_SEP_STRING, "fmt"); /* 95/Jan/25 */
548 show_line(log_line, 0);
550 if (dir_p(buffer)) t = xstrdup(buffer); /* 96/Jan/20 */
552 s = "TEXFMTS"; /* added PC-TeX version 94/Jan/6 */
553 if (getenv(s) == NULL) s = "TEXFMT"; /* em-TeX ... */
556 sprintf(log_line, "\nSetting up %s (default %s) ", "TEXPOOL", t);
557 show_line(log_line, 0);
561 /* path_dirs[TEXPOOLPATH] = initialize_path_list ("TEXPOOL", TEXPOOL); */
562 /* path_dirs[TEXPOOLPATH] = initialize_path_list (s, TEXPOOL); */
563 path_dirs[TEXPOOLPATH] = initialize_path_list (s, t);
564 /* if (t != TEXPOOL) free (t); */
567 if (path_bits & TFMFILEPATHBIT) {
569 /* Introduce encoding specific TEXFONTS env variable 97/April/2 */
570 if ((u = grabenv("ENCODING")) != NULL) { /* get ENCODING=... */
571 encoding_name = u; /* remember for error mess */
572 /* sprintf(log_line, "\nENCODING=%s\n", u); */
573 /* ENCODING is defined, now see if matching env variable */
574 if ((t = grabenv(u)) != NULL) { /* get TEXNANSI=c:\yandy\tfm; ... */
575 /* sprintf(loglein, "\nset %s=%s\n", u, t); */
576 /* prevent problems with TEXNANSI=1 and such mistakes! */
577 /* should have a drive letter and not be a number */
578 if (strchr(t, ':') != NULL &&
579 sscanf(t, "%d", &n) == 0) {
580 s = u; /* look here instead of TEXFONTS=... */
581 /* sprintf(log_line, "\nUSE %s\n", u); */
586 t = TEXFONTS; /* #define TEXFONTS TEXPATH "tfm" */
587 /* if (getenv(s) == NULL) { */
588 if (grabenv(s) == NULL) { /* 1994/May/19 */
589 strcpy(buffer, texpath); /* see if texpath\tfm is directory */
590 strcat(buffer, PATH_SEP_STRING);
591 strcat(buffer, "tfm");
593 sprintf(log_line, "Checking `%s' = %s %s %s\n",
594 buffer, texpath, PATH_SEP_STRING, "tfm"); /* 95/Jan/25 */
595 show_line(log_line, 0);
597 /* if (dir_p(buffer)) t = _strdup(buffer); */
598 if (dir_p(buffer)) t = xstrdup(buffer); /* 96/Jan/20 */
600 s = "TEXTFMS"; /* added PC-TeX version 94/Jan/6 */
601 if (getenv(s) == NULL) s = "TEXTFM"; /* em-TeX uses TEXTFM ... */
604 sprintf(log_line, "\nSetting up %s (default %s) ", "TEXFONTS", t);
605 show_line(log_line, 0);
608 /* path_dirs[TFMFILEPATH] = initialize_path_list ("TEXFONTS", TEXFONTS); */
609 /* path_dirs[TFMFILEPATH] = initialize_path_list (s, TEXFONTS); */
610 path_dirs[TFMFILEPATH] = initialize_path_list (s, t);
611 /* if (t != TEXFONTS) free (t); */
614 if (path_bits & TEXINPUTPATHBIT) {
615 if (format_specific) { /* 1994/Oct/25 */
616 s = format_name; /* try specific */
617 if (grabenv(s) == NULL) s = "TEXINPUTS"; /* no format specific */
619 else s = "TEXINPUTS"; /* normal case */
620 /* if (getenv(s) == NULL) */
621 if (grabenv(s) == NULL) { /* 1994/May/19 */
622 s = "TEXINPUT"; /* added PC-TeX vers 94/Jan/6 */
624 sprintf(log_line, "\nSetting up %s ", "TEXINPUTS");
625 show_line(log_line, 0);
628 /* path_dirs[TEXINPUTPATH] = initialize_path_list ("TEXINPUTS", TEXINPUTS); */
629 path_dirs[TEXINPUTPATH] = initialize_path_list (s, TEXINPUTS);
634 char last_filename[PATH_MAX]=""; /* last full path / file name found C */
635 char last_name[PATH_MAX]=""; /* last file name searched for C */
636 int last_path_index=-1; /* last path_index */
639 /* Look for NAME, a C string (no longer Pascal), in the colon-separated list
640 of directories given by `path_dirs[PATH_INDEX]'. If the search is
641 successful, leave the full pathname in NAME (which therefore must
642 have enough room for such a pathname), padded with blanks.
643 Otherwise, or if NAME is an absolute or relative pathname, just leave
646 /* changed to take C string 97/June/5 - used to take Pascal strings */
647 /* now expects null terminated strings */
649 bool test_read_access (unsigned char *name, int path_index)
651 #ifdef BUILDNAMEDIRECT
652 char buffer[PATH_MAX]; /* for constructing name 1996/Jan/20 */
653 int foundflag; /* true if path found */
658 if (open_trace_flag) {
659 sprintf(log_line, "Test read access for `%s' ", name); /* C */
660 show_line(log_line, 0);
663 if (*name == '\0') return FALSE; /* sanity check */
666 /* If file name and path_index matches - and saved filename exists */
667 /* then use cached full path / file name 96/Nov/16 */
668 if (cache_file_flag) {
669 if (path_index == last_path_index &&
670 strcmp(name, last_name) == 0 && *last_filename != '\0') {
671 if (open_trace_flag) {
672 sprintf(log_line, "\nFOUND `%s' (%d) IN CACHE: `%s' ",
673 name, path_index, last_filename);
674 /* name+1, path_index, last_filename); */
675 show_line(log_line, 0);
677 strcpy(name, last_filename);
680 last_path_index = path_index;
681 strcpy(last_name, name);
682 *last_filename = '\0'; /* in case not found */
686 /* Look for it. */ /* only call to find_path_filename in pathsrch.c */
687 #ifdef BUILDNAMEDIRECT
688 foundflag = xfind_path_filename (buffer, (char *)name, path_dirs[path_index]);
690 /* this returns either a newly allocated string or name */
691 /* will need to free it later again ... */
692 foundname = find_path_filename (name, path_dirs[path_index]);
694 /* If we didn't find it, and we're looking for a font, maybe it's
695 an alias defined in a mapping file. */
696 /* if (!foundname && path_index == TFMFILEPATH) */
697 #ifdef BUILDNAMEDIRECT
698 if (foundflag == 0 && path_index == TFMFILEPATH)
700 if (foundname == NULL && path_index == TFMFILEPATH)
704 static map_type fontmap = NULL; /* GLOBAL, so won't recreate */
706 /* fault in the mapping if necessary. */
707 if (fontmap == NULL) {
709 sprintf(log_line, "Loading in texfonts.map file for %s\n", name);
710 show_line(log_line, 0);
712 fontmap = map_create (path_dirs[path_index]);
715 /* Now look for our filename in the mapping. */
716 mapped_name = map_lookup (fontmap, (char *) name);
718 /* Found a possibility. Look for the new name. */
719 #ifdef BUILDNAMEDIRECT
720 foundflag = xfind_path_filename (buffer, mapped_name, path_dirs[path_index]);
722 foundname = find_path_filename (mapped_name, path_dirs[path_index]);
724 /* NOTE: mapped_name is NOT an allocated string to be freed ... */
728 if (open_trace_flag) {
729 show_line("\n", 0); /* improve trace format out 94/Jan/8 */
732 if (open_trace_flag) {
733 #ifdef BUILDNAMEDIRECT
734 if (foundflag != 0) {
735 sprintf(log_line, "`%s' in test_read_access\n", buffer);
736 show_line(log_line, 0);
739 if (foundname != NULL) {
740 sprintf(log_line, "`%s' in test_read_access\n", foundname);
741 show_line(log_line, 0);
746 /* If we found it somewhere, save it. */
747 #ifdef BUILDNAMEDIRECT
748 if (foundflag != 0) {
749 strcpy ((char *)name, buffer);
751 if (cache_file_flag) {
752 strcpy(last_filename, buffer); /* full path */
757 if (foundname != NULL) {
758 strcpy (name, foundname);
760 if (cache_file_flag) {
761 strcpy(last_filename, foundname); /* full path */
762 last_namelength = strlen(buffer);
768 #ifdef BUILDNAMEDIRECT
771 if (foundname == NULL) return FALSE;
773 if (foundname != name) free (foundname); /* copied, now free ??? 96/Jan/10 */
779 /********************************************************************/
781 #define STREQ(s1, s2) (strcmp (s1, s2) == 0)
785 /* Fontname mapping. We use a straightforward hash table. */
789 /* The hash function. We go for simplicity here. */
791 static unsigned map_hash (char *key)
795 /* There are very few font names which are anagrams of each other
796 so no point in weighting the characters. */
797 while (*key != 0) n += *key++;
802 /* Look up STR in MAP. Return the corresponding `value' or NULL. */
804 static char *map_lookup_str (map_type map, char *key)
807 unsigned n = map_hash (key);
809 for (p = map[n]; p != NULL; p = p->next)
810 if (STREQ (key, p->key)) return p->value;
816 /* Look up KEY in MAP; if it's not found, remove any suffix from KEY and
819 char *map_lookup (map_type map, char *key)
821 string suffix = find_suffix (key);
822 string ret = map_lookup_str (map, key);
825 /* OK, the original KEY didn't work. Let's check for the KEY without
826 an extension -- perhaps they gave foobar.tfm, but the mapping only
829 string base_key = remove_suffix (key);
830 ret = map_lookup_str (map, base_key);
831 free (base_key); // it's safe to free copy
835 /* Append the same suffix we took off, if necessary. */
836 /* what if suffix is NULL ??? */ /* what if we didn't take off suffix ??? */
838 if (ret && suffix) { /* 1994/March/18 */
839 ret = extend_filename (ret, suffix);
840 // the above creates a newly allocated string ... should free old ?
841 // except extend_filename may return the old one ?
846 /* If KEY is not already in MAP, insert it and VALUE. */
847 /* This was very buggy (when hash codes collided) - rewritten 94/March/18 */
849 void map_insert (map_type map, char *key, char *value)
851 unsigned n = map_hash (key);
852 map_element_type **ptr = &map[n];
853 /* map_element_type ***trailer = &p; */
855 while (*ptr != NULL && ! STREQ (key, (*ptr)->key)) {
856 /* *p = (*p)->next; */
857 ptr = &((*ptr)->next);
862 /* **trailer = XTALLOC (MAP_SIZE, map_element_type); *//* 94/March/19 */
863 *ptr = (map_element_type *) xmalloc (sizeof(map_element_type));
864 /* (**trailer)->key = xstrdup (key); */
865 (*ptr)->key = xstrdup (key);
866 /* (**trailer)->value = xstrdup (value); */
867 (*ptr)->value = xstrdup (value);
868 /* (**trailer)->next = NULL; */
873 /* Open and read the mapping file FILENAME, putting its entries into
874 MAP. Comments begin with % and continue to the end of the line. Each
875 line of the file defines an entry: the first word is the real
876 filename (e.g., `ptmr'), the second word is the alias (e.g.,
877 `Times-Roman'), and any subsequent words are ignored. .tfm is added
878 if either the filename or the alias have no extension. This is the
879 same order as in Dvips' psfonts.map; unfortunately, we can't have TeX
880 read that same file, since most of the real filenames start with an
881 `r', because of the virtual fonts Dvips uses. */
883 /* Modified 97/May/17 to avoid malloc for each line read */
889 int map_file_parse (map_type map, char *map_filename)
892 unsigned map_lineno = 0;
895 char line[MAXLINE]; /* 97/May/17 */
899 sprintf(log_line, "Opening %s\n", map_filename); /* 97/May/17 */
900 show_line(log_line, 0);
902 // f = xfopen (map_filename, FOPEN_R_MODE);
903 f = fopen (map_filename, FOPEN_R_MODE);
905 perrormod(map_filename); // should not happen, since we tested
909 while ((l = read_line (f)) != NULL)
911 while ((l = read_a_line (f, line, sizeof(line))) != NULL) /* 97/May/17 */
917 /* comment_loc = strrchr (l, '%'); */
918 comment_loc = strchr (l, '%'); /* 96/Nov/16 */
919 /* if (comment_loc == NULL) comment_loc = strrchr (l, ';'); */
920 if (comment_loc == NULL) comment_loc = strchr (l, ';'); /* fixed */
922 /* Ignore anything after a % or; */
923 /* if (comment_loc) *comment_loc = 0; */
924 if (comment_loc != NULL) *comment_loc = '\0';
928 /* If we don't have any filename, that's ok, the line is blank. */
929 filename = strtok (l, " \t");
931 if (filename != NULL) {
932 string alias = strtok (NULL, " \t");
934 /* But if we have a filename and no alias, something's wrong. */
935 if (alias == NULL || *alias == 0) {
937 " Have file name `%s', but no mapping (line %u in file %s).\n",
938 filename, map_lineno, map_filename);
939 show_line(log_line, 1);
942 /* We've got everything. Insert the new entry. */
943 map_insert (map, alias, filename);
950 // xfclose (f, map_filename);
951 (void) fclose (f); // we don't care about errors at this stage
955 void unshroud_string (char *, char *, int); /* in texmf.c */
957 /* Look for the file `texfonts.map' in each of the directories in
958 DIR_LIST. Entries in earlier files override later files. */
960 /* This is probably quite silly - but what the hell lets leave it in */
962 map_type map_create (string *dir_list)
964 map_type map = (map_type) xcalloc (MAP_SIZE, sizeof (map_element_type *));
967 char filename[PATH_MAX];
969 /* We don't bother with the filename truncation that `readable' in
970 `pathsrch.c' does, since we ourselves are giving the filename,
971 and I don't think it's worth worrying about too-long
972 intermediate directory names in the path. */
973 strcpy (filename, *dir_list);
974 /* strcat (filename, "texfonts.map"); */ /* 1993/Nov/20 */
975 unshroud_string (filename+strlen(filename),
976 "ufygpout/nbq", PATH_MAX - strlen(filename));
978 /* testing access first so xfopen won't fail... */
979 /* maybe do this another way to avoid using `access' ? */
981 if (file_p (filename) != NULL) { /* use file_p the new way */
982 (void) map_file_parse (map, filename);
986 /* if (access (filename, R_OK) == 0) */ /* use access the old way */
987 if (_access (filename, R_OK) == 0) { /* 1999/Jan/8 ??? */
988 /* if (readable (filename) != NULL) */
989 (void) map_file_parse (map, filename);
997 /**********************************************************************/
999 /* #pragma optimize ("g", off) *//* try and avoid compiler bug here _dos_find */
1001 /* NOTE: _dos_find... prevents running under Windows NT ??? */
1002 /* This is called if file_method != 0 */ /* which is currently the default */
1005 /* see whether a file exists, is readable and is not a directory */
1006 /* 1994/Feb/13 may be faster than `access' in `readable' */
1007 /* returns NULL or the name filename passed in ??? */
1009 char *file_p (string fn)
1011 struct _finddata_t fi;
1015 if (open_trace_flag) {
1016 sprintf(log_line, "Is `%s' a readable file? ", fn);
1017 show_line(log_line, 0);
1020 /* allow for `normal' (_A_NORMAL) as well as `read-only' files */
1022 hFind = _findfirst (fn, &fi);
1029 /* check whether found and whether *not* a sub-directory */
1031 if ((fi.attrib & _A_SUBDIR) == 0) {
1032 if (open_trace_flag) {
1033 sprintf(log_line, "`%s' IS a readable file. ", fn);
1034 show_line(log_line, 0);
1036 return fn; /* true - its a file, not a dir */
1039 if (open_trace_flag) {
1040 sprintf(log_line, "`%s' is a subdirectory. ", fn);
1041 show_line(log_line, 0);
1043 return NULL; /* false - directory */
1047 if (open_trace_flag) {
1048 sprintf(log_line, "`%s' is NOT a readable file. ", fn);
1049 show_line(log_line, 0);
1051 return NULL; /* false - not found or no read access */
1057 /* #pragma optimize ("g",) */ /* try and avoid compiler bug here _dos_find */
1058 /* #pragma optimize ("g",)*/ /* try and avoid compiler bug here _dos_find */
1059 // #pragma optimize ("", on) /* 96/Sep/15 */
1062 /**************************************************************************/
1064 /* S_IFMT is file type mask 0170000 and S_IFDIR is directory 0040000 */
1066 #pragma optimize ("g", off) /* try and avoid compiler bug here _dos_find */
1068 /* NOTE: _dos_find... prevents running under Windows NT ??? */
1069 /* and presently dir_method = true so we do use this _dos_find_first */
1071 bool dir_p (string fn)
1075 struct _finddata_t fi;
1078 char tmpfn[FILENAME_MAX]; /* long enough ??? */
1080 strcpy (tmpfn, fn); /* make copy so can modify */
1081 if (open_trace_flag) {
1082 sprintf(log_line, "Is `%s' a directory? ", tmpfn);
1083 show_line(log_line, 0);
1086 s = tmpfn + strlen(tmpfn) - 1;
1087 if (*s == '\\' || *s == '/') *s = '\0'; /* get rid of trailing path sep */
1089 /* quick test for "." and ".." case - avoid confusion later */
1090 if (strcmp (tmpfn, ".") == 0 || strcmp(tmpfn, "..") == 0) return 1;
1092 if (dir_method) { /* use _findfirst *first* if requested */
1093 hFind = _findfirst(tmpfn, &fi);
1101 /* _findfirst succeeded --- now test attributes of what was found */
1102 if (fi.attrib & _A_SUBDIR) {
1103 if (open_trace_flag) {
1104 sprintf(log_line, "Directory `%s' DOES exist ", fn);
1105 show_line(log_line, 0);
1107 return 1; /* true - it is a sub-directory */
1110 if (open_trace_flag) {
1111 sprintf(log_line, "`%s' is a FILE, not a DIRECTORY ", fn);
1112 show_line(log_line, 0);
1114 return 0; /* false - its a file, not a dir */
1118 /* _findfirst failed --- possible causes: missing *or* top-level */
1119 /* crude check first top level directory ? - assume form `c:' */
1120 if (*(tmpfn+1) != ':' || *(tmpfn+2) != '\0') {
1121 /* it is *not* top level and _findfirst failed - give up */
1122 if (open_trace_flag) {
1123 sprintf(log_line, "Directory `%s' does NOT exist ", fn);
1124 show_line(log_line, 0);
1126 return 0; /* false - it is not a directory */
1128 /* else drop through to old method */
1130 /* top-level dir, so revert to the old method after all ... */
1131 /* return dir_p_1 (fn); */
1132 /* or try _findfirst after appending PATH_SEP and nul ? */
1133 /* } */ /* drop through */
1137 /* either: dropped through (top-level of driver) or dir_method is false */
1138 /* use the old method --- fopen of nul in supposed directory */
1139 /* NOTE: nul device exists in all dirs */ /* Possible OS/2 and NDOS problem */
1140 strcat (tmpfn, PATH_SEP_STRING "nul");
1141 /* if ((test = fopen (tmpfn, "r")) == NULL) */
1142 if (share_flag == 0) test = fopen (tmpfn, "r");
1143 else test = _fsopen(tmpfn, "r", share_flag); /* 1994/July/12 */
1145 if (open_trace_flag) {
1146 sprintf(log_line, "Directory `%s' does NOT exist ", tmpfn);
1147 show_line(log_line, 0);
1149 return 0; /* false */
1152 (void) fclose(test); /* have to remember to close it again */
1153 if (open_trace_flag) {
1154 sprintf(log_line, "Directory `%s' DOES exist ", tmpfn);
1155 show_line(log_line, 0);
1157 return 1; /* true */
1161 /* #pragma optimize ("g",)*/ /* try and avoid compiler bug here _dos_find */
1162 /* #pragma optimize ("g") */ /* try and avoid compiler bug here _dos_find */
1163 #pragma optimize ("", on) /* 96/Sep/12 */
1165 /* NOTE: calling _stat makes packed EXE file 3,400 bytes larger ! */
1167 /* we don't want _fsopen instead of fopen here because only for dir\nul ??? */
1169 /********************************************************************************/
1173 /* If FILENAME is absolute or explicitly relative (i.e., starts with
1174 `/', `./', or `../'), or if DIR_LIST is null, we return whether
1175 FILENAME is readable as-is. Otherwise, we test if FILENAME is in any of
1176 the directories listed in DIR_LIST. (The last entry of DIR_LIST must
1177 be null.) We return the complete path if found, NULL else.
1179 In the interests of doing minimal work here, we assume that each
1180 element of DIR_LIST already ends with a `/'.
1182 DIR_LIST is most conveniently made by calling `initialize_path_list'.
1183 This is a separate routine because we allow recursive searching, and
1184 it may take some time to discover the list of directories.
1185 We do not want to incur that overhead every time we want to look for
1188 (Actually, `/' is not hardwired into this routine; we use PATH_SEP,
1191 /* xfind_path_filename is used now */
1193 /* Called only from test_read_access(...) in ourpaths.c */
1195 #ifdef BUILDNAMEDIRECT
1197 /* this string allocation / concatination is silly - use fixed buffer! */
1199 int xfind_path_filename (string buffer, string filename, string * dir_list)
1201 string found_name = NULL;
1203 if (buffer == filename) {
1204 show_line("buffer == filename\n", 1);
1207 *buffer = '\0'; /* "" in case we fail */
1209 if (open_trace_flag) {
1210 sprintf(log_line, "Find path for `%s' ", filename);
1211 show_line(log_line, 0);
1214 /* ignore current directory for TFM files ? */ /* 1994/Jan/24 */
1215 if (!current_tfm && strstr(filename, ".tfm") != NULL &&
1216 strcmp(*dir_list, "./") == 0) {
1217 if (open_trace_flag) {
1218 sprintf(log_line, "Ignoring `.' for %s ", filename);
1219 show_line(log_line, 0);
1221 dir_list++; /* step over first entry in dir list */
1224 if (trace_flag && open_trace_flag) { /* debugging trace 1994/Jan/8 */
1228 sprintf(log_line, "Find path for `%s' ", filename);
1229 show_line(log_line, 0);
1230 show_line("- IN: ", 0);
1231 while (*pstrs != NULL) {
1232 sprintf(log_line, "%s ", *pstrs);
1233 show_line(log_line, 0);
1239 /* Do this before testing for absolute-ness, as a leading ~ will be an
1240 absolute pathname. */ /* forget this for DOS ! */
1242 filename = expand_tilde (filename);
1246 /* is this always safe? That is, is filename writable and its OK to modify */
1247 /* unixify(filename); */ /* done `in place' */
1248 if (deslash) unixify(filename); /* made conditional 94/Feb/24 */
1251 /* following addded in attempt to catch `nul' */ /* 94/Jan/6 bkph */
1252 /* could also try and catch `nul.tex' first - but who cares about speed ? */
1253 /* needed to add this since `access' gets the wrong answer */
1254 /* that is, `nul' is a file that can be opened, but `access' says not so */
1255 if (strcmp(filename, "nul") == 0) strcpy(buffer, filename);
1256 /* If FILENAME is absolute or explicitly relative, or if DIR_LIST is
1257 null, only check if FILENAME is readable. */
1258 /* if (absolute_p (filename) || dir_list == NULL) */ /* 94/Jan/6 */
1259 else if (absolute_p (filename) || dir_list == NULL) {
1260 if (file_method) found_name = file_p (filename);
1261 else found_name = readable (filename);
1262 if (found_name != NULL) strcpy(buffer, found_name);
1263 else *buffer = '\0';
1265 else { /* Test if FILENAME is in any of the directories in DIR_LIST. */
1269 while (*dir_list != NULL) {
1270 /* if item is current directory, look in source file directory first */
1271 /* provided usesourcedirectory flag is set and workingdirectory in use */
1274 if (strcmp(s, "./") == 0) {
1275 if (firsttime && usesourcedirectory && workingdirectory &&
1276 source_direct != NULL && *source_direct != '\0') {
1279 sprintf(log_line, "Using %s dir %s %s\n", "source", s, "X");
1280 show_line(log_line, 0);
1282 sourceflag = 1; /* avoid increment of list below */
1283 firsttime = 0; /* special stuff only first time */
1285 else if (trace_flag) {
1286 sprintf(log_line, "Using %s dir %s %s\n", "current", s, "X");
1287 show_line(log_line, 0);
1291 sprintf(log_line, "XCONCAT %s %s in find_path_filename\n",
1293 show_line(log_line, 0);
1295 /* filename = concat (*dir_list, save_filename); */
1296 (void) xconcat (buffer, s, filename);
1297 if (file_method) found_name = file_p (buffer); /* new way */
1298 else found_name = readable (buffer); /* slow old way */
1299 if (found_name == NULL) {
1301 if (! sourceflag) /* 98/Sep/29 repeat in current dir */
1302 dir_list++; /* try next */
1305 if (found_name != buffer)
1306 strcpy(buffer, found_name); /* success */
1311 return (*buffer != '\0'); /* true if not empty string */
1316 /* We are dealing with a C string here for filename presumably ... */
1318 /* Also, this returns a string that was allocated --- */
1319 /* unless it happens to equal the filename sent in ... */
1320 /* this needs to be freed later - unless it happens to be ... */
1322 string find_path_filename (string filename, string * dir_list)
1324 string found_name = NULL;
1326 if (open_trace_flag) {
1327 // printf("Find path for `%s' ", filename);
1328 sprintf(log_line, "Find path for `%s' ", filename);
1329 show_line(log_line, 0);
1332 /* ignore current directory for TFM files ? */ /* 1994/Jan/24 */
1334 strstr(filename, ".tfm") != NULL &&
1335 strcmp(*dir_list, "./") == 0) {
1336 if (open_trace_flag) {
1337 sprintf(log_line, "Ignoring `.' for %s ", filename);
1338 show_line(log_line, 0);
1340 dir_list++; /* step over first entry in dir list */
1343 if (trace_flag && open_trace_flag) { /* debugging trace 1994/Jan/8 */
1347 sprintf(log_line, "Find path for `%s' ", filename);
1348 show_line(log_line, 0);
1349 show_line("- IN: ", 0);
1350 while (*pstrs != NULL) {
1351 // printf("%s ", *pstrs);
1352 sprintf(log_line, "%s ", *pstrs);
1353 show_line(log_line, 0);
1359 /* Do this before testing for absolute-ness, as a leading ~ will be an
1360 absolute pathname. */ /* forget this for DOS ! */
1362 filename = expand_tilde (filename);
1366 /* is this always safe? That is, is filename writable and its OK to modify */
1367 /* unixify(filename); */ /* done `in place' */
1368 if (deslash) unixify(filename); /* made conditional 94/Feb/24 */
1370 /* following addded in attempt to catch `nul' */ /* 94/Jan/6 bkph */
1371 /* could also try and catch `nul.tex' first - but who cares about speed ? */
1372 /* needed to add this since `access' gets the wrong answer */
1373 /* that is, `nul' is a file that can be opened, but `access' says not so */
1374 if (strcmp(filename, "nul") == 0) found_name = filename;
1375 /* If FILENAME is absolute or explicitly relative, or if DIR_LIST is
1376 null, only check if FILENAME is readable. */
1377 /* if (absolute_p (filename) || dir_list == NULL) */ /* 94/Jan/6 */
1378 else if (absolute_p (filename) || dir_list == NULL) {
1379 if (file_method) found_name = file_p (filename); /* new way 94/Feb/13 */
1380 else found_name = readable (filename); /* slow old way */
1382 else { /* Test if FILENAME is in any of the directories in DIR_LIST. */
1383 string save_filename = filename;
1387 while (*dir_list != NULL) {
1388 /* if item is current directory, look in source file directory first */
1389 /* provided usesourcedirectory flag is set and workingdirectory in use */
1392 if (strcmp(s, "./") == 0) {
1393 if (firsttime && usesourcedirectory && workingdirectory &&
1394 source_direct != NULL && *source_direct != '\0') {
1397 sprintf(log_line, "Using %s dir %s %s\n", "source", s, "F");
1398 show_line(log_line, 0);
1400 sourceflag = 1; /* avoid increment of list below */
1401 firsttime = 0; /* special stuff only first time */
1403 else if (trace_flag) {
1404 sprintf(log_line, "Using %s dir %s %s\n", "current", s, "F");
1405 show_line(log_line, 0);
1409 sprintf(log_line, "CONCAT %s %s in find_path_filename\n",
1410 s, save_filename); /* 1996/Jan/20 */
1411 show_line(log_line, 0);
1413 filename = concat (s, save_filename);
1414 /* found_name = readable (filename); */
1415 if (file_method) found_name = file_p (filename); /* new way */
1416 else found_name = readable (filename); /* slow old way */
1417 if (found_name == NULL) {
1418 free (filename); /* if this is not it, free it again */
1419 if (! sourceflag) /* 98/Sep/29 repeat in current dir */
1423 if (found_name != filename)
1424 free (filename); /* don't free if is the one passed in */
1429 return found_name; /* return the allocated name - free later */
1433 /* If NAME is readable, return it. If the error is ENAMETOOLONG,
1434 truncate any too-long path components and return the result (unless
1435 there were no too-long components, i.e., a overall too-long name
1436 caused the error, in which case return NULL). On any other error,
1439 POSIX invented this brain-damage of not necessarily truncating
1440 pathname components; the system's behavior is defined by the value of
1441 the symbol _POSIX_NO_TRUNC, but you can't change it dynamically! */
1443 /* Using access (and dir_p) is considerably slower than using dosfind */
1444 /* NOTE: this is only called from find_path_file,
1445 and then ONLY if file_method is false (-G) */
1447 /* returns NULL or the file name passed in ??? */
1449 /* static string readable (name) */
1450 string readable (string name)
1454 if (open_trace_flag) {
1455 sprintf(log_line, "is %s readable? ", name);
1456 show_line(log_line, 0);
1459 /* Check first whether we have read access, then */
1460 /* need to test if directory, since access always says OK for directory */
1461 /* BUT: readable is called only from find_path_file */
1462 /* So we never call this with directory, so why waste time ? bkph */
1463 /* BUT: can be caught out by idiot with a directory called myfile.tex ? */
1465 if (_access (name, R_OK) == 0) {
1466 if (test_dir_access) { /* check only if we are asked to ... */
1468 if (open_trace_flag) {
1469 sprintf(log_line, "tested read access of directory `%s' ", name);
1470 show_line(log_line, 0);
1478 /* if (_access (name, R_OK) == 0 && !dir_p (name)) ret = name; */
1480 else if (errno == ENAMETOOLONG) {
1481 ret = truncate_pathname (name);
1482 /* Perhaps some other error will occur with the truncated name, so
1483 let's call access again. */
1484 if (!(_access (ret, R_OK) == 0 && !dir_p (ret))) { /* Failed. */
1490 else if (errno == EACCES) {
1491 if (trace_flag) show_line("Access denied!\n", 0);
1494 else if (errno == ENOENT) {
1495 if (trace_flag) show_line("File or path name not found!\n", 1);
1500 sprintf(log_line, "Unknown access error %d!\n", errno);
1501 show_line(log_line, 0);
1509 /* Truncate any too-long path components in NAME, returning the result. */
1511 string truncate_pathname (string name)
1513 unsigned c_len = 0; /* Length of current component. */
1514 unsigned ret_len = 0; /* Length of constructed result. */
1515 string ret = (string) xmalloc (PATH_MAX + 1);
1517 for (; *name; name++)
1519 if (*name == PATH_SEP) /* not in DOS */
1520 { /* At a directory delimiter, reset component length. */
1523 else if (c_len > NAME_MAX)
1524 { /* If past the max for a component, ignore this character. */
1528 /* If we've already copied the max, give up. */
1529 if (ret_len == PATH_MAX)
1535 /* Copy this character. */
1536 ret[ret_len++] = *name;
1543 #endif /* end of ifdef ENAMETOOLONG */
1546 /* Return true if FILENAME is absolute or explicitly relative, else false. */
1547 /* Absolute: in DOS name starts with PATH_SEP, or with DRIVE letter and colon */
1548 /* Explicitly relative: starts with ./ or ../ */
1550 // static bool absolute_p (string filename) {
1551 bool absolute_p (string filename)
1554 bool explicit_relative;
1557 /* absolute = (*filename == PATH_SEP) */ /* 1994/Mar/1 */
1558 absolute = (*filename == PATH_SEP || *filename == '\\')
1559 || ((filename[1] == ':') && ISALPHA (*filename));
1560 /* || ISALPHA (*filename) && filename[1] == ':'; */
1562 absolute = (*filename == PATH_SEP);
1564 if (absolute) return true; /* don't waste any more time */
1567 explicit_relative = (*filename == '.'
1568 && ((filename[1] == PATH_SEP || filename[1] == '\\')
1569 || (filename[1] == '.' &&
1570 (filename[2] == PATH_SEP || filename[2] == '\\'))));
1572 explicit_relative = (*filename == '.' && (filename[1] == PATH_SEP
1573 || (filename[1] == '.' && filename[2] == PATH_SEP)));
1576 return explicit_relative;
1577 /* return absolute || explicit_relative; */ /* slight rewrite 1994/Feb/13 */
1581 /* note: this strips off trailing white space in actual environment var ... */
1582 void striptrailing (string env_value, string env_name, string default_path)
1585 if (env_name == NULL) { /* 1994/Feb/24 */
1587 sprintf(log_line, "WARNING: no env_name noted, using default %s\n",
1589 show_line(log_line, 0);
1593 if (env_value == NULL) {
1595 sprintf(log_line, "WARNING: %s not defined in environment, using default %s\n",
1596 env_name, default_path);
1597 show_line(log_line, 0);
1601 if (strlen(env_value) == 0) return;
1602 s = env_value + strlen(env_value) - 1;
1603 /* while (*s <= ' ') *s-- = '\0'; */
1604 while (s >= env_value && *s <= ' ')*s-- = '\0'; /* 94/Feb/24 */
1608 /* convert /! and /!! to / and // 97/Mar/22 */
1611 void convertexclam (string env_value) { /* 97/Mar/22 */
1613 if (env_value == NULL) return;
1615 if (strchr(s, '!') == NULL) return;
1616 while ((s = strchr(s, '!')) != NULL) {
1617 if (*(s+1) == '!') { /* double !! */
1618 if (*(s+2) == PATH_DELIMITER || *(s+2) == '\0') {
1619 if (s > env_value && *(s-1) == PATH_SEP) {
1620 *s = PATH_SEP; /* convert the first ! */
1621 strcpy(s+1, s+2); /* flush the second ! */
1625 else { /* single ! */ /* assume already unixified */
1626 if (*(s+1) == PATH_DELIMITER || *(s+1) == '\0') {
1627 if (s > env_value && *(s-1) == PATH_SEP)
1628 strcpy(s, s+1); /* just flush the ! */
1634 sprintf(log_line,"Now is %s\n", env_value);
1635 show_line(log_line, 0);
1640 /* Return a NULL-terminated array of directory names, each name ending
1641 with PATH_SEP, created by parsing the PATH_DELIMITER-separated list
1642 in the value of the environment variable ENV_NAME, or DEFAULT_PATH if
1643 the env var is not set.
1645 A leading or trailing PATH_DELIMITER in the value of ENV_NAME is replaced
1648 Any element of the path that ends with double PATH_SEP characters
1649 (e.g., `foo//') is replaced by all its subdirectories.
1651 If ENV_NAME is null, only parse DEFAULT_PATH. If both are null, do
1652 nothing and return NULL. */
1654 string *initialize_path_list (string env_name, string default_path)
1658 unsigned dir_count = 0;
1661 struct _finddata_t findt;
1662 /* _finddata_t structure *can* be reused, unlike _find_t 95/Jan/31 */
1663 /* so save on stack space, by having one copy here, not one per expand_subdir*/
1665 /* env_value = env_name ? getenv (env_name) : NULL; */
1666 env_value = env_name ? grabenv (env_name) : NULL; /* 1994/May/19 */
1668 /* need to convert \ to / as soon as possible to avoid confusion */
1669 /* we may be modifying DOS environment variable here ... is it always safe ? */
1671 if (deslash) unixify (env_value); /* 1994/Feb/24 */
1674 if (env_name) { /* only if env_name is non-null 94/Feb/24 */
1675 sprintf(log_line, "\nSet %s=", env_name);
1676 show_line(log_line, 0);
1677 if (env_value) { /* only if env_name value is set */
1678 show_line(env_value, 0);
1684 /* strip off trailing white space which would confuse things - bkph */
1685 striptrailing (env_value, env_name, default_path);
1686 convertexclam (env_value); /* 97/Mar/22 */
1688 orig_path = expand_default (env_value, default_path);
1690 if (orig_path == NULL || *orig_path == 0) return NULL;
1692 /* need to convert \ to / as soon as possible to avoid confusion */
1694 if (deslash) unixify (orig_path); /* redundant ? */
1697 /* If we've already seen this PATH_DELIMITER-separated list, then just get
1698 it back instead of going back to the filesystem. */
1699 dir_list = find_dir_list (orig_path);
1700 if (dir_list != NULL) return dir_list;
1702 /* Be sure `path' is in writable memory. */ /* if not, copy it */
1703 path = (orig_path == env_value || orig_path == default_path
1704 ? xstrdup (orig_path) : orig_path);
1706 /* Find each element in the path in turn. */
1708 /* if (!switchflag) */
1709 if (current_flag) { /* suppress adding current directory - debugging */
1711 sprintf(log_line, "Adding directory `%s'\n", "."); /* 95/Jan/24 */
1712 show_line(log_line, 0);
1714 add_directory(&dir_list, &dir_count, ".");
1717 for (dir = strtok (path, PATH_DELIMITER_STRING); dir != NULL;
1718 dir = strtok (NULL, PATH_DELIMITER_STRING)) {
1723 sprintf(log_line, "dir %s\n", dir);
1724 show_line(log_line, 0);
1727 /* If the path starts with ~ or ~user, expand it. Do this
1728 before calling `expand_subdir' or `add_directory', so that
1729 1) we don't expand the same ~ for every subdirectory; and
1730 2) pathnames in `expand_subdir' don't have a `~' in them
1731 (since the system won't grok `~/foo' as a directory). */
1733 dir = expand_tilde (dir);
1737 /* If `dir' is the empty string, ignore it. */
1738 if (len == 0) continue;
1740 /* If `dir' ends in double PATH_SEP, do subdirectories (and remove
1741 the second PATH_SEP, so the final pathnames we return don't look
1742 like foo//bar). Because we obviously want to do subdirectories
1743 of `dir', we don't check if it is a leaf. This means that if
1744 `dir' is `foo//', and `foo' contains only symlinks (so our leaf
1745 test below would be true), the symlinks are chased. */
1747 /* modified to treat single PATH_SEP as expand subdirs without recursion */
1748 /* modified to treat double PATH_SEP as expand subdirs *with* recursion */
1751 if (len > 2 && /* 1994/Mar/1 */
1752 (dir[len - 1] == PATH_SEP || dir[len - 1] == '\\')
1753 && (dir[len - 2] == PATH_SEP || dir[len - 2] == '\\'))
1755 if (len > 2 && dir[len - 1] == PATH_SEP && dir[len - 2] == PATH_SEP)
1758 if (open_trace_flag) {
1759 sprintf(log_line, "Double backslash on `%s' ", dir); /* bkph */
1760 show_line(log_line, 0);
1766 sprintf(log_line, "Adding directory `%s'\n", dir);
1767 show_line(log_line, 0);
1769 add_directory (&dir_list, &dir_count, dir);
1770 /* local variable 'findt' used without having been initialized ? &findt ? */
1771 expand_subdir (&dir_list, &dir_count, dir,
1772 findt, 1); /* 95/Jan/31 */
1775 /* following is new to find only directories to one level 1994/Jan/24 */
1777 else if (len > 1 && /* 1994/Mar/1 */
1778 (dir[len - 1] == PATH_SEP || dir[len - 1] == '\\'))
1780 else if (len > 1 && dir[len - 1] == PATH_SEP)
1783 if (open_trace_flag) {
1784 sprintf(log_line, "Single backslash on `%s' ", dir); /* bkph */
1785 show_line(log_line, 0);
1788 /* dir[len - 1] = 0; */
1791 sprintf(log_line, "Adding directory `%s'\n", dir);
1792 show_line(log_line, 0);
1794 add_directory (&dir_list, &dir_count, dir);
1795 expand_subdir (&dir_list, &dir_count, dir,
1796 findt, 0); /* 95/Jan/31 */
1799 else { /* Don't bother to add the directory if it doesn't exist. */
1802 sprintf(log_line, "Adding directory `%s'\n", dir);
1803 show_line(log_line, 0);
1805 add_directory (&dir_list, &dir_count, dir);
1812 show_line("Adding terminating null\n", 0);
1816 /* Add the terminating null entry to `dir_list'. */
1818 XRETALLOC (dir_list, dir_count, string);
1819 dir_list[dir_count - 1] = NULL;
1821 /* Save the directory list we just found. */
1822 save_dir_list (orig_path, dir_list);
1827 /* Subroutines for `initialize_path_list'. */
1829 /* Add a newly-allocated copy of DIR to the end of the array pointed to
1830 by DIR_LIST_PTR. Increment DIR_COUNT_PTR. Append a `/' to DIR if
1831 necessary. We assume DIR is a directory, to avoid an unnecessary
1834 void add_directory (string **dir_list_ptr, unsigned *dir_count_ptr, string dir)
1836 if (dir == NULL) return; /* paranoia 1995/Jan/24 */
1837 /* If `dir' does not end with a `/', add it. We can't just
1838 write it in place, since that would overwrite the null that
1839 strtok may have put in. So we ALWAYS make a copy of DIR. */
1841 dir = (dir[strlen (dir) - 1] == PATH_SEP || /* 1994/Mar/1 */
1842 dir[strlen (dir) - 1] == '\\') ?
1843 xstrdup (dir) : concat (dir, PATH_SEP_STRING);
1845 dir = (dir[strlen (dir) - 1] == PATH_SEP ? xstrdup (dir)
1846 : concat (dir, PATH_SEP_STRING));
1849 if (deslash) unixify (dir); /* redundant ? bkph */
1854 sprintf(log_line, "Adding directory `%s'\n", dir);
1855 show_line(log_line, 0);
1858 // if (open_trace_flag) {
1859 // sprintf(log_line, "Adding directory `%s' ", dir);
1860 // show_line(log_line, 0);
1864 /* Add `dir' to the list of the directories. */
1866 XRETALLOC (*dir_list_ptr, *dir_count_ptr, string);
1867 (*dir_list_ptr)[*dir_count_ptr - 1] = dir;
1870 void lowercase (char *s)
1872 while (*s) *s++ = (char) tolower(*s);
1876 /* These routines, while not strictly needed to be exported, are
1877 plausibly useful to be called by outsiders. */
1879 /* Replace a leading or trailing PATH_DELIMITER in ENV_PATH with
1880 DEFAULT_PATH. If neither is present, return ENV_PATH if that is
1881 non-null, else DEFAULT_PATH. */
1883 string expand_default (string env_path, string default_path)
1887 if (env_path == NULL) expansion = default_path;
1888 else if (*env_path == PATH_DELIMITER)
1889 expansion = concat (default_path, env_path);
1890 else if (env_path[strlen (env_path) - 1] == PATH_DELIMITER)
1891 expansion = concat (env_path, default_path);
1892 else expansion = env_path;
1894 if (trace_flag) { /* 1994/Jan/8 */
1895 if (env_path == NULL) {
1896 sprintf(log_line, "Using the default %s\n", expansion);
1897 show_line(log_line, 0);
1899 else if (expansion == env_path) {
1900 sprintf(log_line, "Using %s (default was %s)\n", expansion, default_path);
1901 show_line(log_line, 0);
1903 else { /* expansion != env_path */
1904 sprintf(log_line, "Expanded %s (default was %s) to %s\n",
1905 env_path, default_path, expansion);
1906 show_line(log_line, 0);
1914 /* Expand a leading ~ or ~user, Unix-style, unless we are some weirdo
1915 operating system. */
1917 string expand_tilde (string name)
1919 #if defined (MSDOS) || defined (VMS) || defined (VMCMS)
1925 /* If no leading tilde, do nothing. */
1929 /* If `~' or `~/', use $HOME if it exists, or `.' if it doesn't. */
1930 else if (name[1] == PATH_SEP || name[1] == 0) /* not in DOS */
1932 home = getenv ("HOME"); /* not in DOS */
1937 = name[1] == 0 ? home : concat3 (home, PATH_SEP_STRING, name + 2);
1940 /* If `~user' or `~user/', look up user in the passwd database. */
1946 while (name[c] != PATH_SEP && name[c] != 0) /* not in DOS */
1949 user = (string) xmalloc (c);
1950 strncpy (user, name + 1, c - 1);
1953 /* We only need the cast here for those (old deficient) systems
1954 which do not declare `getpwnam' in <pwd.h>. */
1955 p = (struct passwd *) getpwnam (user);
1957 /* If no such user, just use `.'. */
1958 home = p == NULL ? "." : p->pw_dir;
1960 expansion = name[c] == 0 ? home : concat (home, name + c);
1964 #endif /* not (DOS or VMS or VM/CMS) */
1968 // structure used for manipulation dir lists
1975 /* Routines to save and retrieve a directory list keyed by the original
1976 PATH_DELIMITER-separated path. This is useful because 1) it can take a
1977 significant amount of time to discover all the subdirectories of a
1978 given directory, and 2) many paths all have the same basic default,
1979 and thus would recompute the directory list. */
1981 static saved_path_entry *saved_paths = NULL;
1982 static unsigned saved_paths_length = 0;
1984 /* We implement the data structure as a simple linear list, since it's
1985 unlikely to ever be more than a dozen or so elements long. We don't
1986 bother to check here if PATH has already been saved; we always add it
1989 void save_dir_list (string path, string *dir_list)
1991 // saved_paths_length++;
1992 XRETALLOC (saved_paths, saved_paths_length+1, saved_path_entry);
1993 saved_paths[saved_paths_length].path = path;
1994 saved_paths[saved_paths_length].dir_list = dir_list;
1995 saved_paths_length++;
1998 /* When we retrieve, just check the list in order. */
2000 string *find_dir_list (string path)
2006 sprintf(log_line, "Find Dir List for path: %s\n", path);
2007 show_line(log_line, 0);
2011 for (p = 0; p < saved_paths_length; p++) {
2012 if (strcmp (saved_paths[p].path, path) == 0)
2013 return saved_paths[p].dir_list;
2018 /* Unixify filename and path (turn \ into /) --- assumes null terminated */
2020 char *unixify (char * t)
2023 if (s == NULL) return s; /* paranoia -- 1993/Apr/10 */
2026 while (*s != '\0') { /* paranoia -- 1997/Oct/23 */
2027 /* if (*s == '\\') *s = '/'; */
2028 if (*s == '\\') *s = PATH_SEP;
2034 sprintf(log_line, "Unixified name: %s\n", t);
2035 show_line(log_line, 0);
2042 /****************************************************************************/
2044 /* moved here to avoid problems with pragma */
2046 /* struct _finddata_t findt; */ /* make global, can be reused unlike _find_t */
2047 /* avoids heavy stack usage in tree search */
2048 /* but ties up some global fixed space ... */
2050 #pragma optimize ("g", off) /* try and avoid compiler bug here _dos_find */
2052 /* Add DIRNAME to DIR_LIST and look for subdirectories, possibly recursively.
2053 We assume DIRNAME is the name of a directory. */
2055 /* NOTE: _dos_find... prevents running under Windows NT as console app ??? */
2056 /* Yes, so lets flush it! use _findfirst, _findnext, _findclose instead */
2058 /* called only from initialize_path_list (and recursively) */
2060 void expand_subdir (string **dir_list_ptr, unsigned *dir_count_ptr, string dirname,
2061 struct _finddata_t findt, integer recurseflag)
2064 /* struct _finddata_t findt; */
2068 /* char buffer[PATH_MAX]; */ /* pretty long? potential recursion problem? */
2069 char buffer[FILENAME_MAX]; /* this is DOS and Windows NT after all ... */
2074 sprintf(log_line, "\nExpanding sub dir %s ", dirname);
2075 show_line(log_line, 0);
2079 strcpy(buffer, dirname);
2080 len = strlen(dirname);
2083 /* if (buffer[len-1] == PATH_SEP) strcat(buffer, "*.*"); */
2084 if (buffer[len-1] == PATH_SEP || buffer[len-1] == '\\')
2085 strcat(buffer, "*.*"); /* 1994/Mar/1 */
2086 else strcat(buffer, PATH_SEP_STRING "*.*");
2088 if (buffer[len-1] == PATH_SEP) strcat(buffer, "*");
2089 else strcat(buffer, PATH_SEP_STRING "*");
2092 /* Note: the _A_SUBDIR means we get ordinary files PLUS sub-directories */
2093 if (open_trace_flag) {
2094 sprintf(log_line, "\nDIRNAME `%s' ", dirname);
2095 show_line(log_line, 0);
2097 /* we'll need to step over `.' and `..' up front of directory list */
2098 hFind = _findfirst(buffer, &findt);
2099 if (hFind > 0) ret = 0;
2101 /* _dos_findnext( &findt ); */
2102 /* while(_dos_findnext(&findt)== 0) { */
2104 /* if (open_trace_flag) */
2105 if (open_trace_flag && trace_flag) {
2106 sprintf(log_line, "NEXT `%s' (%0x) ", findt.name, findt.attrib);
2107 show_line(log_line, 0);
2109 /* if (strchr(findt.name, '.') != NULL) continue; *//* not needed */
2110 if (findt.name[0] != '.' && /* ignore "." and ".." */
2111 findt.attrib & _A_SUBDIR){ /* only look at SUBDIRs */
2112 if (open_trace_flag) {
2113 sprintf(log_line, "\nDIRNAME `%s' ", dirname);
2114 show_line(log_line, 0);
2117 potential = concat3(dirname,
2118 (dirname[len-1] == PATH_SEP || dirname[len-1] == '\\')
2119 ? "" : PATH_SEP_STRING, findt.name);
2121 potential = concat3(dirname, dirname[len-1] == PATH_SEP
2122 ? "" : PATH_SEP_STRING, findt.name);
2124 lowercase (potential); /* make look nicer ? */
2125 if (open_trace_flag) {
2126 sprintf(log_line, "POTENTIAL `%s' ", potential);
2127 show_line(log_line, 0);
2130 sprintf(log_line, "Adding directory `%s'\n", potential); /* 95/Jan/24 */
2131 show_line(log_line, 0);
2133 add_directory(dir_list_ptr, dir_count_ptr, potential);
2135 expand_subdir(dir_list_ptr, dir_count_ptr,
2136 potential, findt, 1); /* 95/Jan/31 */
2138 } /* end of findt.attrib & _A_SUBDIR != 0 */
2139 ret = _findnext(hFind, &findt);
2142 if (hFind > 0) _findclose (hFind);
2146 _dos_findclose(&findt);
2149 #else /* end of MSDOS (way up there) */
2151 /* This is how we do this if we are NOT using DOS */
2155 char potential[PATH_MAX];
2158 /* We will be looking at its contents. */
2159 dir = opendir (dirname);
2163 /* Compute the length of DIRNAME, since it's loop-invariant. */
2164 length = strlen (dirname);
2166 /* Construct the part of the pathname that doesn't change. */
2167 strcpy (potential, dirname);
2168 if (potential[length - 1] != PATH_SEP) /* not in DOS */
2170 potential[length] = PATH_SEP;
2171 potential[length + 1] = 0;
2175 /* about to use _stat --- shouldn't get here when using MSDOS anyway */
2177 while ((e = readdir (dir)) != NULL)
2178 { /* If it's . or .., never mind. */
2179 if (!(e->d_name[0] == '.'
2180 && (e->d_name[1] == 0
2181 || (e->d_name[1] == '.' && e->d_name[2] == 0))))
2182 { /* If it's not a directory, we will skip it on the
2184 strcat (potential, e->d_name);
2186 /* If we can't _stat it, or if it isn't a directory, continue. */
2187 if (_stat (potential, &st) == 0 && S_ISDIR (st.st_mode))
2188 { /* It's a subdirectory; add `potential' to the list. */
2190 sprintf(log_line, "Adding directory `%s'\n", potential); /* 95/Jan/24 */
2191 show_line(log_line, 0);
2193 add_directory (dir_list_ptr, dir_count_ptr, potential);
2195 /* If it's not a leaf, quit. Assume that leaf
2196 directories have two links (one for . and one for ..).
2197 This means that symbolic links to directories do not affect
2198 the leaf-ness. This is arguably wrong, but the only
2199 alternative I know of is to _stat every entry in the
2200 directory, and that is unacceptably slow. */
2201 if (st.st_nlink > 2)
2202 { /* All criteria are met; find subdirectories. */
2203 expand_subdir (dir_list_ptr, dir_count_ptr, potential,
2204 findt, 1); /* 95/Jan/31 */
2208 /* ``Remove'' the directory entry name. */
2209 potential[length] = 0;
2214 #endif /* end of *not* DOS case */
2217 // #pragma optimize ("", on) /* 96/Sep/12 */
2219 /************************************************************************/
2223 /* This version of `getopt' appears to the caller like standard Unix `getopt'
2224 but it behaves differently for the user, since it allows the user
2225 to intersperse the options with the other arguments.
2227 As `getopt' works, it permutes the elements of ARGV so that,
2228 when it is done, all the options precede everything else. Thus
2229 all application programs are extended to handle flexible argument
2232 /* For communication from `getopt' to the caller.
2233 When `getopt' finds an option that takes an argument,
2234 the argument value is returned here.
2235 Also, when `ordering' is RETURN_IN_ORDER,
2236 each non-option ARGV-element is returned here. */
2240 /* Index in ARGV of the next element to be scanned.
2241 This is used for communication to and from the caller
2242 and for communication between successive calls to `getopt'.
2244 On entry to `getopt', zero means this is the first call; initialize.
2246 When `getopt' returns EOF, this is the index of the first of the
2247 non-option elements that the caller should itself scan.
2249 Otherwise, `optind' communicates from one call to the next
2250 how much of ARGV has been scanned so far. */
2254 /* The next char to be scanned in the option-element
2255 in which the last option character we returned was found.
2256 This allows us to pick up the scan where we left off.
2258 If this is zero, or a null string, it means resume the scan
2259 by advancing to the next ARGV-element. */
2261 static char *nextchar;
2263 /* Callers store zero here to inhibit the error message
2264 for unrecognized options. */
2268 /* Describe how to deal with options that follow non-option ARGV-elements.
2270 If the caller did not specify anything,
2271 the default is REQUIRE_ORDER if the environment variable
2272 POSIXLY_CORRECT is defined, PERMUTE otherwise.
2274 REQUIRE_ORDER means don't recognize them as options;
2275 stop option processing when the first non-option is seen.
2276 This is what Unix does.
2277 This mode of operation is selected by either setting the environment
2278 variable POSIXLY_CORRECT, or using `+' as the first character
2279 of the list of option characters.
2281 PERMUTE is the default. We permute the contents of ARGV as we scan,
2282 so that eventually all the non-options are at the end. This allows options
2283 to be given in any order, even with programs that were not written to
2286 RETURN_IN_ORDER is an option available to programs that were written
2287 to expect options and other ARGV-elements in any order and that care about
2288 the ordering of the two. We describe each non-option ARGV-element
2289 as if it were the argument of an option with character code 1.
2290 Using `-' as the first character of the list of option characters
2291 selects this mode of operation.
2293 The special argument `--' forces an end of option-scanning regardless
2294 of the value of `ordering'. In the case of RETURN_IN_ORDER, only
2295 `--' can cause `getopt' to return EOF with `optind' != ARGC. */
2299 REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
2303 #define my_index strchr
2304 #define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
2307 /* Handle permutation of arguments. */
2309 /* Describe the part of ARGV that contains non-options that have
2310 been skipped. `first_nonopt' is the index in ARGV of the first of them;
2311 `last_nonopt' is the index after the last of them. */
2313 static int first_nonopt;
2314 static int last_nonopt;
2316 /* Exchange two adjacent subsequences of ARGV.
2317 One subsequence is elements [first_nonopt,last_nonopt)
2318 which contains all the non-options that have been skipped so far.
2319 The other is elements [last_nonopt,optind), which contains all
2320 the options processed since those non-options were skipped.
2322 `first_nonopt' and `last_nonopt' are relocated so that they describe
2323 the new indices of the non-options in ARGV after they are moved. */
2325 static void exchange (char **argv)
2327 int nonopts_size; /* paranoia - bkph */
2328 char **temp; /* paranoia - bkph */
2329 /* int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); */
2330 nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
2331 /* char **temp = (char **) _alloca (nonopts_size); */
2332 temp = (char **) _alloca (nonopts_size);
2334 /* Interchange the two blocks of data in ARGV. */
2336 my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
2337 my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
2338 (optind - last_nonopt) * sizeof (char *));
2339 my_bcopy ((char *) temp,
2340 (char *) &argv[first_nonopt + optind - last_nonopt],
2343 /* Update records for the slots the non-options now occupy. */
2345 first_nonopt += (optind - last_nonopt);
2346 last_nonopt = optind;
2350 char *get_env_shroud (char *); /* in texmf.c */
2352 /* Scan elements of ARGV (whose length is ARGC) for option characters
2355 If an element of ARGV starts with '-', and is not exactly "-" or "--",
2356 then it is an option element. The characters of this element
2357 (aside from the initial '-') are option characters. If `getopt'
2358 is called repeatedly, it returns successively each of the option characters
2359 from each of the option elements.
2361 If `getopt' finds another option character, it returns that character,
2362 updating `optind' and `nextchar' so that the next call to `getopt' can
2363 resume the scan with the following option character or ARGV-element.
2365 If there are no more option characters, `getopt' returns `EOF'.
2366 Then `optind' is the index in ARGV of the first ARGV-element
2367 that is not an option. (The ARGV-elements have been permuted
2368 so that those that are not options now come last.)
2370 OPTSTRING is a string containing the legitimate option characters.
2371 If an option character is seen that is not listed in OPTSTRING,
2372 return '?' after printing an error message. If you set `opterr' to
2373 zero, the error message is suppressed but we still return '?'.
2375 If a char in OPTSTRING is followed by a colon, that means it wants an arg,
2376 so the following text in the same ARGV-element, or the text of the following
2377 ARGV-element, is returned in `optarg'. Two colons mean an option that
2378 wants an optional arg; if there is text in the current ARGV-element,
2379 it is returned in `optarg', otherwise `optarg' is set to zero.
2381 If OPTSTRING starts with `-' or `+', it requests different methods of
2382 handling the non-option ARGV-elements.
2383 See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
2385 Long-named options begin with `--' instead of `-'.
2386 Their names may be abbreviated as long as the abbreviation is unique
2387 or is an exact match for some defined option. If they have an
2388 argument, it follows the option name in the same ARGV-element, separated
2389 from the option name by a `=', or else the in next ARGV-element.
2390 When `getopt' finds a long-named option, it returns 0 if that option's
2391 `flag' field is nonzero, the value of the option's `val' field
2392 if the `flag' field is zero.
2394 The elements of ARGV aren't really const, because we permute them.
2395 But we pretend they're const in the prototype to be compatible
2398 LONGOPTS is a vector of `struct option' terminated by an
2399 element containing a name which is zero.
2401 LONGIND returns the index in LONGOPT of the long-named option found.
2402 It is only valid when a long-named option has been found by the most
2405 If LONG_ONLY is nonzero, '-' as well as '--' can introduce
2406 long-named options. */
2408 int _getopt_internal (int argc, char *const *argv, const char *optstring,
2409 const struct option *longopts, int *longind, int long_only)
2412 char *commandlineflag = "command line flag";
2416 /* Initialize the internal data when the first call is made.
2417 Start processing options with ARGV-element 1 (since ARGV-element 0
2418 is the program name); the sequence of previously skipped
2419 non-option ARGV-elements is empty. */
2422 first_nonopt = last_nonopt = optind = 1;
2426 /* Determine how to handle the ordering of options and nonoptions. */
2428 if (optstring[0] == '-') {
2429 ordering = RETURN_IN_ORDER;
2432 else if (optstring[0] == '+') {
2433 ordering = REQUIRE_ORDER;
2436 /* else if (getenv ("POSIXLY_CORRECT") != NULL) */
2437 else if (get_env_shroud ("QPTJYMZ`DPSSFDU") != NULL)
2438 ordering = REQUIRE_ORDER;
2443 if (nextchar == NULL || *nextchar == '\0') {
2444 if (ordering == PERMUTE) {
2445 /* If we have just processed some options following some non-options,
2446 exchange them so that the options come first. */
2448 if (first_nonopt != last_nonopt && last_nonopt != optind)
2449 exchange ((char **) argv);
2450 else if (last_nonopt != optind)
2451 first_nonopt = optind;
2453 /* Now skip any additional non-options
2454 and extend the range of non-options previously skipped. */
2456 while (optind < argc
2457 && (argv[optind][0] != '-' || argv[optind][1] == '\0')
2460 last_nonopt = optind;
2463 /* Special ARGV-element `--' means premature end of options.
2464 Skip it like a null option,
2465 then exchange with previous non-options as if it were an option,
2466 then skip everything else like a non-option. */
2468 if (optind != argc && !strcmp (argv[optind], "--")) {
2471 if (first_nonopt != last_nonopt && last_nonopt != optind)
2472 exchange ((char **) argv);
2473 else if (first_nonopt == last_nonopt)
2474 first_nonopt = optind;
2480 /* If we have done all the ARGV-elements, stop the scan
2481 and back over any non-options that we skipped and permuted. */
2483 if (optind == argc) {
2484 /* Set the next-arg-index to point at the non-options
2485 that we previously skipped, so the caller will digest them. */
2486 if (first_nonopt != last_nonopt)
2487 optind = first_nonopt;
2491 /* If we have come to a non-option and did not permute it,
2492 either stop the scan or describe it to the caller and pass it by. */
2494 if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) {
2495 if (ordering == REQUIRE_ORDER)
2497 optarg = argv[optind++];
2501 /* We have found another option-ARGV-element.
2502 Start decoding its characters. */ /* unusual use of bool */
2504 nextchar = (argv[optind] + 1
2505 + (longopts != NULL && argv[optind][1] == '-'));
2508 if (longopts != NULL
2509 && ((argv[optind][0] == '-'
2510 && (argv[optind][1] == '-' || long_only)))) {
2511 const struct option *p;
2515 const struct option *pfound = NULL;
2516 int indfound=0; /* keep compiler quiet */
2518 while (*s && *s != '=')
2521 /* Test all options for either exact match or abbreviated matches. */
2522 for (p = longopts, option_index = 0; p->name;
2523 p++, option_index++)
2524 if (!strncmp (p->name, nextchar, s - nextchar))
2526 /* if (s - nextchar == strlen (p->name)) */
2527 if (s - nextchar == (int) strlen (p->name)) { /* avoid warning bkph */
2528 /* Exact match found. */
2530 indfound = option_index;
2534 else if (pfound == NULL) {
2535 /* First nonexact match found. */
2537 indfound = option_index;
2540 /* Second nonexact match found. */
2544 if (ambig && !exact) {
2547 "%s `%s' is ambiguous\n", commandlineflag, argv[optind]);
2548 show_line(log_line, 1);
2550 nextchar += strlen (nextchar);
2555 if (pfound != NULL) {
2556 option_index = indfound;
2559 /* Don't test has_arg with >, because some C compilers don't
2560 allow it to be used on enums. */
2561 if (pfound->has_arg)
2565 if (argv[optind - 1][1] == '-') { /* --option */
2568 "%s `--%s' does not take an argument\n",
2569 commandlineflag,pfound->name);
2570 show_line(log_line, 1);
2572 else { /* +option or -option */
2575 "%s `%c%s' does not take an argument\n",
2576 commandlineflag, argv[optind - 1][0], pfound->name);
2577 show_line(log_line, 1);
2580 nextchar += strlen (nextchar);
2584 else if (pfound->has_arg == 1) {
2586 optarg = argv[optind++];
2591 "%s `%s' requires an argument\n",
2592 commandlineflag, argv[optind - 1]);
2593 show_line(log_line, 1);
2595 nextchar += strlen (nextchar);
2599 nextchar += strlen (nextchar);
2600 if (longind != NULL)
2601 *longind = option_index;
2604 *(pfound->flag) = pfound->val;
2609 /* Can't find it as a long option. If this is not getopt_long_only,
2610 or the option starts with '--' or is not a valid short
2611 option, then it's an error.
2612 Otherwise interpret it as a short option. */
2613 if (!long_only || argv[optind][1] == '-'
2614 || my_index (optstring, *nextchar) == NULL) {
2616 if (argv[optind][1] == '-') { /* --option */
2618 "don't understand %s `--%s'\n",
2619 commandlineflag, nextchar);
2620 show_line(log_line, 1);
2622 else { /* +option or -option */
2624 "don't understand %s `%c%s'\n",
2625 commandlineflag, argv[optind][0], nextchar);
2626 show_line(log_line, 1);
2629 nextchar = (char *) "";
2635 /* Look at and handle the next option-character. */
2638 char c = *nextchar++;
2639 char *temp = my_index (optstring, c);
2641 /* Increment `optind' when we start to process its last character. */
2642 if (*nextchar == '\0')
2645 /* if (temp == NULL || c == ':') */
2646 if (temp == NULL || c == ARGSEP) {
2648 if (c < 040 || c >= 0177) {
2651 "Unrecognized %s (0%o)\n", commandlineflag, c);
2652 show_line(log_line, 1);
2657 "Unrecognized %s `-%c'\n", commandlineflag, c);
2658 show_line(log_line, 1);
2663 /* if (temp[1] == ':') */
2664 if (temp[1] == ARGSEP) {
2665 /* if (temp[2] == ':') */
2666 if (temp[2] == ARGSEP) {
2667 /** This is an option that accepts an argument optionally. */
2668 if (*nextchar != '\0') {
2677 /* This is an option that requires an argument. */
2678 if (*nextchar != '\0') {
2680 /* If we end this ARGV-element by taking the rest as an arg,
2681 we must advance to the next element now. */
2684 else if (optind == argc) {
2687 "%s `-%c' requires an argument\n",
2688 commandlineflag, c);
2689 show_line(log_line, 1);
2694 /* We already incremented `optind' once;
2695 increment it again when taking next ARGV-elt as argument. */
2696 optarg = argv[optind++];
2704 int getopt (int argc, char *const *argv, const char *optstring)
2706 return _getopt_internal (argc, argv, optstring,
2707 (const struct option *) 0,
2712 #pragma optimize ("", on)
2714 /* this uses output to stderr quite a bit for errors on command line */
2715 /* clean up command line option error output */
2716 /* disallow all the weird combinations like -- */
2718 //////////////////////////////////////////////////////////////////////