source: trunk/third/tex/xdvi/filefind.c @ 12209

Revision 12209, 62.0 KB checked in by ghudson, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12208, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1996 Paul Vojta.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21
22/*
23 *      filefind.c - Routines to perform file searches.
24 */
25
26#include <stdlib.h>
27#include <unistd.h>
28#include <string.h>
29#include <memory.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <setjmp.h>
33#include <pwd.h>
34
35#include "filf_app.h"           /* application-related declarations & defs */
36#include "filefind.h"
37
38#ifndef XtNumber
39#define XtNumber(array) (sizeof(array) / sizeof(*array))
40#endif
41
42#ifndef ST_NLINK_TRICK
43#if     defined(VMS)
44#define ST_NLINK_TRICK  0
45#else
46#define ST_NLINK_TRICK  1
47#endif
48#endif  /* !defined(ST_NLINK_TRICK) */
49
50extern  char    *newstring ARGS((_Xconst char *, int));
51
52        /* these are defined so that the editor can match braces properly. */
53#define LBRACE  '{'
54#define RBRACE  '}'
55
56/*      Local copy of findrec record. */
57
58static  struct findrec  lrec;
59
60/*
61 *      Additional data structures.
62 */
63
64struct  statrec {               /* status vis-a-vis laying down the file name */
65        int     pos;                    /* position in ffline */
66        int     flags;
67        char    quickchar;              /* '\0' or 'q' or 'Q' */
68        int     quickpos;               /* value of pos (above) if 'q' or 'Q' */
69};
70
71struct  texmfrec {              /* list of texmf components */
72        struct texmfrec *next;
73        _Xconst char    *home;
74        _Xconst char    *str;
75        int             len;
76        int             flags;
77};
78
79#if     !CFG_FILE
80static  _Xconst char    default_texmf_path[]    = DEFAULT_TEXMF_PATH;
81#endif
82
83/*      These are global variables. */
84
85_Xconst char                    *fF_values[MAX_N_OPTS]; /* not static */
86
87static  FILE                    *file_found;
88static  jmp_buf                 got_it;
89static  struct steprec          **bracepp;
90static  struct steprec          **steppp;
91static  struct findrec          *frecp;         /* used to distinguish ls-Rs */
92static  int                     seqno;
93static  struct texmfrec         *texmfhead      = NULL;
94static  struct rootrec          **rootpp;
95static  _Xconst struct atomrec  *treeatom;
96#ifdef  PK_AND_GF
97static  int                     gflags;         /* global flags */
98static  Boolean                 pk_second_pass;
99#endif
100
101        /* dummy records for handling %s after // or wild cards */
102static  struct atomrec  pct_s_dummy = {NULL, NULL, NULL, F_PCT_S};
103static  struct atomrec  pct_s_dummy_slash
104                        = {NULL, NULL, NULL, F_PCT_S | F_SLASH_SLASH};
105
106        /* flag value for directories not read yet */
107static  struct treerec  not_read_yet = {NULL, NULL, NULL};
108
109/*
110 *      The following expands in 128-byte increments to be as long as is
111 *      necessary.  We don't often use pointers to refer to elements of this
112 *      array, since it may move when it is enlarged.
113 */
114
115#ifndef EXTERN_FFLINE
116static  char    *ffline         = NULL;
117static  int     ffline_len      = 0;    /* current length of the array */
118#endif
119
120
121/*
122 *      ls-R database hash table
123 */
124
125struct  lsr {
126        struct lsr      *next;          /* next in hash chain */
127        struct findrec  *frecp;         /* which search type? */
128        short           seqno;          /* which ls-R invocation we are using */
129        short           keylen;         /* length of key */
130        _Xconst char    *key;
131        _Xconst char    *value;
132};
133
134static  struct lsr      *lsrtab[1024];  /* hash table */
135
136
137/*
138 *      Forward references.
139 */
140
141static  void            dobrace();
142static  struct steprec  *scan_pct_s();
143static  void            atomize_pct_s();
144
145
146#ifndef EXTERN_FFLINE
147
148/*
149 *      Expand ffline[] to at least the given size.
150 */
151
152static  void
153expandline(n)
154        int     n;
155{
156        int     newlen  = n + 128;
157
158        ffline = (ffline == NULL) ? xmalloc(newlen, "space for file paths")
159                : xrealloc(ffline, newlen, "space for file paths");
160        ffline_len = newlen;
161}
162
163#endif  /* !EXTERN_FFLINE */
164
165
166/*
167 *      prehash() - hash function (before modding).
168 */
169
170unsigned int
171prehash(str, len)
172        _Xconst char    *str;
173        int             len;
174{
175        _Xconst char    *p;
176        unsigned int    hash;
177
178        hash = 0;
179        for (p = str + len; p > str; )
180            hash = hash * 5 + *--p;
181        return hash;
182}
183
184
185#ifndef EXTERN_GETPW
186
187/*
188 *      Look up the home directory.
189 */
190
191static  _Xconst struct passwd *
192ff_getpw(pp, p_end)
193        _Xconst char    **pp;
194        _Xconst char    *p_end;
195{
196        _Xconst char            *p      = *pp;
197        _Xconst char            *p1;
198        int                     len;
199        _Xconst struct passwd   *pw;
200
201        ++p;    /* skip the tilde */
202        p1 = p;
203        while (p1 < p_end && *p1 != '/') ++p1;
204        len = p1 - p;
205        if (len == 0)   /* if no user name */
206            pw = getpwuid(getuid());
207        else {
208            if (len >= ffline_len)
209                expandline(len);
210            bcopy(p, ffline, len);
211            ffline[len] = '\0';
212            pw = getpwnam(ffline);
213        }
214        if (pw != NULL)
215            *pp = p1;
216        return pw;
217}
218
219#endif  /* !EXTERN_GETPW */
220
221
222static  void
223gethome(pp, p_end, homepp)
224        _Xconst char    **pp;
225        _Xconst char    *p_end;
226        _Xconst char    **homepp;
227{
228        _Xconst struct passwd   *pw;
229
230        pw = ff_getpw(pp, p_end);
231        if (pw != NULL)
232            *homepp = newstring(pw->pw_dir, -1);
233}
234
235
236#if     CFGFILE
237
238struct envrec           *envtab[128];   /* hash table for local environment */
239
240struct cfglist {                /* linked list of config files we've done */
241        struct cfglist  *next;
242        _Xconst char    *value;
243};
244
245struct cfglist  *cfghead;       /* head of that linked list */
246
247/* global stuff for the fancy loop over config files. */
248
249static  _Xconst struct envrec   *lastvar                = NULL;
250static  _Xconst char            *deflt                  = DEFAULT_CONFIG_PATH;
251
252/*
253 *      Get the node containing a local environment variable.
254 */
255
256struct envrec *
257ffgetenv(key)
258        _Xconst char    *key;
259{
260        int             keylen;
261        struct envrec   *envp;
262
263        keylen = strlen(key);
264        envp = envtab[prehash(key, keylen) % XtNumber(envtab)];
265        ++keylen;
266        for (;;) {
267            if (envp == NULL ||
268              (envp->key != NULL && memcmp(envp->key, key, keylen) == 0))
269                break;
270            envp = envp->next;
271        }
272        return envp;
273}
274
275
276/*
277 *      Store the value of a local environment variable.
278 */
279
280static  void
281ffputenv(key, keylen, value, flag)
282        _Xconst char    *key;
283        int             keylen;
284        _Xconst char    *value;
285        Boolean         flag;
286{
287        struct envrec   **envpp;
288        struct envrec   *envp;
289        _Xconst char    *key1;
290
291        envpp = envtab + prehash(key, keylen) % XtNumber(envtab);
292        ++keylen;
293        for (;;) {      /* loop to find the key in the hash chain */
294            envp = *envpp;
295            if (envp == NULL) {         /* if no existing entries */
296                key1 = newstring(key, keylen);
297                break;
298            }
299            if (envp->key != NULL && memcmp(key, envp->key, keylen) == 0) {
300                /* skip to the end of the chain of identical keys */
301                while (envp->next != NULL && envp->next->key == NULL)
302                    envp = envp->next;
303                /* If we already defined this variable this time around. */
304                if (envp->flag)
305                    return;
306                /* If this is a getenv() placeholder. */
307                if (envp->value == NULL) {
308                    envp->value = value;
309                    envp->flag = flag;
310                    return;
311                }
312                envpp = &envp->next;
313                key1 = NULL;
314                break;
315            }
316            envpp = &envp->next;
317        }
318
319        envp = (struct envrec *) xmalloc(sizeof(*envp), "Local env record");
320        envp->next = *envpp;
321        envp->key = key1;
322        envp->value = value;
323        envp->flag = flag;
324        *envpp = envp;
325}
326
327
328#ifdef  SELFAUTO
329
330        /* string containing values of SELFAUTODIR and SELFAUTOPARENT */
331static  _Xconst char    *selfautostr    = NULL;
332static  int             selfautodirlen;
333static  int             selfautoparentlen;
334
335/*
336 *      Resolve a symlink.  Returns the length of the new string.
337 */
338
339static  int
340getrealname(pos, len)
341        int     pos;    /* position in ffline[] of beginning of filename */
342        int     len;    /* length of string in ffline[] */
343{
344        struct stat     statbuf;
345        char            *buffer;
346        int             bufsize;
347        char            *buf1;
348        int             pos1;
349        int             len1;
350
351        for (;;) {      /* loop over symlinks in chain */
352            if (lstat(ffline + pos, &statbuf) != 0) {
353                perror(ffline + pos);
354                return len;
355            }
356            if (!S_ISLNK(statbuf.st_mode))
357                break;
358            buffer = xmalloc(statbuf.st_size + 1, "symlink contents");
359            bufsize = readlink(ffline + pos, buffer, statbuf.st_size + 1);
360            if (bufsize < 0 || bufsize > statbuf.st_size) {
361                perror(ffline + pos);
362                return len;
363            }
364            buffer[bufsize] = '\0';
365            buf1 = buffer;
366            if (buffer[0] == '/')       /* if absolute path, just replace */
367                pos1 = pos;     /* copy it to the beginning */
368            else {
369                pos1 = pos + len;
370                /* find preceding slash */
371                while (pos1 > pos && ffline[--pos1] != '/') ;
372                /* get rid of multiple slashes */
373                while (pos1 > pos && ffline[pos1 - 1] == '/') --pos1;
374                for (;;) {
375                    if (buf1[0] == '.' && buf1[1] == '/')
376                        buf1 += 2;
377                    else if (buf1[0] == '.' && buf1[1] == '.' && buf1[2] == '/')
378                    {
379                        buf1 += 3;
380                        ffline[pos1] = '\0';
381                        pos1 = getrealname(pos, pos1 - pos) + pos;
382                        /* back up to preceding slash */
383                        while (pos1 > pos && ffline[--pos1] != '/') ;
384                        /* get rid of multiple slashes */
385                        while (pos1 > pos && ffline[pos1 - 1] == '/') --pos1;
386                    }
387                    else break;
388                    while (*buf1 == '/') ++buf1;
389                }
390                ++pos1; /* put the slash back */
391            }
392            len1 = buffer + bufsize + 1 - buf1;
393            if (pos1 + len1 >= ffline_len)
394                expandline(pos1 + len1);
395            bcopy(buf1, ffline + pos1, len1);
396            free(buffer);
397            len = pos1 + len1 - pos - 1;
398        }
399        return len;
400}
401
402
403/*
404 *      Initialize SELFAUTODIR and SELFAUTOPARENT.
405 */
406
407static  void
408selfautoinit(pos)
409        int     pos;
410{
411        _Xconst char    *p;
412        int             len;
413        _Xconst char    *path;
414        int             argv0len;
415        int             pos1;
416        struct stat     statbuf;
417
418        if (index(argv0, '/') != NULL) {  /* if program was called directly */
419            len = strlen(argv0);
420            if (pos + len >= ffline_len)
421                expandline(pos + len);
422            bcopy(argv0, ffline + pos, len);
423        }
424        else {                  /* try to find it in $PATH */
425            path = getenv("PATH");
426            argv0len = strlen(argv0) + 1;
427            len = 0;
428            if (path != NULL)
429                for (;;) {
430                    p = index(path, ':');
431                    if (p == NULL) p = path + strlen(path);
432                    len = p - path;
433                    pos1 = pos + len;
434                    if (pos + len + argv0len >= ffline_len)
435                        expandline(pos + len + argv0len);
436                    bcopy(path, ffline + pos, len);
437                    ffline[pos1] = '/';
438                    bcopy(argv0, ffline + pos1 + 1, argv0len);
439                    len += argv0len;
440                    if (stat(ffline + pos, &statbuf) == 0
441                      && S_ISREG(statbuf.st_mode)
442                      && (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
443                        break;
444                    if (*p == '\0') {
445                        len = 0;
446                        break;
447                    }
448                    path = p + 1;
449                }
450        }
451
452        /* Now we have:  file name starting in ffline[pos], and len = length. */
453
454        len = getrealname(pos, len);
455        p = ffline + pos + len;
456        while (p > ffline + pos && *--p != '/') ;
457        len = p - (ffline + pos);
458        selfautostr = newstring(ffline + pos, len);
459        p = selfautostr + len;
460        while (p > selfautostr && *--p != '/') ;
461        selfautodirlen = p - selfautostr;
462        while (p > selfautostr && *--p != '/') ;
463        selfautoparentlen = p - selfautostr;
464
465        if (FFDEBUG) {
466            fputs("SELFAUTODIR = ", stdout);
467            fwrite(selfautostr, 1, selfautodirlen, stdout);
468            fputs("\nSELFAUTOPARENT = ", stdout);
469            fwrite(selfautostr, 1, selfautoparentlen, stdout);
470            putchar('\n');
471        }
472}
473
474#endif  /* SELFAUTO */
475
476
477/*
478 *      Do a dollar substitution of this variable.  Return position in ffline.
479 */
480
481static  int     envexpand();
482
483static  int
484dollarsub(key, keylen, pos, percent)
485        _Xconst char    *key;
486        int             keylen;
487        int             pos;
488        char            percent;
489{
490        _Xconst char    *env_value;
491        struct envrec   *env_rec;
492
493#ifdef  SELFAUTO
494        if (keylen >= 11 && memcmp(key, "SELFAUTO", 8) == 0
495          && ((keylen == 11 && memcmp(key + 8, "DIR", 3) == 0)
496          || (keylen == 14 && memcmp(key + 8, "PARENT", 6) == 0))) {
497            int len;
498
499            if (selfautostr == NULL)
500                selfautoinit(pos);
501            len = (keylen == 11 ? selfautodirlen : selfautoparentlen);
502            if (pos + len >= ffline_len)
503                expandline(pos + len);
504            bcopy(selfautostr, ffline + pos, len);
505            return pos + len;
506        }
507#endif
508
509        if (pos + keylen >= ffline_len)
510            expandline(pos + keylen);
511        bcopy(key, ffline + pos, keylen);
512        ffline[pos + keylen] = '\0';
513        env_value = getenv(ffline + pos);
514        env_rec = ffgetenv(ffline + pos);
515        if (env_rec == NULL)
516            if (env_value == NULL)
517                return pos;     /* no value found */
518            else {              /* create a placeholder record */
519                ffputenv(ffline + pos, keylen, NULL, False);
520                env_rec = ffgetenv(ffline + pos);
521            }
522
523        if (env_rec->flag)      /* if recursive call */
524            return pos;
525        if (env_value == NULL) env_value = env_rec->value;
526        env_rec->flag = True;
527        pos = envexpand(env_value, env_value + strlen(env_value), pos, percent);
528        env_rec->flag = False;
529        return pos;
530}
531
532
533/*
534 *      Expand all environment references in a string and add the result to
535 *      ffline.  Return the length of ffline.
536 */
537
538#define LBRACE  '{'
539#define RBRACE  '}'
540
541static  int
542envexpand(p, p_end, pos, percent)
543        _Xconst char    *p;
544        _Xconst char    *p_end;
545        int             pos;
546        char            percent;
547{
548        _Xconst char    *p1;
549        _Xconst char    *p2;
550
551        for (;;) {      /* transfer this to ffline */
552            for (p2 = p;;) {    /* find the next $ */
553                _Xconst char *p3;
554
555                p1 = memchr(p2, '$', p_end - p);
556                if (p1 == NULL) {
557                    p1 = p_end;
558                    break;
559                }
560                p3 = p1;
561                while (p3-- > p2 && *p3 == percent) ;
562                /* if preceded by an even number of percents */
563                if ((p3 - p1) % 2 != 0)
564                    break;
565                p2 = p1 + 1;
566            }
567            if (p1 > p) {
568                if (pos + (p1 - p) >= ffline_len)
569                    expandline(pos + (p1 - p));
570                bcopy(p, ffline + pos, p1 - p);
571                pos += p1 - p;
572            }
573            if (p1 >= p_end)
574                break;
575            ++p1;
576            if (*p1 == LBRACE) {
577                ++p1;
578                for (p = p1;; ++p) {
579                    if (p >= p_end) /* if syntax error */
580                        break;
581                    if (*p == RBRACE) {
582                        pos = dollarsub(p1, p - p1, pos, percent);
583                        ++p;
584                        break;
585                    }
586                }
587            }
588            else {
589                p = p1;
590                while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')
591                  || (*p >= '0' && *p <= '9') || *p == '_')
592                    ++p;
593                pos = dollarsub(p1, p - p1, pos, percent);
594            }
595        }
596        return pos;
597}
598
599
600/*
601 *      Do all expansions up to the next colon or '\0'.
602 */
603
604static  int
605form_path_part(pathpp, percent)
606        _Xconst char    **pathpp;
607        char            percent;
608{
609        _Xconst char    *path;
610        _Xconst char    *p;
611        _Xconst char    *p_end;
612        int             pos;
613
614        path = *pathpp;
615        p_end = p;
616        for (p = path;;) {      /* find end of path part */
617            _Xconst char *p1;
618
619            p_end = index(p, ':');
620            if (p_end == NULL) {
621                p_end = p + strlen(p);
622                break;
623            }
624            p1 = p_end;
625            while (p1 > p && *--p1 == percent) ;
626            /* if preceded by an even number of percents */
627            if ((p_end - p1) % 2 != 0)
628                break;
629            p = p_end + 1;
630        }
631        *pathpp = p_end;
632
633        return envexpand(path, p_end, 0, percent);
634}
635
636/*
637 *      Read a config file.
638 */
639
640static  void
641read_config_file(f)
642        FILE    *f;
643{
644        int             pos;
645        char            *p;
646        char            *keybegin, *keyend, *valend;
647        struct envrec   *envp;
648        struct envrec   **envpp;
649
650        for (;;) {      /* loop over input lines */
651
652            /*
653             * Read a line into ffline[].  It can be arbitrarily long.
654             * We may assume here that ffline_len > 0 since ffline[] was used
655             * to hold the full name of the config file.
656             */
657
658            pos = 0;
659            for (;;) {
660                int     len;
661
662                if (fgets(ffline + pos, ffline_len - pos, f) == NULL) break;
663                len = strlen(ffline + pos);
664                if (len == 0) break;
665                pos += len;
666                if (ffline[pos - 1] == '\n')
667                    break;
668                if (pos + 2 >= ffline_len)
669                    expandline(pos);
670            }
671
672            if (pos == 0) break;        /* if end of file */
673
674            if (ffline[pos - 1] == '\n')        /* trim off trailing \n */
675                ffline[--pos] = '\0';
676
677            p = ffline;
678            while (*p == ' ' || *p == '\t') ++p;
679            if (*p == '\0') continue;   /* blank line */
680            if (*p == '%' || *p == '#') continue;       /* comment */
681
682            keybegin = p;
683            while (*p != '\0' && *p != ' ' && *p != '\t' && *p != '='
684              && *p != '.')
685                ++p;
686            keyend = p;
687            while (*p == ' ' || *p == '\t') ++p;
688            if (*p == '.') {    /* if qualified by a program name */
689                _Xconst char    *progbegin;
690
691                ++p;
692                while (*p == ' ' || *p == '\t') ++p;
693                progbegin = p;
694                while (*p != '\0' && *p != ' ' && *p != '\t' && *p != '=')
695                    ++p;
696                if (memcmp(progbegin, prog, p - progbegin) != 0
697                  || prog[p - progbegin] != '\0')
698                  /* if program name does not match */
699                    continue;
700                while (*p == ' ' || *p == '\t') ++p;
701            }
702            if (*p != '=') {    /* syntax error */
703                if (FFDEBUG)
704                    printf("Config line rejected (bad syntax):  %s\n", ffline);
705                continue;
706            }
707            do
708                ++p;
709            while (*p == ' ' || *p == '\t');
710            valend = ffline + pos;
711            while (valend > p && (valend[-1] == ' ' || valend[-1] == '\t'))
712                --valend;
713            *keyend = *valend = '\0';
714            ffputenv(keybegin, keyend - keybegin, newstring(p, valend - p + 1),
715              True);
716        }
717
718        /*
719         * Clear all the flag fields.  This indicates that the variables are
720         * no longer new.
721         */
722
723        for (envpp = envtab; envpp < envtab + XtNumber(envtab); ++envpp)
724            for (envp = *envpp; envp != NULL; envp = envp->next)
725                envp->flag = False;
726}
727
728
729/*
730 *      Read a config file path list.
731 */
732
733static  void
734rd_cfg(paths)
735        _Xconst char            *paths;
736{
737        int             pos;
738        FILE            *f;
739        struct cfglist  **cfgpp;
740
741        for (;;) {
742            if (*paths == ':' || *paths == '\0') {      /* do the default */
743                if (deflt != NULL) {
744                    _Xconst char *d1 = deflt;
745
746                    deflt = NULL;
747                    rd_cfg(d1);         /* do the compiled-in default */
748                }
749            }
750            else {
751                pos = form_path_part(&paths, '\0');
752                if (ffline[0] == '~') {
753                    _Xconst char *home = NULL;
754                    char *ffp = ffline;
755
756                    gethome(&ffp, ffline + pos, &home);
757                    if (home != NULL) {
758                        int     homelen = strlen(home);
759                        int     pos1    = ffp - ffline;
760
761                        pos -= pos1;
762                        if (pos + homelen >= ffline_len)
763                            expandline(pos + homelen);
764                        bcopy(ffline + pos1, ffline + homelen, pos);
765                        bcopy(home, ffline, homelen);
766                        pos += homelen;
767                        free((char *) home);
768                    }
769                }
770                if (pos > 0 && ffline[pos - 1] == '/')
771                    --pos;      /* trim trailing slash */
772                if (pos + 11 >= ffline_len)
773                    expandline(pos);
774                ffline[pos] = '\0';
775                cfgpp = &cfghead;
776                for (;;) {                      /* check for duplicates */
777                    struct cfglist      *cfgp;
778
779                    if (*cfgpp == NULL) {       /* if not a duplicate */
780                        /* Add it to the list */
781                        cfgp = (struct cfglist *)
782                          xmalloc(sizeof(*cfgp), "Linked list node");
783                        cfgp->next = NULL;
784                        cfgp->value = newstring(ffline, pos + 1);
785                        *cfgpp = cfgp;
786
787                        bcopy("/texmf.cnf", ffline + pos, 11);
788                        if (FFDEBUG)
789                            printf("Reading config file %s\n", ffline);
790                        f = fopen(ffline, "r");
791                        if (f == NULL) {
792                            if (FFDEBUG) perror(ffline);
793                        }
794                        else {
795                            read_config_file(f);
796                            fclose(f);
797                            /* Did it define a new TEXMFCNF value? */
798                            /* If so, utilize its value. */
799                            if (lastvar == NULL) {
800                                lastvar = ffgetenv("TEXMFCNF");
801                                if (lastvar != NULL)
802                                    rd_cfg(lastvar->value);
803                            }
804                            else if (lastvar->next != NULL
805                              && lastvar->next->key == NULL) {
806                                lastvar = lastvar->next;
807                                rd_cfg(lastvar->value);
808                            }
809                        }
810                        break;
811                    }
812                    cfgp = *cfgpp;
813                    if (strcmp(cfgp->value, ffline) == 0) {
814                        if (FFDEBUG)
815                            printf(
816                              "Skipped duplicate config file %s/texmf.cnf\n",
817                              ffline);
818                        break;
819                    }
820                    cfgpp = &cfgp->next;
821                }
822            }
823            if (*paths == '\0') break;
824            ++paths;
825        }
826}
827
828
829/*
830 *      Read the config file (public routine).
831 */
832
833void
834#ifndef CFG2RES
835readconfig()
836#else
837readconfig(cfg2reslist, cfg2resend, appres, appres_end)
838        _Xconst struct cfg2res  *cfg2reslist;
839        _Xconst struct cfg2res  *cfg2resend;
840        XtResource              *appres;
841        XtResource              *appres_end;
842#endif
843{
844        _Xconst char    *paths;
845#ifdef  CFG2RES
846        _Xconst struct cfg2res  *cfgp;
847#endif
848
849        paths = getenv("TEXMFCNF");
850        if (paths == NULL) {    /* if no env variable set */
851            paths = deflt;      /* use compiled-in default */
852            deflt = NULL;       /* mark it as used */
853        }
854        rd_cfg(paths);
855
856#ifdef  CFG2RES
857        for (cfgp = cfg2reslist; cfgp < cfg2resend; ++cfgp) {
858            _Xconst struct envrec *envp;
859            _Xconst char *value;
860            XtResource *resp;
861
862            envp = ffgetenv(cfgp->cfgname);
863            if (envp == NULL) continue;
864            value = envp->value;
865            if (value == NULL) continue;        /* if getenv() marker record */
866            for (resp = appres; resp < appres_end; ++resp)
867                if (strcmp(cfgp->resname, resp->resource_name) == 0) {
868                    resp->default_addr = cfgp->numeric ? (XtPointer) atoi(value)
869                      : (XtPointer) value;
870                    break;
871                }
872        }
873#endif  /* CFG2RES */
874}
875
876#endif  /* if CFGFILE */
877
878
879/*
880 *      Scan the path part from // or wild cards, to the end.
881 *      This assumes that {} have already been taken care of.
882 */
883
884static  void
885atomize(p0, sp)
886        _Xconst char    *p0;
887        struct steprec  *sp;
888{
889        struct atomrec  *head;
890        struct atomrec  **linkpp;
891        int             flags;          /* F_SLASH_SLASH and F_WILD */
892        Boolean         percent_s;
893        _Xconst char    *p      = p0;
894        _Xconst char    *p1;
895        _Xconst char    *p2;
896
897        /*
898         * First check the string for braces.
899         */
900
901        for (p1 = p; ;++p1) {
902            if (*p1 == '\0' || *p1 == ':')
903                break;
904            if (*p1 == LBRACE) {
905                /* Take care of braces first.  This code should only be reached
906                 * if atomize() is called from prescan(). */
907                int n;
908
909                bracepp = &sp->nextstep;
910                n = p1 - p0;
911                if (n >= ffline_len)
912                    expandline(n);
913                bcopy(p0, ffline, n);
914                dobrace(p1, n, 1);
915                return;
916            }
917            if (*p1 == '%' && p1[1] != '\0')
918                ++p1;
919        }
920
921        /*
922         * No braces.  Split it up into atoms (delimited by / or //).
923         */
924
925        linkpp = &head;
926        flags = 0;
927        percent_s = False;
928        for (;;) {
929            if (*p == '/') {
930                ++p;
931                flags |= F_SLASH_SLASH;
932                /* a %[qQ] may occur following the first //. */
933                if (*p == '%')
934                    if (p[1] == 'q' || p[1] == 'Q') {
935                        if (p[1] == 'Q') flags |= F_QUICKONLY;
936                        flags |= F_QUICKFIND;
937                        p += 2;
938                    }
939            }
940            p1 = p;
941            for (;; ++p) {
942                if (*p == '\0' || *p == ':') {
943                    p2 = NULL;
944                    if (!(sp->flags & F_FILE_USED))
945                        p2 = lrec.no_f_str + 1;
946                    break;
947                }
948                if (*p == '/') {
949                    p2 = p + 1;
950                    break;
951                }
952                if (*p == '%' && p[1] != '\0' && p[1] != '/') {
953                    ++p;
954                    if (*p == 'f') sp->flags |= F_FILE_USED;
955#ifdef  PK_AND_GF
956                        /* set it in sp because we may never reach try_to_open*/
957                    else if (*p == lrec.pk_opt_char) sp->flags |= F_PK_USED;
958#endif
959                    else if (*p == 's' && (p[1] == '\0' || p[1] == ':')) {
960                        --p;
961                        percent_s = True;
962                        if (lrec.v.pct_s_atom == NULL)
963                            atomize_pct_s();
964                        break;
965                    }
966                }
967                else if (*p == '*' || *p == '?' || *p == '[')
968                    flags |= F_WILD;
969            }
970            if (p != p1) {      /* add on an atomrec */
971                struct atomrec  *atomp;
972
973                atomp = (struct atomrec *) xmalloc(sizeof(struct atomrec),
974                    "Record for tail of path string");
975                *linkpp = atomp;
976                linkpp = &atomp->next;
977                atomp->flags = flags;
978                flags = 0;
979                atomp->p = p1;
980                atomp->p_end = p;
981            }
982            if (percent_s) {
983                *linkpp = (flags & F_SLASH_SLASH) ? &pct_s_dummy_slash
984                    : &pct_s_dummy;
985                sp->flags |= F_PCT_S;
986                break;
987            }
988            p = p2;
989            if (p == NULL) {
990                *linkpp = NULL;
991                break;
992            }
993        }
994        sp->atom = head;
995}
996
997/*
998 *      prescan2() sets up the steprec information for a brace alternative.
999 *      It also does much of the work in prescan().
1000 */
1001
1002static  _Xconst char *
1003prescan2(sp, p)
1004        struct steprec  *sp;
1005        _Xconst char    *p;
1006{
1007        int             flags;
1008
1009        /*
1010         * Scan the string, looking for // or wild cards or braces.
1011         * It takes the input / by / so that we can back up to the previous
1012         * slash if there is a wild card (which may be hidden in braces).
1013         */
1014
1015        flags = sp->flags;
1016        for (;;) {      /* loop over slash-separated substrings */
1017            sp->strend = p;
1018            sp->flags = flags;
1019            if (*p == '/') {    /* if it's a // */
1020                atomize(p, sp);
1021                return p;
1022            }
1023            for (;;) {  /* loop over characters in this subpart */
1024                if (*p == '\0' || *p == ':') {
1025                    sp->strend = p;
1026                    sp->flags = flags;
1027                    return p;
1028                }
1029                if (*p == '/')
1030                    break;
1031                if (*p == '%' && p[1] != '\0') {
1032                    ++p;
1033                    if (*p == 's' && (p[1] == '\0' || p[1] == ':')) {
1034                        sp->strend = p - 1;
1035                        sp->flags = flags;
1036                        sp->nextstep = scan_pct_s();
1037                        return p;
1038                    }
1039                    else if (*p == 'f')
1040                        flags |= F_FILE_USED | F_VARIABLE;
1041                    else if (*p == 'F' || *p == lrec.x_var_char)
1042                        flags |= F_VARIABLE;
1043#ifdef  PK_AND_GF
1044                    else if (*p == lrec.pk_opt_char)
1045                        flags |= F_PK_USED;
1046#endif
1047                    /* %c and %C are sorted out later. */
1048                }
1049                else if (*p == '*' || *p == '?' || *p == '[') {
1050                    atomize(sp->strend, sp);
1051                    return p;
1052                }
1053                else if (*p == LBRACE) {
1054                    int n;
1055
1056                    bracepp = &sp->nextstep;
1057                    n = p - sp->strend;
1058                    if (n >= ffline_len)
1059                        expandline(n);
1060                    bcopy(sp->strend, ffline, n);
1061                    dobrace(p, n, 1);
1062                    return p;
1063                }
1064                ++p;
1065            }
1066            ++p;
1067        }
1068}
1069
1070/*
1071 *      dobrace() is the recursive routine for handling {} alternatives.
1072 *      bracepp (global) = address of where to put next brace item on the
1073 *              linked list.  It has to be global because dobrace() is called
1074 *              recursively and leaves its droppings at varying levels.
1075 *      p = input string pointer; it should point to the opening brace.
1076 *      pos = position within ffline[] for output string.
1077 *      level = nesting depth.
1078 */
1079
1080static  void
1081dobrace(p, pos0, level)
1082        _Xconst char    *p;
1083        int             pos0;
1084        int             level;
1085{
1086        int             lev;
1087        int             level1;
1088        _Xconst char    *p1;
1089        int             pos;
1090
1091        for (;;) {      /* loop over the alternatives */
1092            lev = 0;
1093            ++p;        /* skip left brace or comma */
1094            pos = pos0;
1095            for (;;) {  /* loop over characters */
1096                if (*p == '\0' || *p == ':') {
1097                        /* keep the braces matched:  { */
1098                    fprintf(stderr, "xdvi: Missing } in %s path.\n", lrec.type);
1099                    return;
1100                }
1101                else if (*p == '%') {
1102                    if (p[1] != '\0') {
1103                        if (pos >= ffline_len)
1104                            expandline(pos);
1105                        ffline[pos++] = *p++;
1106                    }
1107                }
1108                else if (*p == LBRACE) {
1109                    dobrace(p, pos, level + 1);
1110                    /* skip to the next matching comma or right brace */
1111                    for (;;) {
1112                        if (*p == '\0' || *p == ':')
1113                            return;     /* this has already been reported */
1114                        else if (*p == '%') {
1115                            if (p[1] != '\0') ++p;
1116                        }
1117                        else if (*p == LBRACE)
1118                            ++lev;
1119                        else if (*p == ',') {
1120                            if (lev == 0)
1121                                break;
1122                        }
1123                        else if (*p == RBRACE) {
1124                            if (lev == 0)
1125                                break;
1126                            --lev;
1127                        }
1128                        ++p;
1129                    }
1130                    goto out2;
1131                }
1132                else if (*p == ',' || *p == RBRACE)
1133                    break;
1134                if (pos >= ffline_len)
1135                    expandline(pos);
1136                ffline[pos++] = *p++;
1137            }
1138            p1 = p;
1139            level1 = level;
1140            for (;;) {
1141                /* skip until matching right brace */
1142                for (;;) {
1143                    if (*p1 == '\0' || *p1 == ':')
1144                        goto out2;      /* this has already been reported */
1145                    if (*p1 == '%') {
1146                        if (p1[1] != '\0') ++p1;
1147                    }
1148                    else if (*p1 == LBRACE)
1149                        ++lev;
1150                    else if (*p1 == RBRACE) {
1151                        if (lev == 0) {
1152                            ++p1;
1153                            break;
1154                        }
1155                        --lev;
1156                    }
1157                    ++p1;
1158                }
1159                --level1;
1160                /* now copy until a qualifying comma */
1161                for (;;) {
1162                    if (*p1 == '\0' || *p1 == ':') {
1163                        if (level1 == 0) {
1164                            struct steprec *bp;
1165
1166                            if (pos >= ffline_len)
1167                                expandline(pos);
1168                            ffline[pos++] = '\0';
1169                            *bracepp = bp = (struct steprec *)
1170                                xmalloc(sizeof(struct steprec),
1171                                    "Font search brace record");
1172                            bp->next = NULL;
1173                            bp->atom = NULL;
1174                            bp->nextstep = NULL;
1175                            bracepp = &bp->next;
1176                            bp->str = newstring(ffline, pos);
1177                            bp->flags = 0;
1178                            prescan2(bp, bp->str);
1179                        }
1180                        /* else:  this has already been reported */
1181                        goto out2;
1182                    }
1183                    if (*p1 == '%') {
1184                        if (p1[1] != '\0') {
1185                            if (pos >= ffline_len)
1186                                expandline(pos);
1187                            ffline[pos++] = '%';
1188                            ++p1;
1189                        }
1190                    }
1191                    else if (*p1 == LBRACE) {
1192                        dobrace(p1, pos, level1 + 1);
1193                        goto out2;
1194                    }
1195                    else if (*p1 == ',' && level1 > 0)
1196                        break;
1197                    else if (*p1 == RBRACE && level1 > 0) {
1198                        --level1;
1199                        ++p1;
1200                        continue;
1201                    }
1202                    if (pos >= ffline_len)
1203                        expandline(pos);
1204                    ffline[pos++] = *p1++;
1205                }
1206            }
1207            out2:
1208            if (*p != ',') break;
1209        }
1210}
1211
1212
1213/*
1214 *      Prescan the '%s' string.  This is done only once.
1215 */
1216
1217static  struct steprec *
1218scan_pct_s()
1219{
1220        struct steprec  **headpp;
1221        _Xconst char    *p;
1222        struct steprec  *sp;
1223
1224        if (lrec.v.pct_s_head != NULL)  /* if we've already done this */
1225            return lrec.v.pct_s_head;
1226
1227        headpp = &lrec.v.pct_s_head;
1228        p = lrec.pct_s_str;
1229        for (p = lrec.pct_s_str; ; ++p) {
1230            ++lrec.v.pct_s_count;
1231            *headpp = sp = (struct steprec *) xmalloc(sizeof(struct steprec),
1232                "record for %s in path search code");
1233            sp->atom = NULL;
1234            sp->nextstep = NULL;
1235            sp->flags = 0;
1236            sp->str = p;
1237            headpp = &sp->next;
1238            prescan2(sp, p);
1239            for (; *p != ':' && *p != '\0'; ++p)
1240                if (*p == '%' && p[1] != '\0') ++p;
1241            if (*p == '\0') break;
1242        }
1243        *headpp = NULL;
1244        return lrec.v.pct_s_head;
1245}
1246
1247/*
1248 *      Pre-atomize the '%s' strings.  Again, done only once.
1249 */
1250
1251static  void
1252atomize_pct_s()
1253{
1254        _Xconst struct atomrec  **app;
1255        struct steprec          *sp;
1256
1257        (void) scan_pct_s();    /* make sure the count is valid */
1258
1259        app = lrec.v.pct_s_atom = (_Xconst struct atomrec **)
1260                xmalloc((unsigned) lrec.v.pct_s_count * sizeof(*app),
1261                "list of atomized chains for %s in path search code");
1262
1263        for (sp = lrec.v.pct_s_head; sp != NULL; sp = sp->next, ++app) {
1264            static struct steprec dummy;
1265
1266            dummy.flags = 0;
1267            atomize(sp->str, &dummy);
1268            *app = dummy.atom;
1269#ifdef  PK_AND_GF
1270            dummy.atom->flags |= dummy.flags;
1271#endif
1272        }
1273}
1274
1275
1276/*
1277 *      Init_texmf - Initialize the linked list of options for %t.
1278 */
1279
1280static  void            /* here's a small subroutine */
1281add_to_texmf_list(pp)
1282        _Xconst char    **pp;
1283{
1284        static  struct texmfrec **tpp   = &texmfhead;
1285        struct texmfrec         *tp;
1286        _Xconst char            *p;
1287        _Xconst char            *p_end;
1288        _Xconst char            *home   = NULL;
1289        int                     flags   = 0;
1290#if     CFGFILE
1291        _Xconst char            *p0;
1292        int                     pos;
1293#endif
1294
1295#if     CFGFILE
1296        p0 = *pp;
1297        pos = form_path_part(pp, '\0');
1298        p = ffline;
1299        p_end = ffline + pos;
1300        ffline[pos] = '\0';
1301#else
1302        p = *pp;
1303        p_end = index(p, ':');
1304        if (p_end == NULL) p_end = p + strlen(p);
1305        *pp = p_end;
1306#endif  /* CFGFILE */
1307
1308        if (*p == '!' && p[1] == '!') {
1309            p += 2;
1310            flags = F_QUICKONLY;
1311        }
1312
1313        if (*p == '~')
1314            gethome(&p, p_end, &home);
1315
1316#if     CFGFILE
1317        pos -= p - ffline;
1318        if (*pp - p0 >= pos && memcmp(p, *pp - pos, pos) == 0)
1319            p = *pp - pos;
1320        else
1321            p = newstring(p, pos + 1);
1322#endif  /* CFGFILE */
1323
1324        tp = *tpp = (struct texmfrec *)
1325            xmalloc(sizeof(struct texmfrec), "texmf record");
1326        tp->home = home;
1327        tp->str = p;
1328#if     CFGFILE
1329        tp->len = pos;
1330#else
1331        tp->len = p_end - p;
1332#endif
1333        tp->flags = flags;
1334        tp->next = NULL;
1335        tpp = &tp->next;
1336}
1337
1338
1339#if     CFGFILE
1340
1341static  void
1342intexmf(env_value, env_ptr, dflt)
1343        _Xconst char            *env_value;
1344        _Xconst struct envrec   *env_ptr;
1345        _Xconst char            *dflt;
1346{
1347        _Xconst char            *path;
1348        Boolean                 did_next_default;
1349
1350        if (env_value != NULL) {
1351            path = env_value;
1352            env_value = NULL;
1353        }
1354        else if (env_ptr != NULL && env_ptr->value != NULL) {
1355            path = env_ptr->value;
1356            env_ptr = env_ptr->next;
1357            if (env_ptr != NULL && env_ptr->key != NULL)
1358                env_ptr = NULL;
1359        }
1360        else if (dflt != NULL) {
1361            path = dflt;
1362            dflt = NULL;
1363        }
1364        else return;
1365
1366        did_next_default = False;
1367
1368        for (;;) {
1369            if (*path == '\0' || *path == ':') {
1370                if (!did_next_default) {
1371                    intexmf(env_value, env_ptr, dflt);
1372                    did_next_default = True;
1373                }
1374            }
1375            else
1376                add_to_texmf_list(&path);
1377            if (*path == '\0')
1378                break;
1379            ++path;
1380        }
1381}
1382
1383#endif  /* if CFGFILE */
1384
1385
1386static  void
1387init_texmf()
1388{
1389#if     !CFGFILE
1390        _Xconst char            *texmf;
1391        _Xconst char            *texmf2;
1392#endif
1393        _Xconst struct texmfrec *tp;
1394        int                     n;
1395
1396        if (texmfhead != NULL)
1397            return;
1398
1399#if     CFGFILE
1400
1401        intexmf(getenv("TEXMF"), ffgetenv("TEXMF"), DEFAULT_TEXMF_PATH);
1402
1403#else   /* !CFGFILE */
1404
1405        texmf = getenv("TEXMF");
1406        texmf2 = default_texmf_path;
1407        if (texmf == NULL) {
1408            texmf = default_texmf_path;
1409            texmf2 = NULL;
1410        }
1411
1412        for (;;) {
1413            if (*texmf == '\0' || *texmf == ':') {
1414                if (texmf2 != NULL)
1415                    for (;;) {
1416                        while (*texmf2 == ':') ++texmf2;
1417                        if (*texmf2 == '\0') break;
1418                        add_to_texmf_list(&texmf2);
1419                    }
1420            }
1421            else
1422                add_to_texmf_list(&texmf);
1423            if (*texmf == '\0')
1424                break;
1425            ++texmf;
1426        }
1427
1428#endif
1429
1430        /* Make sure ffline[] is long enough for these. */
1431
1432        n = 0;
1433        for (tp = texmfhead; tp != NULL; tp = tp->next)
1434            if (tp->len > n)
1435                n = tp->len;
1436
1437        if (n > ffline_len)
1438            expandline(n);
1439}
1440
1441
1442/*
1443 *      Scan this path part.  This is done at most once.
1444 *      We look for // or wild cards or {}.
1445 */
1446
1447static  _Xconst char *
1448prescan(p)
1449        _Xconst char    *p;
1450{
1451        struct steprec  *sp;
1452
1453        *steppp = sp = (struct steprec *) xmalloc(sizeof(struct steprec),
1454            "main info. record in path search code.");
1455        sp->next = NULL;
1456        sp->flags = 0;
1457        sp->atom = NULL;
1458        sp->nextstep = NULL;
1459
1460        /*
1461         * Scan the string, looking for // or wild cards or braces.
1462         */
1463
1464        sp->str = p;
1465        if (*p == '/') ++p;     /* take care of an initial slash */
1466        return prescan2(sp, p);
1467}
1468
1469/*
1470 *      This routine handles the translation of the '%' modifiers.
1471 *      It returns the new number string length (pos).
1472 *      It makes sure that at least one free byte remains in ffline[].
1473 */
1474
1475static  int
1476xlat(stat_in, stat_ret, pos0, p, lastp)
1477        _Xconst struct statrec  *stat_in;
1478        struct statrec          *stat_ret;
1479        int                     pos0;
1480        _Xconst char            *p;
1481        _Xconst char            *lastp;
1482{
1483        struct statrec          status;
1484        _Xconst char            *q;
1485        int                     l;
1486
1487        status.pos = pos0;
1488        if (stat_ret != NULL)
1489            status = *stat_in;
1490
1491        while (p < lastp) {
1492            q = memchr(p, '%', lastp - p);
1493            l = (q != NULL ? q : lastp) - p;
1494            if (status.pos + l >= ffline_len)
1495                expandline(status.pos + l);
1496            bcopy(p, ffline + status.pos, l);
1497            status.pos += l;
1498            p = q;
1499            if (p == NULL) break;
1500            do {
1501                ++p;
1502                if (p >= lastp) break;
1503                q = index(lrec.fF_etc, *p);
1504                if (q != NULL) {
1505                    _Xconst char *str = fF_values[q - lrec.fF_etc];
1506
1507                    if (str != NULL) {
1508                        l = strlen(str);
1509                        if (status.pos + l >= ffline_len)
1510                            expandline(status.pos + l);
1511                        bcopy(str, ffline + status.pos, l);
1512                        status.pos += l;
1513                    }
1514                    else {      /* eliminate a possible double slash */
1515                        if (p[1] == '/' && (status.pos == 0
1516                                || ffline[status.pos - 1] == '/'))
1517                            --status.pos;
1518                    }
1519                }
1520                else if (*p == 'q' || *p == 'Q') {
1521                    status.quickchar = *p;
1522                    status.quickpos = status.pos;
1523                }
1524                else {
1525                    if (status.pos + 1 >= ffline_len)
1526                        expandline(ffline_len);
1527                    ffline[status.pos++] = *p;
1528                }
1529                ++p;
1530            }
1531            while (p < lastp && *p == '%');
1532        }
1533
1534        if (stat_ret != NULL)
1535            *stat_ret = status;
1536        return status.pos;
1537}
1538
1539
1540/*
1541 *      TRY_TO_OPEN - Try to open the file.  Exit via longjmp() if success.
1542 */
1543
1544#ifdef  PK_AND_GF
1545#define TRY_TO_OPEN(pos, flags) try_to_open(pos, flags)
1546#else
1547#define TRY_TO_OPEN(pos, flags) try_to_open(pos)
1548#endif
1549
1550static  void
1551TRY_TO_OPEN(pos, flags)
1552        int     pos;
1553#ifdef  PK_AND_GF
1554        int     flags;
1555#endif
1556{
1557#ifdef  PK_AND_GF
1558        gflags |= flags;
1559        if (pk_second_pass && !(flags & F_PK_USED))
1560            return;
1561#endif
1562        ffline[pos] = '\0';
1563
1564        if (FFDEBUG)
1565            printf("Trying %s file %s\n", lrec.type, ffline);
1566
1567        file_found = xfopen(ffline, OPEN_MODE);
1568        if (file_found != NULL)
1569            longjmp(got_it, 1);
1570}
1571
1572
1573/*
1574 *      lsrgetline - Read a line from the (ls-R) file and return:
1575 *              minus its length if it ends in a colon (including the colon);
1576 *              its length       otherwise;
1577 *              zero             for end of file.
1578 *      Blank lines are skipped.
1579 */
1580
1581static  int
1582lsrgetline(f, pos0)
1583        FILE    *f;     /* ls-R file */
1584        int     pos0;   /* relative position in line to start reading */
1585{
1586        int     pos     = pos0;
1587
1588        for (;;) {
1589            if (pos + 4 >= ffline_len)
1590                expandline(pos);
1591            if (fgets(ffline + pos, ffline_len - pos, f) == NULL)
1592                break;
1593            pos += strlen(ffline + pos);
1594            if (pos > pos0 && ffline[pos - 1] == '\n') {
1595                --pos;          /* trim newline */
1596                if (pos > pos0) break;  /* if ffline still nonempty */
1597            }
1598        }
1599        if (pos > pos0 && ffline[pos - 1] == ':') {
1600            ffline[pos - 1] = '/';      /* change trailing : to slash */
1601            return pos0 - pos;
1602        }
1603        else
1604            return pos - pos0;
1605}
1606
1607
1608/*
1609 *      lsrput - Put a key/value pair into the ls-R database.
1610 */
1611
1612static  void
1613lsrput(key, keylen, value)
1614        _Xconst char    *key;
1615        int             keylen;
1616        _Xconst char    *value;
1617{
1618        struct lsr      **lpp;
1619        struct lsr      *lp;
1620
1621#if 0
1622        fputs("lsrput:  key = `", stdout);
1623        fwrite(key, 1, keylen, stdout);
1624        printf("'; value = `%s'.\n", value);
1625#endif
1626
1627            /* make the new record */
1628        lp = (struct lsr *) xmalloc(sizeof(*lp), "ls-R record");
1629        lp->next = NULL;
1630        lp->frecp = frecp;
1631        lp->seqno = seqno;
1632        lp->keylen = keylen;
1633        lp->key = newstring(key, keylen);
1634        lp->value = value;
1635
1636            /* insert it */
1637        lpp = lsrtab + prehash(key, keylen) % XtNumber(lsrtab);
1638        while (*lpp != NULL)    /* skip to end of chain (performance reasons) */
1639            lpp = &(*lpp)->next;
1640        *lpp = lp;
1641}
1642
1643
1644/*
1645 *      init_quick_find - Read ls-R database for this particular search.
1646 */
1647
1648static  void
1649init_quick_find(root, pos, atom, pos1)
1650        struct rootrec          *root;  /* pointer to the governing rootrec */
1651        int                     pos;    /* position of %[qQ] in ffline[] */
1652        _Xconst struct atomrec  *atom;  /* the beginning of the atomlist */
1653        int                     pos1;   /* next free byte in ffline[] */
1654{
1655#define LS_R_LEN        5               /* length of the string "ls-R\0" */
1656        char            tmpsav[LS_R_LEN];
1657        int             keypos;         /* position of start of key pattern */
1658        int             keyend;         /* last + 1 character of the key */
1659        int             keypos_a;       /* position + 1 of last slash */
1660        int             pos2;           /* end + 1 of the decoded thing */
1661        int             nslashes;       /* how many slashes in bbbcccddd */
1662        FILE            *f;             /* ls-R file */
1663        _Xconst char    *fullpat;       /* fields in *root */
1664        _Xconst char    *keypat;        /* " */
1665        _Xconst char    *keypat_end;    /* " */
1666        _Xconst char    *fullpat_end;   /* " */
1667        _Xconst char    *keypat_a;      /* position of end+1 of restricted
1668                                         * initial fixed part */
1669        int             retval;         /* return value of lsrgetline */
1670
1671        /*
1672         *      Reencode the string of atoms into a character string
1673         *      When we're done, ffline[] contains:
1674         *      /aa/aa/a...a/ bbb/bb...bb/bbb %f/cc/c...c/%d ddd/ddd/.../ddd
1675         *
1676         *      where the aaa part is what's already in ffline[], and
1677         *      the rest is the decoded atom chain.  The ccc part of this
1678         *      string contains all the variable specifiers (not replaced by
1679         *      anything), and the bbb and ddd parts are character strings that
1680         *      should occur verbatim in the file name.
1681         */
1682
1683        root->flags &= ~F_QUICKFIND;    /* clear it for now; set it later */
1684
1685        if (atom->flags & F_PCT_S)
1686            atom = treeatom;
1687
1688        if (atom->flags & F_WILD) return;       /* not allowed */
1689
1690        pos2 = pos1;
1691        keypos = -1;
1692        nslashes = 0;
1693
1694        for (;;) {
1695            _Xconst char *p;
1696
1697            keypos_a = pos2;
1698                /* translate the atom, leaving %f and maybe %d alone */
1699            for (p = atom->p; p < atom->p_end; ++p) {
1700                if (p >= atom->p_end) break;
1701                if (*p == '%') {
1702                    _Xconst char *q;
1703
1704                    ++p;
1705                    if (p >= atom->p_end) break;
1706                    q = index(lrec.fF_etc, *p);
1707                    if (q != NULL) {
1708                        if (q - lrec.fF_etc < lrec.n_var_opts) {
1709                            /* it's a variable specifier (%f or ...) */
1710                            --p;        /* copy it over */
1711                            if (keypos < 0) keypos = pos2;
1712                            keyend = pos2 + 2;
1713                        }
1714                        else {
1715                            _Xconst char *str   = fF_values[q - lrec.fF_etc];
1716
1717                            if (str != NULL) {
1718                                int     l       = strlen(str);
1719
1720                                if (pos2 + l >= ffline_len)
1721                                    expandline(pos2 + l);
1722                                bcopy(str, ffline + pos2, l);
1723                                pos2 += l;
1724                            }
1725                            continue;
1726                        }
1727                    }
1728                }
1729                if (pos2 + 1 >= ffline_len)
1730                    expandline(pos2);
1731                ffline[pos2++] = *p;
1732            }
1733
1734            atom = atom->next;
1735            if (atom == NULL) break;
1736
1737            if (atom->flags & F_PCT_S) atom = treeatom;
1738            if (atom->flags & (F_SLASH_SLASH | F_WILD))
1739                return;         /* not supported */
1740
1741            if (pos2 == keypos_a) continue;     /* if /%m/ with no mode */
1742            ffline[pos2++] = '/';
1743            ++nslashes;
1744        }
1745
1746        if (keypos_a > keypos) keypos_a = keypos;
1747
1748        root->flags |= F_QUICKFIND;     /* it's eligible */
1749
1750        if (FFDEBUG) {
1751#define DDD(a,b,c)      \
1752                fputs(a, stdout); \
1753                fwrite(ffline + (b), 1, (c) - (b), stdout); \
1754                puts("'");
1755
1756            DDD("\nReading ls-R.\nOriginal string = `", 0, pos1);
1757            DDD("Restricted initial fixed part = `", pos1, keypos_a);
1758            DDD("Initial fixed part = `", pos1, keypos);
1759            DDD("Key pattern = `", keypos, keyend);
1760            DDD("Final fixed part = `", keyend, pos2);
1761#undef  DDD
1762        }
1763
1764        /*
1765         * Open ls-R and start reading it.
1766         */
1767
1768        bcopy(ffline + pos, tmpsav, sizeof(tmpsav));
1769        bcopy("ls-R", ffline + pos, sizeof(tmpsav));
1770        if (FFDEBUG) printf("Opening ls-R file %s\n", ffline);
1771        f = xfopen(ffline, OPEN_MODE);
1772        bcopy(tmpsav, ffline + pos, sizeof(tmpsav));
1773        if (f == NULL) {
1774            if (FFDEBUG) perror("xfopen");
1775            return;
1776        }
1777
1778        fullpat = newstring(ffline + pos1, pos2 - pos1);
1779        keypat = fullpat + (keypos - pos1);
1780        keypat_a = fullpat + (keypos_a - pos1);
1781        keypat_end = fullpat + (keyend - pos1);
1782        fullpat_end = fullpat + (pos2 - pos1);
1783
1784        do {                    /* skip initial comment lines */
1785            retval = lsrgetline(f, pos1);
1786            if (retval == 0) {
1787                fclose(f);
1788                return;         /* premature end of file */
1789            }
1790        } while (ffline[pos1] == '%');
1791
1792        /* First take care of file names without a directory: these are
1793         * those in the same directory as the ls-R file */
1794
1795        while (retval > 0) {
1796            if (nslashes == 0   /* if there are no slashes in the pattern */
1797                                /* and if ls-R is in the same directory
1798                                 * as the //: */
1799                    && pos1 == pos
1800                                /* and if the initial fixed part matches: */
1801                    && memcmp(ffline + pos1, fullpat, keypat - fullpat) == 0
1802                                /* and if the final fixed part matches: */
1803                    && memcmp(ffline + pos1 + retval
1804                                - (fullpat_end - keypat_end),
1805                                keypat_end, fullpat_end - keypat_end) == 0)
1806                                /* then create a directory entry */
1807                lsrput(ffline + pos1 + (keypat - fullpat),
1808                        retval - (keypat - fullpat)
1809                        - (fullpat_end - keypat_end), "");
1810            retval = lsrgetline(f, pos1);       /* read next line */
1811        }
1812
1813        /*
1814         * Now do whole directories.
1815         */
1816
1817        for (;;) {
1818            int         i;
1819            _Xconst char *p;
1820            _Xconst char *p_begin;
1821            _Xconst char *p_end;
1822            int         pos3;
1823            int         pos4;
1824            int         pos5;
1825            int         retpos;
1826            char        *value;
1827
1828            while (retval > 0)          /* skip to next directory */
1829                retval = lsrgetline(f, pos1);
1830            if (retval == 0) break;     /* if end of file */
1831            retval = -retval;           /* length of the string */
1832            /* also, retval is now > 0, so a continue statement won't lead to
1833             * an infinite loop. */
1834            retpos = pos1 + retval;
1835            if (ffline[pos1] == '/') {  /* if absolute path */
1836                        /* these should be referring to the same directory */
1837                if (retval < pos1 || memcmp(ffline + pos1, ffline, pos1) != 0)
1838                    continue;
1839                pos2 = pos1 + pos1;
1840            }
1841            else {              /* relative path */
1842                pos2 = pos1;
1843                if (retval >= 2 && ffline[pos1] == '.'
1844                  && ffline[pos1 + 1] == '/')
1845                    pos2 += 2;  /* eliminate "./" */
1846                        /* these should be referring to the same directory */
1847                if (retpos - pos2 < pos1 - pos
1848                        || memcmp(ffline + pos2, ffline + pos, pos1 - pos) != 0)
1849                    continue;
1850                pos2 += pos1 - pos;
1851            }
1852            /* now pos2 points to what should follow the // */
1853            /* count backwards from the end of the string to just after the
1854               (nslashes + 1)-st slash */
1855            p_begin = ffline + pos2;
1856            p_end = ffline + retpos;
1857            i = nslashes;
1858            for (p = p_end; p > p_begin; )
1859                if (*--p == '/') {
1860                    if (i == 0) {
1861                        ++p;
1862                        break;
1863                    }
1864                    else --i;
1865                }
1866            if (i > 0) continue;        /* if too few slashes */
1867            pos3 = p - ffline;
1868            /* the next part of the directory name should match the initial
1869               fixed part of the pattern */
1870            if (p_end - p < keypat_a - fullpat
1871              || memcmp(p, fullpat, keypat_a - fullpat) != 0)
1872                continue;
1873            pos4 = pos3 + (keypat - fullpat);   /* start of key */
1874            value = NULL;
1875            for (;;) {          /* read the files in this directory */
1876                retval = lsrgetline(f, retpos);
1877                if (retval <= 0) {      /* if done with list*/
1878                    bcopy(ffline + retpos, ffline + pos1, -retval);
1879                    break;
1880                }
1881                    /* check the remaining part of the initial fixed part */
1882                    /* (the first test is only for performance) */
1883                if (keypat_a != keypat
1884                  && memcmp(ffline + retpos, keypat_a, keypat - keypat_a) != 0)
1885                    continue;
1886                    /* end of key */
1887                pos5 = retpos + retval - (fullpat_end - keypat_end);
1888                    /* check the final fixed part */
1889                if (pos5 < pos4 || (fullpat_end != keypat_end
1890                        && memcmp(ffline + pos5, keypat_end,
1891                        fullpat_end - keypat_end) != 0))
1892                    continue;
1893                if (value == NULL) {
1894                    value = newstring(ffline + pos2, pos3 - pos2 + 1);
1895                    value[pos3 - pos2] = '\0';
1896                }
1897                lsrput(ffline + pos4, pos5 - pos4, value);
1898            }
1899        }
1900
1901        fclose(f);      /* close the file */
1902
1903            /* Fill in the requisite entries in *root:  we have ls-R. */
1904        root->fullpat = fullpat;
1905        root->fullpat_end = fullpat_end;
1906        root->keypat = keypat;
1907        root->keypat_end = keypat_end;
1908}
1909
1910/*
1911 *      wildmatch - Tell whether the string matches the pattern.
1912 *
1913 *              pat             pointer to first character of pattern
1914 *              pat_end         pointer to last + 1 character of pattern
1915 *              candidate       the string
1916 *              allowvar        how many characters of fF_etc should be treated
1917 *                              as *
1918 */
1919
1920static  Boolean
1921wildmatch(pat, pat_end, candidate, allowvar)
1922        _Xconst char    *pat;
1923        _Xconst char    *pat_end;
1924        _Xconst char    *candidate;
1925        int             allowvar;       /* How many of %f, %F, %d, ... to */
1926                                        /* treat as '*' */
1927{
1928        _Xconst char    *p;
1929        _Xconst char    *q      = candidate;
1930
1931        for (p = pat; p < pat_end; ++p)
1932            switch (*p) {
1933
1934                case '%': {
1935                    _Xconst char *q1;
1936
1937                    if (p + 1 >= pat_end) continue;
1938                    ++p;
1939                    q1 = index(lrec.fF_etc, *p);
1940                    if (q1 == NULL) {           /* %[ or something */
1941                        if (*q != *p)
1942                            return False;
1943                        ++q;
1944                        break;
1945                    }
1946
1947                    if (q1 - lrec.fF_etc < allowvar) {  /* treat it as '*' */
1948                        for (q1 = q + strlen(q); q1 >= q; --q1)
1949                            if (wildmatch(p + 1, pat_end, q1, allowvar))
1950                                return True;
1951                        return False;
1952                    }
1953                    else {
1954                        _Xconst char    *str    = fF_values[q1 - lrec.fF_etc];
1955                        int             l;
1956
1957                        if (str == NULL) str = "";
1958                        l = strlen(str);
1959                        if (bcmp(q, str, l) != 0)
1960                            return False;
1961                        q += l;
1962                    }
1963                    break;
1964                }
1965
1966                case '*': {
1967                    _Xconst char *q1;
1968
1969                    for (q1 = q + strlen(q); q1 >= q; --q1)
1970                        if (wildmatch(p + 1, pat_end, q1, allowvar))
1971                            return True;
1972                    return False;
1973                }
1974
1975                case '?':
1976                    if (*q == '\0')
1977                        return False;
1978                    ++q;
1979                    break;
1980
1981                case '[': {
1982                    char c;
1983                    Boolean reverse = False;
1984
1985                    c = *q++;
1986                    if (c == '\0')
1987                        return False;
1988
1989                    ++p;
1990                    if (*p == '^') {
1991                        reverse = True;
1992                        ++p;
1993                    }
1994
1995                    for (;;) {
1996                        char    c1;
1997                        char    c2;
1998
1999                        if (p >= pat_end)
2000                            return False;       /* syntax error */
2001                        c1 = *p;
2002                        if (c1 == ']')
2003                            if (reverse)
2004                                break;          /* success */
2005                            else
2006                                return False;
2007                        c2 = c1;
2008                        ++p;
2009                        if (*p == '-' && p[1] != ']') {
2010                            ++p;
2011                            c2 = *p++;
2012                            if (c2 == '%') c2 = *p++;
2013                        }
2014                        if (p >= pat_end)
2015                            return False;       /* syntax error */
2016                        if (c >= c1 && c <= c2)
2017                            if (reverse)
2018                                return False;
2019                            else {              /* success */
2020                                while (*p != ']') {
2021                                    if (*p == '%') ++p;
2022                                    ++p;
2023                                    if (p >= pat_end)
2024                                        return False;   /* syntax error */
2025                                }
2026                                break;
2027                            }
2028                    }
2029                    break;
2030                }
2031
2032                default:
2033                    if (*q != *p)
2034                        return False;
2035                    ++q;
2036            }
2037
2038        if (*q != '\0')
2039            return False;
2040        return True;
2041}
2042
2043
2044/*
2045 *      Read in the given directory.
2046 */
2047
2048static  void
2049filltree(pos, treepp, atom, slashslash)
2050        int             pos;
2051        struct treerec  **treepp;
2052        struct atomrec  *atom;
2053        Boolean         slashslash;
2054{
2055        DIR             *f;
2056        struct dirent   *dp;
2057
2058        if (pos == 0) {
2059            if (ffline_len == 0)
2060                expandline(2);
2061            ffline[0] = '.';
2062            ffline[1] = '\0';
2063        }
2064        else
2065            ffline[pos - 1] = '\0';
2066
2067        if (FFDEBUG)
2068            printf("Opening directory %s\n", ffline);
2069
2070        f = xopendir(ffline);
2071        if (f == NULL) {
2072            if (FFDEBUG) perror(ffline);
2073            *treepp = NULL;
2074            return;
2075        }
2076
2077        if (pos != 0) ffline[pos - 1] = '/';
2078        for (;;) {
2079            char                *line1;
2080            struct stat         statbuf;
2081            struct treerec      *leaf;
2082            Boolean             isdir;
2083#if     ST_NLINK_TRICK
2084            struct treerec      *child;
2085#endif
2086
2087            dp = readdir(f);
2088            if (dp == NULL) break;      /* done */
2089
2090            if (pos + dp->d_reclen >= ffline_len)
2091                expandline(pos + dp->d_reclen);
2092            line1 = ffline + pos;
2093            bcopy(dp->d_name, line1, dp->d_reclen);
2094            line1[dp->d_reclen] = '\0';
2095            if (*line1 == '.' && (line1[1] == '\0'
2096                    || (line1[1] == '.' && line1[2] == '\0')))
2097                continue;       /* skip . and .. */
2098
2099            if (stat(ffline, &statbuf) != 0) {
2100                fputs("xdvi/filltree/stat: ", stderr);
2101                perror(ffline);
2102                continue;
2103            }
2104            isdir = False;
2105#if     ST_NLINK_TRICK
2106            child = &not_read_yet;
2107#endif
2108            if (S_ISDIR(statbuf.st_mode)) {
2109                isdir = True;
2110                if (!slashslash
2111                        && (atom->next == NULL
2112                        || !wildmatch(atom->p, atom->p_end, line1,
2113                                lrec.n_var_opts)))
2114                    continue;
2115#if     ST_NLINK_TRICK
2116                if (statbuf.st_nlink <= 2)
2117                    child = NULL;
2118#endif
2119            }
2120            else if (S_ISREG(statbuf.st_mode)) {
2121                if (atom->next != NULL
2122                        || (slashslash && !(atom->flags & F_WILD))
2123                        || !wildmatch(atom->p, atom->p_end, line1,
2124                                lrec.n_var_opts))
2125                    continue;
2126            }
2127            else continue;      /* something else */
2128
2129            leaf = (struct treerec *) xmalloc(sizeof(struct treerec),
2130                    "leaf for subdir searching");
2131            {
2132                char    *str;
2133
2134                str = newstring(dp->d_name, dp->d_reclen + 1);
2135                str[dp->d_reclen] = '\0';
2136                leaf->dirname = str;
2137            }
2138#if     ST_NLINK_TRICK
2139            leaf->child = child;
2140#else
2141            leaf->child = &not_read_yet;
2142#endif
2143            leaf->isdir = isdir;
2144            *treepp = leaf;
2145            treepp = &leaf->next;
2146        }
2147        closedir(f);
2148
2149        *treepp = NULL;
2150}
2151
2152
2153/*
2154 *      do_tree_search() - recursive routine for // and wild card searches.
2155 */
2156
2157static  void
2158do_tree_search(treepp, atom, goback, pos, flags)
2159        struct treerec          **treepp;
2160        _Xconst struct atomrec  *atom;
2161        _Xconst struct atomrec  *goback;
2162        int                     pos;
2163        int                     flags;
2164{
2165        int             aflags;
2166        struct treerec  *tp;
2167
2168        /*
2169         * If we're at the end of the chain of atoms, try to open the file.
2170         */
2171
2172        if (atom == NULL) {
2173            TRY_TO_OPEN(pos - 1, flags);
2174            return;
2175        }
2176
2177        /*
2178         * Otherwise, we're still forming the path.
2179         */
2180
2181        aflags = atom->flags;
2182        if (aflags & F_PCT_S) {         /* if it's a dummy record */
2183            atom = treeatom;
2184            aflags |= atom->flags;
2185        }
2186
2187        if (aflags & F_SLASH_SLASH)
2188            goback = atom;
2189
2190        if (goback == NULL) {           /* wild card search, no // yet */
2191            /*
2192             * If the next atom is neither wild nor variable, then we don't
2193             * need to do a readdir() on the directory.  Just add the
2194             * appropriate characters to the path and recurse.
2195             */
2196            if (!(atom->flags & (F_WILD | F_VARIABLE))) {
2197                pos = xlat(NULL, NULL, pos, atom->p, atom->p_end);
2198                ffline[pos++] = '/';
2199                do_tree_search(treepp, atom->next, goback, pos,
2200                    flags | atom->flags);
2201                return;
2202            }
2203            /*
2204             * Otherwise, go down one level in the tree.
2205             */
2206            if (*treepp == &not_read_yet)       /* if readdir() not done yet */
2207                filltree(pos, treepp, atom, False);     /* do it */
2208
2209            for (tp = *treepp; tp != NULL; tp = tp->next)
2210                if (wildmatch(atom->p, atom->p_end, tp->dirname, 0)) {
2211                    int len = strlen(tp->dirname);
2212                    bcopy(tp->dirname, ffline + pos, len);
2213                    ffline[pos + len++] = '/';
2214                    do_tree_search(&tp->child, atom->next, goback, pos + len,
2215                        flags | atom->flags);
2216                }
2217
2218            return;
2219        }
2220
2221        /*
2222         * If we got here, then we're past a //
2223         */
2224
2225        if (atom->next == NULL && !(atom->flags & F_WILD))
2226                /* if we can try a file */
2227            TRY_TO_OPEN(xlat(NULL, NULL, pos, atom->p, atom->p_end),
2228                flags | atom->flags);
2229
2230        if (*treepp == &not_read_yet)           /* if readdir() not done yet */
2231            filltree(pos, treepp, atom, True);  /* do it */
2232
2233        for (tp = *treepp; tp != NULL; tp = tp->next) {
2234            tp->tried_already = False;
2235            if ((atom->next != NULL || (atom->flags & F_WILD))
2236                    && wildmatch(atom->p, atom->p_end, tp->dirname, 0)) {
2237                int len = strlen(tp->dirname);
2238                bcopy(tp->dirname, ffline + pos, len);
2239                ffline[pos + len++] = '/';
2240                do_tree_search(&tp->child, atom->next, goback, pos + len,
2241                    flags | atom->flags);
2242                tp->tried_already = True;
2243            }
2244        }
2245
2246        for (tp = *treepp; tp != NULL; tp = tp->next)
2247            if (!tp->tried_already && tp->isdir) {
2248                int len = strlen(tp->dirname);
2249                bcopy(tp->dirname, ffline + pos, len);
2250                ffline[pos + len++] = '/';
2251                do_tree_search(&tp->child, goback, goback, pos + len, flags);
2252            }
2253}
2254
2255
2256/*
2257 *      begin_tree_search() - start a wild card or // search.
2258 */
2259
2260static  void
2261begin_tree_search(status, oneatom, twoatom)
2262        _Xconst struct statrec  *status;
2263        _Xconst struct atomrec  *oneatom;       /* initial atom */
2264        _Xconst struct atomrec  *twoatom;       /* if F_PCT_S set */
2265{
2266        struct treerec          **tpp;
2267
2268#ifdef  PK_AND_GF
2269        {
2270            int flags = status->flags;
2271
2272            if (twoatom != NULL)
2273                flags |= twoatom->flags;
2274            if (pk_second_pass && !(flags & F_PK_USED))
2275                return;
2276        }
2277#endif
2278
2279#if 0
2280        {
2281            _Xconst struct atomrec      *atom;
2282
2283            puts("begin_tree_search():");
2284            fwrite(ffline, 1, status->pos, stdout);
2285            putchar('\n');
2286            atom = oneatom;
2287            for (;;) {
2288                fputs("+ ", stdout);
2289                if (atom->flags & F_SLASH_SLASH) fputs("(//) ", stdout);
2290                if (atom->flags & F_PCT_S) atom = twoatom;
2291                fwrite(atom->p, 1, atom->p_end - atom->p, stdout);
2292                if (atom->flags & F_WILD) fputs(" (wild)", stdout);
2293                putchar('\n');
2294                atom = atom->next;
2295                if (atom == NULL) break;
2296            }
2297            putchar('\n');
2298        }
2299#endif
2300
2301        ++seqno;
2302        treeatom = twoatom;             /* save this globally */
2303
2304        /*
2305         * Find the record controlling this mess.  Create one, if necessary.
2306         */
2307
2308        if (status->flags & F_VARIABLE) {
2309            struct vrootrec {
2310                struct rootrec  *next;  /* link to next in hash chain */
2311                struct treerec  *tree;  /* the tree */
2312                _Xconst char    *path;
2313                int             seqno;
2314            };
2315            struct rtab {
2316                struct rtab     *next;
2317                int             seqno;
2318                int             len;
2319                _Xconst char    *tag;
2320                struct vrootrec *r;
2321            };
2322            static struct rtab  *varroot[32];
2323            struct rtab         **tpp2;
2324            struct rtab         *t;
2325            struct vrootrec     *r;
2326
2327            tpp2 = varroot + prehash(ffline, status->pos) % XtNumber(varroot);
2328            for (;;) {
2329                t = *tpp2;
2330                if (t == NULL) {
2331                    t = (struct rtab *) xmalloc(sizeof(*t), "root record");
2332                    t->seqno = seqno;
2333                    t->len = status->pos;
2334                    t->tag = newstring(ffline, status->pos);
2335                    r = (struct vrootrec *) xmalloc(sizeof(*r), "root record");
2336                    r->next = NULL;
2337                    r->tree = &not_read_yet;    /* readdir() not done yet */
2338                    t->r = r;
2339                    t->next = NULL;
2340                    *tpp2 = t;
2341                    break;
2342                }
2343                if (t->seqno == seqno && t->len == status->pos
2344                        && memcmp(t->tag, ffline, t->len) == 0) {
2345                    r = t->r;
2346                    break;
2347                }
2348                tpp2 = &t->next;
2349            }
2350            tpp = &r->tree;
2351        }
2352        else {
2353            struct rootrec *r;
2354
2355            r = *rootpp;
2356            if (r == NULL) {
2357                r = (struct rootrec *) xmalloc(sizeof(*r), "root record");
2358                r->next = NULL;
2359                r->tree = &not_read_yet;        /* readdir() not done yet */
2360                r->fullpat = NULL;              /* no ls-R yet */
2361                *rootpp = r;
2362                r->flags = status->flags |
2363                    ((oneatom->flags & F_PCT_S) ? twoatom : oneatom) ->flags;
2364                if (r->flags & F_QUICKFIND)
2365                    init_quick_find(r, status->pos, oneatom, status->pos);
2366                else if (status->quickchar != '\0') {
2367                    r->flags |= (status->quickchar == 'Q' ? F_QUICKONLY : 0);
2368                    /* (init_quick_find() will set F_QUICKFIND in r->flags) */
2369                    init_quick_find(r, status->quickpos, oneatom, status->pos);
2370                }
2371            }
2372            rootpp = &r->next;
2373            tpp = &r->tree;
2374                /* do ls-R search, if appropriate */
2375            if ((r->flags & F_QUICKFIND) && r->fullpat != NULL) {
2376                int             pos;
2377                struct lsr      *lp;
2378
2379                pos = xlat(NULL, NULL, status->pos, r->keypat, r->keypat_end);
2380                lp = lsrtab[prehash(ffline + status->pos, pos - status->pos)
2381                    % XtNumber(lsrtab)];
2382                for (;;) {
2383                    if (lp == NULL) break;      /* no match */
2384                    if (lp->frecp == frecp && lp->seqno == seqno
2385                      && lp->keylen == pos - status->pos
2386                      && memcmp(lp->key, ffline + status->pos, lp->keylen) == 0)
2387                      {         /* if match */
2388                        struct statrec  stat1;
2389                        int             len = strlen(lp->value);
2390
2391                        stat1 = *status;
2392                        if (stat1.pos + len > ffline_len)
2393                            expandline(stat1.pos + len);
2394                        bcopy(lp->value, ffline + stat1.pos, len);
2395                        stat1.pos += len;
2396                        xlat(&stat1, &stat1, 0, r->fullpat, r->fullpat_end);
2397                        TRY_TO_OPEN(stat1.pos, stat1.flags);
2398                        return;
2399                    }
2400                    lp = lp->next;
2401                }
2402                    /* if there's an ls-R database, and our file is not there,
2403                     * then we don't look recursively this time. */
2404                return;
2405            }
2406            if ((r->flags & (F_QUICKFIND | F_QUICKONLY))
2407                    == (F_QUICKFIND | F_QUICKONLY)) {
2408#ifdef  PK_AND_GF
2409                gflags |= status->flags;
2410#endif
2411                return;
2412            }
2413        }
2414
2415        do_tree_search(tpp, oneatom, NULL, status->pos, status->flags);
2416}
2417
2418
2419/*
2420 *      dostep() - Process a steprec record.
2421 */
2422
2423static  void
2424dostep(sp, stat0)
2425        _Xconst struct steprec  *sp;
2426        _Xconst struct statrec  *stat0;
2427{
2428        struct statrec          status;
2429
2430        xlat(stat0, &status, 0, sp->str, sp->strend);
2431
2432        status.flags |= sp->flags;
2433
2434        if (sp->atom != NULL) {         /* if wild cards or // */
2435            if (sp->flags & F_PCT_S) {
2436                int     i;
2437
2438                for (i = 0; i < lrec.v.pct_s_count; ++i)
2439                    begin_tree_search(&status, sp->atom, lrec.v.pct_s_atom[i]);
2440            }
2441            else
2442                begin_tree_search(&status, sp->atom, NULL);
2443        }
2444        else if (sp->nextstep != NULL) {        /* if {} list */
2445            struct steprec *sp1;
2446
2447            for (sp1 = sp->nextstep; sp1 != NULL; sp1 = sp1->next)
2448                dostep(sp1, &status);
2449        }
2450        else {  /* end of string */
2451            if (!(status.flags & F_FILE_USED)) {
2452                xlat(&status, &status, 0, lrec.no_f_str, lrec.no_f_str_end);
2453#ifdef  PK_AND_GF
2454                status.flags |= lrec.no_f_str_flags;
2455#endif
2456            }
2457            TRY_TO_OPEN(status.pos, status.flags);
2458        }
2459}
2460
2461
2462/*
2463 *      fixbegin() - Handle !!, %t, %S, and ~ at the beginning of a specifier.
2464 */
2465
2466static  void
2467fixbegin(sp)
2468        struct steprec  *sp;
2469{
2470        _Xconst char    *p      = sp->str;
2471
2472        /*
2473         * Initial !!.
2474         */
2475
2476        if (*p == '!' && p[1] == '!' && sp->strend >= p + 2) {
2477            sp->flags |= F_QUICKONLY;
2478            p += 2;
2479        }
2480
2481        /*
2482         * Take care of %S, %t, and ~.
2483         */
2484
2485        sp->home = NULL;
2486
2487        if (*p == '%') {
2488            if (p[1] == 'S' && sp->strend == p + 2 && sp->atom == NULL
2489              && sp->nextstep == NULL) {        /* %S */
2490                init_texmf();
2491                sp->flags |= F_PCT_T;
2492                p = "/";
2493                sp->strend = p + 1;
2494                sp->nextstep = scan_pct_s();
2495            }
2496            if (p[1] == 't' && sp->strend >= p + 2) {   /* %t */
2497                init_texmf();
2498                sp->flags |= F_PCT_T;
2499                p += 2;
2500            }
2501        }
2502        else if (*p == '~')
2503            gethome(&p, sp->strend, &sp->home);
2504
2505        sp->str = p;
2506}
2507
2508
2509/*
2510 *      pathpart() - Handle one (colon-separated) part of the search path.
2511 */
2512
2513static  _Xconst char *
2514pathpart(p)
2515        _Xconst char    *p;
2516{
2517        struct steprec                  *sp;
2518        static  _Xconst struct statrec  stat_ini        = {0, 0, '\0', 0};
2519        struct statrec                  status;
2520
2521        sp = *steppp;
2522        if (sp == NULL) {       /* do prescanning (lazy evaluation) */
2523            _Xconst char *p_end;
2524#if     CFGFILE
2525            _Xconst char *p1;
2526            int         pos;
2527#endif
2528
2529#if     CFGFILE
2530            p_end = p;
2531            pos = form_path_part(&p_end, '%');
2532            ffline[pos] = '\0';
2533            if (pos <= p_end - p && memcmp(ffline, p_end - pos, pos) == 0)
2534                p1 = p_end - pos;
2535            else
2536                p1 = newstring(ffline, pos + 1);
2537            (void) prescan(p1);
2538#else   /* !CFGFILE */
2539            p_end = prescan(p);
2540                /* skip to next colon or end of string */
2541            for (; *p_end != '\0' && *p_end != ':'; ++p_end)
2542                if (*p_end == '%' && p_end[1] != '\0')
2543                    ++p_end;
2544#endif  /* CFGFILE */
2545
2546            sp = *steppp;
2547            if (sp->str == sp->strend && sp->nextstep != NULL) {
2548                /* If it's braces right off the bat, then pretend these are */
2549                /* all different specifiers (so %t, %S, !!, and ~ can work). */
2550                /* There's a memory leak here, but it's bounded. */
2551                struct steprec  *sp1    = sp->nextstep;
2552
2553                for (;;) {
2554                    fixbegin(sp1);
2555                    if (sp1->next == NULL) {
2556                        sp1->nextpart = p_end;
2557                        break;
2558                    }
2559                    sp1->nextpart = p;
2560                    sp1 = sp1->next;
2561                }
2562                *steppp = sp = sp->nextstep;    /* relink the chain */
2563            }
2564            else {
2565                fixbegin(sp);
2566                sp->nextpart = p_end;
2567            }
2568        }
2569        steppp = &sp->next;
2570
2571        if (sp->flags & F_PCT_T) {
2572            struct texmfrec     *tp;
2573
2574            for (tp = texmfhead; tp != NULL; tp = tp->next) {
2575                status = stat_ini;
2576                status.flags = tp->flags;
2577                if (tp->home != NULL) {
2578                    status.pos = strlen(tp->home);
2579                    if (status.pos + tp->len >= ffline_len)
2580                        expandline(status.pos + tp->len);
2581                    bcopy(tp->home, ffline, status.pos);
2582                }
2583                bcopy(tp->str, ffline + status.pos, tp->len);
2584                status.pos += tp->len;
2585#ifndef PK_AND_GF
2586                dostep(sp, &status);
2587#else
2588                if (lrec.pk_gf_addr != NULL) *lrec.pk_gf_addr = "pk";
2589                pk_second_pass = False;
2590                gflags = 0;
2591                dostep(sp, &status);
2592                if (gflags & F_PK_USED) {
2593                    *lrec.pk_gf_addr = "gf";
2594                    pk_second_pass = True;
2595                    dostep(sp, &status);
2596                }
2597#endif
2598            }
2599        }
2600        else {
2601            status = stat_ini;
2602            if (sp->home != NULL) {
2603                int     len = strlen(sp->home);
2604
2605                if (len >= ffline_len)
2606                    expandline(len);
2607                bcopy(sp->home, ffline, len);
2608                status.pos = len;
2609            }
2610#ifndef PK_AND_GF
2611            dostep(sp, &status);
2612#else
2613            if (lrec.pk_gf_addr != NULL) *lrec.pk_gf_addr = "pk";
2614            pk_second_pass = False;
2615            gflags = 0;
2616            dostep(sp, &status);
2617            if (gflags & F_PK_USED) {
2618                *lrec.pk_gf_addr = "gf";
2619                pk_second_pass = True;
2620                dostep(sp, &status);
2621            }
2622#endif
2623        }
2624
2625        return sp->nextpart;
2626}
2627
2628
2629#if     CFGFILE
2630
2631/*
2632 *      Recursive routine for searching in config file path variables.
2633 */
2634
2635static  void
2636ffrecurse(env_ptr, dflt)
2637        _Xconst struct envrec   *env_ptr;
2638        _Xconst char            *dflt;
2639{
2640        _Xconst char            *p;
2641        Boolean                 did_next_default;
2642
2643        if (env_ptr != NULL) {
2644            p = env_ptr->value;
2645            env_ptr = env_ptr->next;
2646            if (env_ptr != NULL && env_ptr->key != NULL)
2647                env_ptr = NULL;
2648        }
2649        else if (dflt != NULL) {
2650            p = dflt;
2651            dflt = NULL;
2652        }
2653        else return;
2654
2655        did_next_default = False;
2656        for (;;) {
2657            if (*p == '\0' || *p == ':') {
2658                if (!did_next_default) {
2659                    ffrecurse(env_ptr, dflt);
2660                    did_next_default = True;
2661                }
2662            }
2663            else
2664                p = pathpart(p);
2665            if (*p == '\0')
2666                break;
2667            ++p;
2668        }
2669}
2670
2671#endif  /* CFGFILE */
2672
2673
2674/*
2675 *      This is the main search routine.  It calls pathpath() for each
2676 *      component of the path.  Substitution for the default path is done here.
2677 */
2678
2679FILE *
2680filefind(name, srchtype, path_ret)
2681        _Xconst char    *name;          /* name of the font or file */
2682        struct findrec  *srchtype;      /* what type of search to perform */
2683        _Xconst char    **path_ret;     /* put the name of the file here */
2684{
2685        lrec = *srchtype;
2686        fF_values[0] = fF_values[1] = name;
2687
2688        if (setjmp(got_it) == 0) {
2689            if (*name == '/' || *name == '~') {         /* if absolute path */
2690                int                     pos;
2691                int                     pos0    = 0;
2692                _Xconst struct passwd   *pw     = NULL;
2693
2694                if (*name == '~')
2695                    pw = ff_getpw(&name, name + strlen(name));
2696                if (pw != NULL) {
2697                    pos0 = strlen(pw->pw_dir);
2698                    if (pos0 >= ffline_len)
2699                        expandline(pos0);
2700                    bcopy(pw->pw_dir, ffline, pos0);
2701                    fF_values[0] = fF_values[1] = name;
2702                }
2703#ifndef PK_AND_GF
2704                pos = xlat(NULL, NULL, pos0, lrec.abs_str,
2705                        lrec.abs_str + strlen(lrec.abs_str));
2706                TRY_TO_OPEN(pos, 0);
2707#else
2708                if (lrec.pk_gf_addr != NULL) *lrec.pk_gf_addr = "pk";
2709                pk_second_pass = False;
2710                pos = xlat(NULL, NULL, pos0, lrec.abs_str,
2711                        lrec.abs_str + strlen(lrec.abs_str));
2712                TRY_TO_OPEN(pos, lrec.abs_str_flags);
2713                if (lrec.abs_str_flags & F_PK_USED) {
2714                    *lrec.pk_gf_addr = "gf";
2715                    pk_second_pass = True;
2716                    pos = xlat(NULL, NULL, pos0, lrec.abs_str,
2717                            lrec.abs_str + strlen(lrec.abs_str));
2718                    TRY_TO_OPEN(pos, lrec.abs_str_flags);
2719                }
2720#endif
2721            }
2722            else {
2723                _Xconst char    *p;
2724
2725                steppp = &lrec.v.stephead;
2726                frecp = srchtype;
2727                seqno = 0;
2728                rootpp = &lrec.v.rootp;
2729
2730#if     CFGFILE
2731
2732                if (lrec.path1 == NULL)
2733                    ffrecurse(lrec.envptr, lrec.path2);
2734                else {
2735                    Boolean did_the_default = False;
2736
2737                    for (p = lrec.path1;;) {
2738                        if (*p == '\0' || *p == ':') {
2739                                /* bring in the default path */
2740                            if (!did_the_default) {
2741                                ffrecurse(lrec.envptr, lrec.path2);
2742                                did_the_default = True;
2743                            }
2744                        }
2745                        else
2746                            p = pathpart(p);
2747                        if (*p == '\0') break;
2748                        ++p;    /* skip the colon */
2749                    }
2750                }
2751
2752#else   /* !CFGFILE */
2753
2754                for (p = lrec.path1;;) {
2755                    if (*p == '\0' || *p == ':') {
2756                            /* bring in the default path */
2757                        if (lrec.path2 != NULL) {
2758                            _Xconst char        *p1     = lrec.path2;
2759
2760                            for (;;) {
2761                                if (*p1 == '\0') break;
2762                                if (*p1 == ':') continue;
2763                                p1 = pathpart(p1);
2764                                if (*p1 == '\0') break;
2765                                ++p1;   /* skip the colon */
2766                            }
2767                        }
2768                    }
2769                    else
2770                        p = pathpart(p);
2771                    if (*p == '\0') break;
2772                    ++p;        /* skip the colon */
2773                }
2774
2775#endif  /* CFGFILE */
2776
2777                file_found = NULL;
2778            }
2779        }
2780        else {
2781            /* it longjmp()s here when it finds the file */
2782            if (FFDEBUG) puts("--Success--\n");
2783            if (path_ret != NULL)
2784                *path_ret = newstring(ffline, -1);
2785        }
2786
2787        srchtype->v = lrec.v;   /* restore volatile parameters of *srchtype */
2788
2789        return file_found;
2790}
Note: See TracBrowser for help on using the repository browser.