filefind.c (45081B)
1 /*========================================================================*\ 2 3 Copyright (c) 1996-2013 Paul Vojta 4 5 Permission is hereby granted, free of charge, to any person obtaining a copy 6 of this software and associated documentation files (the "Software"), to 7 deal in the Software without restriction, including without limitation the 8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 sell copies of the Software, and to permit persons to whom the Software is 10 furnished to do so, subject to the following conditions: 11 12 The above copyright notice and this permission notice shall be included in 13 all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 PAUL VOJTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22 \*========================================================================*/ 23 24 /* 25 * filefind.c - Routines to perform file searches. 26 */ 27 28 /* 29 * We assume that "filf-app.h" includes the following, if appropriate: 30 * 31 * <stdlib.h> 32 * <unistd.h> 33 * <string.h> 34 * <memory.h> 35 * <sys/types.h> 36 * <sys/stat.h> 37 * <setjmp.h> 38 * <pwd.h> 39 */ 40 41 #include <sys/types.h> /* opendir() */ 42 43 #include <dirent.h> /* opendir() */ 44 #include <err.h> 45 #include <setjmp.h> /* jmp_buf, longjmp(), setjmp() */ 46 #include <stdlib.h> /* getenv() */ 47 48 #include "filf-app.h" /* application-related declarations & defs */ 49 #include "filefind.h" 50 #include "util.h" /* xfopen(), expandline(), ff_getpw() */ 51 52 53 /* default list of texmf trees */ 54 #define DEFAULT_TEXMF_PATH \ 55 "/usr/local/share/texmf-local:" \ 56 "/usr/local/share/texmf-dist:" \ 57 "/usr/local/share/texmf:" \ 58 "/usr/local/texlive/texmf-local:" \ 59 "/usr/local/texlive/texmf-dist:" \ 60 "/usr/local/texlive/texmf:" \ 61 "/usr/share/texmf-local:" \ 62 "/usr/share/texmf-dist:" \ 63 "/usr/share/texmf:" \ 64 "/usr/share/texlive/texmf-local:" \ 65 "/usr/share/texlive/texmf-dist:" \ 66 "/usr/share/texlive/texmf" 67 68 69 #if !defined(S_ISREG) && defined(S_IFREG) 70 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 71 #endif 72 73 #if !defined(S_ISDIR) && defined(S_IFDIR) 74 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 75 #endif 76 77 #if !defined(S_ISLNK) && defined(S_IFLNK) 78 #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) 79 #endif 80 81 #ifndef ST_NLINK_TRICK 82 #define ST_NLINK_TRICK 1 83 #endif /* !defined(ST_NLINK_TRICK) */ 84 85 /* These are defined so that the editor can match braces properly. */ 86 #define LBRACE '{' 87 #define RBRACE '}' 88 89 /* In configuration files, semicolons are also valid separators. */ 90 #define IS_SEP(c) ((c) == ':') 91 92 /* Local copy of findrec record. */ 93 94 static struct findrec lrec; 95 96 /* 97 * Additional data structures. 98 */ 99 100 struct statrec { /* status vis-a-vis laying down the file name */ 101 unsigned pos; /* position in ffline */ 102 int flags; 103 char quickchar; /* '\0' or 'q' or 'Q' */ 104 unsigned quickpos; /* value of pos (above) if 'q' or 'Q' */ 105 }; 106 107 struct texmfrec { /* list of texmf components */ 108 struct texmfrec *next; 109 const char *home; 110 const char *str; 111 int len; 112 int flags; 113 }; 114 115 static const char default_texmf_path[] = DEFAULT_TEXMF_PATH; 116 117 /* These are global variables. */ 118 119 const char *fF_values[MAX_N_OPTS]; /* not static */ 120 121 static FILE *file_found; 122 static jmp_buf got_it; 123 static struct steprec **bracepp; 124 static struct steprec **steppp; 125 static struct findrec *frecp; /* used to distinguish ls-Rs */ 126 static int seqno; 127 static struct texmfrec *texmfhead = NULL; 128 static struct rootrec **rootpp; 129 static const struct atomrec *treeatom; 130 131 /* dummy records for handling %s after // or wild cards */ 132 static struct atomrec pct_s_dummy = {NULL, NULL, NULL, F_PCT_S}; 133 static struct atomrec pct_s_dummy_slash 134 = {NULL, NULL, NULL, F_PCT_S | F_SLASH_SLASH}; 135 136 /* flag value for directories not read yet */ 137 static struct treerec not_read_yet = {NULL, NULL, NULL}; 138 139 /* 140 * The following expands in 128-byte increments to be as long as is 141 * necessary. We don't often use pointers to refer to elements of this 142 * array, since it may move when it is enlarged. 143 */ 144 145 #if !EXTERN_FFLINE 146 static char *ffline = NULL; 147 static int ffline_len = 0; /* current length of the array */ 148 #endif 149 150 151 /* 152 * ls-R database hash table 153 */ 154 155 struct lsr { 156 struct lsr *next; /* next in hash chain */ 157 struct findrec *frecp; /* which search type? */ 158 short seqno; /* which ls-R invocation we are using */ 159 short keylen; /* length of key */ 160 const char *key; 161 const char *value; 162 }; 163 164 static struct lsr *lsrtab[1024]; /* hash table */ 165 166 167 /* 168 * Forward references. 169 */ 170 171 static void dobrace(const char *, unsigned, int, Boolean); 172 static struct steprec *scan_pct_s(void); 173 static void atomize_pct_s(void); 174 175 176 #if !EXTERN_FFLINE 177 178 /* 179 * Expand ffline[] to at least the given size. 180 */ 181 182 static void 183 expandline(n) 184 int n; 185 { 186 int newlen = n + 128; 187 188 ffline = (ffline == NULL) ? xmalloc(newlen) : xrealloc(ffline, newlen); 189 ffline_len = newlen; 190 } 191 192 #endif /* !EXTERN_FFLINE */ 193 194 195 /* 196 * prehash() - hash function (before modding). 197 */ 198 199 static unsigned int 200 prehash(str, len) 201 const char *str; 202 size_t len; 203 { 204 const char *p; 205 unsigned int hash; 206 207 hash = 0; 208 for (p = str + len; p > str; ) 209 hash = hash * 5 + *--p; 210 return hash; 211 } 212 213 214 #if !EXTERN_GETPW 215 216 /* 217 * Look up the home directory. 218 */ 219 220 static const struct passwd * 221 ff_getpw(pp, p_end) 222 const char **pp; 223 const char *p_end; 224 { 225 const char *p = *pp; 226 const char *p1; 227 int len; 228 const struct passwd *pw; 229 230 ++p; /* skip the tilde */ 231 p1 = p; 232 while (p1 < p_end && *p1 != '/') ++p1; 233 len = p1 - p; 234 if (len == 0) /* if no user name */ 235 pw = getpwuid(getuid()); 236 else { 237 if (len >= ffline_len) 238 expandline(len); 239 bcopy(p, ffline, len); 240 ffline[len] = '\0'; 241 pw = getpwnam(ffline); 242 } 243 if (pw != NULL) 244 *pp = p1; 245 return pw; 246 } 247 248 #endif /* !EXTERN_GETPW */ 249 250 251 static void 252 gethome(pp, p_end, homepp) 253 const char **pp; 254 const char *p_end; 255 const char **homepp; 256 { 257 const struct passwd *pw; 258 259 pw = ff_getpw(pp, p_end); 260 if (pw != NULL) 261 *homepp = xstrdup(pw->pw_dir); 262 } 263 264 /* 265 * Scan the path part from // or wild cards, to the end. 266 * This assumes that {} have already been taken care of. 267 */ 268 269 static void 270 atomize(p0, sp) 271 const char *p0; 272 struct steprec *sp; 273 { 274 struct atomrec *head; 275 struct atomrec **linkpp; 276 int flags; /* F_SLASH_SLASH and F_WILD */ 277 Boolean percent_s; 278 const char *p = p0; 279 const char *p1; 280 const char *p2; 281 282 /* 283 * First check the string for braces. 284 */ 285 286 for (p1 = p; ;++p1) { 287 if (*p1 == '\0' || IS_SEP(*p1)) 288 break; 289 if (*p1 == LBRACE) { 290 /* Take care of braces first. This code should only be reached 291 * if atomize() is called from ff_prescan(). */ 292 unsigned n; 293 294 bracepp = &sp->nextstep; 295 n = p1 - p0; 296 if (n >= ffline_len) 297 expandline(n); 298 bcopy(p0, ffline, n); 299 dobrace(p1, n, 1, False); 300 return; 301 } 302 if (*p1 == '%' && p1[1] != '\0') 303 ++p1; 304 } 305 306 /* 307 * No braces. Split it up into atoms (delimited by / or //). 308 */ 309 310 linkpp = &head; 311 flags = 0; 312 percent_s = False; 313 for (;;) { 314 if (*p == '/') { 315 ++p; 316 flags |= F_SLASH_SLASH; 317 /* a %[qQ] may occur following the first //. */ 318 if (*p == '%') 319 if (p[1] == 'q' || p[1] == 'Q') { 320 if (p[1] == 'Q') flags |= F_QUICKONLY; 321 flags |= F_QUICKFIND; 322 p += 2; 323 } 324 } 325 p1 = p; 326 for (;; ++p) { 327 if (*p == '\0' || IS_SEP(*p)) { 328 p2 = NULL; 329 if (!(sp->flags & F_FILE_USED)) 330 p2 = lrec.no_f_str + 1; 331 break; 332 } 333 if (*p == '/') { 334 p2 = p + 1; 335 break; 336 } 337 if (*p == '%' && p[1] != '\0' && p[1] != '/') { 338 ++p; 339 if (*p == 'f') sp->flags |= F_FILE_USED; 340 else if (*p == 's' && (p[1] == '\0' || IS_SEP(p[1]))) { 341 --p; 342 percent_s = True; 343 if (lrec.v.pct_s_atom == NULL) 344 atomize_pct_s(); 345 break; 346 } 347 } 348 else if (*p == '*' || *p == '?' || *p == '[') 349 flags |= F_WILD; 350 } 351 if (p != p1) { /* add on an atomrec */ 352 struct atomrec *atomp; 353 354 atomp = xmalloc(sizeof(struct atomrec)); 355 *linkpp = atomp; 356 linkpp = &atomp->next; 357 atomp->flags = flags; 358 flags = 0; 359 atomp->p = p1; 360 atomp->p_end = p; 361 } 362 if (percent_s) { 363 *linkpp = (flags & F_SLASH_SLASH) ? &pct_s_dummy_slash 364 : &pct_s_dummy; 365 sp->flags |= F_PCT_S; 366 break; 367 } 368 p = p2; 369 if (p == NULL) { 370 *linkpp = NULL; 371 break; 372 } 373 } 374 sp->atom = head; 375 } 376 377 /* 378 * prescan2() sets up the steprec information for a brace alternative. 379 * It also does much of the work in ff_prescan(). 380 */ 381 382 static const char * 383 prescan2(sp, p, skip) 384 struct steprec *sp; 385 const char *p; 386 Boolean skip; /* true if initial / does not mean // */ 387 { 388 int flags; 389 390 /* 391 * Scan the string, looking for // or wild cards or braces. 392 * It takes the input / by / so that we can back up to the previous 393 * slash if there is a wild card (which may be hidden in braces). 394 */ 395 396 flags = sp->flags; 397 for (;;) { /* loop over slash-separated substrings */ 398 sp->strend = p; 399 sp->flags = flags; 400 while (*p == '/') { /* check for // */ 401 if (skip) { 402 ++p; 403 skip = False; 404 } 405 else { 406 atomize(p, sp); 407 return p; 408 } 409 } 410 for (;;) { /* loop over characters in this subpart */ 411 if (*p == '\0' || IS_SEP(*p)) { 412 sp->strend = p; 413 sp->flags = flags; 414 return p; 415 } 416 if (*p == '/') 417 break; 418 if (*p == LBRACE) { 419 unsigned n; 420 421 bracepp = &sp->nextstep; 422 n = p - sp->strend; 423 if (n >= ffline_len) 424 expandline(n); 425 bcopy(sp->strend, ffline, n); 426 dobrace(p, n, 1, skip); 427 return p; 428 } 429 skip = False; 430 if (*p == '%' && p[1] != '\0') { 431 ++p; 432 if (*p == 's' && (p[1] == '\0' || IS_SEP(p[1]))) { 433 sp->strend = p - 1; 434 sp->flags = flags; 435 sp->nextstep = scan_pct_s(); 436 return p; 437 } 438 else if (*p == 'f') 439 flags |= F_FILE_USED | F_VARIABLE; 440 else if (*p == 'F' || *p == lrec.x_var_char) 441 flags |= F_VARIABLE; 442 /* %c and %C are sorted out later. */ 443 } 444 else if (*p == '*' || *p == '?' || *p == '[') { 445 atomize(sp->strend, sp); 446 return p; 447 } 448 ++p; 449 } 450 ++p; 451 } 452 } 453 454 /* 455 * dobrace() is the recursive routine for handling {} alternatives. 456 * bracepp (global) = address of where to put next brace item on the 457 * linked list. It has to be global because dobrace() is called 458 * recursively and leaves its droppings at varying levels. 459 * p = input string pointer; it should point to the opening brace. 460 * pos = position within ffline[] for output string. 461 * level = nesting depth. 462 * 463 * skip: true if initial / does not mean // 464 */ 465 466 static void 467 dobrace(const char *p, unsigned pos0, int level, Boolean skip) 468 { 469 int lev; 470 int level1; 471 const char *p1; 472 unsigned pos; 473 474 for (;;) { /* loop over the alternatives */ 475 lev = 0; 476 ++p; /* skip left brace or comma */ 477 pos = pos0; 478 for (;;) { /* loop over characters */ 479 if (*p == '\0' || IS_SEP(*p)) { 480 /* keep the braces matched: { */ 481 warnx("Missing } in '%s' path.", lrec.type); 482 return; 483 } 484 else if (*p == '%') { 485 if (p[1] != '\0') { 486 if (pos >= ffline_len) 487 expandline(pos); 488 ffline[pos++] = *p++; 489 } 490 } 491 else if (*p == LBRACE) { 492 dobrace(p, pos, level + 1, skip && pos == pos0); 493 /* skip to the next matching comma or right brace */ 494 for (;;) { 495 if (*p == '\0' || IS_SEP(*p)) 496 return; /* this has already been reported */ 497 else if (*p == '%') { 498 if (p[1] != '\0') ++p; 499 } 500 else if (*p == LBRACE) 501 ++lev; 502 else if (*p == ',') { 503 if (lev == 0) 504 break; 505 } 506 else if (*p == RBRACE) { 507 if (lev == 0) 508 break; 509 --lev; 510 } 511 ++p; 512 } 513 goto out2; 514 } 515 else if (*p == ',' || *p == RBRACE) 516 break; 517 if (pos >= ffline_len) 518 expandline(pos); 519 ffline[pos++] = *p++; 520 } 521 p1 = p; 522 level1 = level; 523 for (;;) { 524 /* skip until matching right brace */ 525 for (;;) { 526 if (*p1 == '\0' || IS_SEP(*p1)) 527 goto out2; /* this has already been reported */ 528 if (*p1 == '%') { 529 if (p1[1] != '\0') ++p1; 530 } 531 else if (*p1 == LBRACE) 532 ++lev; 533 else if (*p1 == RBRACE) { 534 if (lev == 0) { 535 ++p1; 536 break; 537 } 538 --lev; 539 } 540 ++p1; 541 } 542 --level1; 543 /* now copy until a qualifying comma */ 544 for (;;) { 545 if (*p1 == '\0' || IS_SEP(*p1)) { 546 if (level1 == 0) { 547 struct steprec *bp; 548 549 if (pos >= ffline_len) 550 expandline(pos); 551 ffline[pos++] = '\0'; 552 *bracepp = bp = xmalloc(sizeof(struct steprec)); 553 bp->next = NULL; 554 bp->atom = NULL; 555 bp->nextstep = NULL; 556 bracepp = &bp->next; 557 bp->str = xmemdup(ffline, pos); 558 bp->flags = 0; 559 (void) prescan2(bp, bp->str, skip); 560 } 561 /* else: this has already been reported */ 562 goto out2; 563 } 564 if (*p1 == '%') { 565 if (p1[1] != '\0') { 566 if (pos >= ffline_len) 567 expandline(pos); 568 ffline[pos++] = '%'; 569 ++p1; 570 } 571 } 572 else if (*p1 == LBRACE) { 573 dobrace(p1, pos, level1 + 1, False); 574 goto out2; 575 } 576 else if (*p1 == ',' && level1 > 0) 577 break; 578 else if (*p1 == RBRACE && level1 > 0) { 579 --level1; 580 ++p1; 581 continue; 582 } 583 if (pos >= ffline_len) 584 expandline(pos); 585 ffline[pos++] = *p1++; 586 } 587 } 588 out2: 589 if (*p != ',') break; 590 } 591 } 592 593 594 /* 595 * Prescan the '%s' string. This is done only once. 596 */ 597 598 static struct steprec * 599 scan_pct_s() 600 { 601 struct steprec **headpp; 602 const char *p; 603 struct steprec *sp; 604 605 if (lrec.v.pct_s_head != NULL) /* if we've already done this */ 606 return lrec.v.pct_s_head; 607 608 headpp = &lrec.v.pct_s_head; 609 p = lrec.pct_s_str; 610 for (p = lrec.pct_s_str; ; ++p) { 611 ++lrec.v.pct_s_count; 612 *headpp = sp = xmalloc(sizeof(struct steprec)); 613 sp->atom = NULL; 614 sp->nextstep = NULL; 615 sp->flags = 0; 616 sp->str = p; 617 headpp = &sp->next; 618 (void) prescan2(sp, p, False); 619 for (; *p != '\0' && !IS_SEP(*p); ++p) 620 if (*p == '%' && p[1] != '\0') ++p; 621 if (*p == '\0') break; 622 } 623 *headpp = NULL; 624 return lrec.v.pct_s_head; 625 } 626 627 /* 628 * Pre-atomize the '%s' strings. Again, done only once. 629 */ 630 631 static void 632 atomize_pct_s() 633 { 634 const struct atomrec **app; 635 struct steprec *sp; 636 637 (void) scan_pct_s(); /* make sure the count is valid */ 638 639 app = lrec.v.pct_s_atom = 640 xmalloc((unsigned) lrec.v.pct_s_count * sizeof(*app)); 641 642 for (sp = lrec.v.pct_s_head; sp != NULL; sp = sp->next, ++app) { 643 static struct steprec dummy; 644 645 dummy.flags = 0; 646 atomize(sp->str, &dummy); 647 *app = dummy.atom; 648 } 649 } 650 651 652 /* 653 * Init_texmf - Initialize the linked list of options for %t. 654 */ 655 656 static void /* here's a small subroutine */ 657 add_to_texmf_list(pp) 658 const char **pp; 659 { 660 static struct texmfrec **tpp = &texmfhead; 661 struct texmfrec *tp; 662 const char *p; 663 const char *p_end; 664 const char *home = NULL; 665 int flags = 0; 666 667 p = *pp; 668 p_end = index(p, ':'); 669 if (p_end == NULL) p_end = p + strlen(p); 670 *pp = p_end; 671 672 if (*p == '!' && p[1] == '!') { 673 p += 2; 674 flags = F_QUICKONLY; 675 } 676 677 if (*p == '~') 678 gethome(&p, p_end, &home); 679 680 tp = *tpp = xmalloc(sizeof(struct texmfrec)); 681 tp->home = home; 682 tp->str = p; 683 tp->len = p_end - p; 684 tp->flags = flags; 685 tp->next = NULL; 686 tpp = &tp->next; 687 } 688 689 static void 690 init_texmf() 691 { 692 const char *texmf; 693 const char *texmf2; 694 const struct texmfrec *tp; 695 unsigned n; 696 697 if (texmfhead != NULL) 698 return; 699 700 texmf = getenv("TEXMF"); 701 texmf2 = default_texmf_path; 702 if (texmf == NULL) { 703 texmf = default_texmf_path; 704 texmf2 = NULL; 705 } 706 707 for (;;) { 708 if (*texmf == '\0' || *texmf == ':') { 709 if (texmf2 != NULL) 710 for (;;) { 711 while (*texmf2 == ':') ++texmf2; 712 if (*texmf2 == '\0') break; 713 add_to_texmf_list(&texmf2); 714 } 715 } 716 else 717 add_to_texmf_list(&texmf); 718 if (*texmf == '\0') 719 break; 720 ++texmf; 721 } 722 723 /* Make sure ffline[] is long enough for these. */ 724 725 n = 0; 726 for (tp = texmfhead; tp != NULL; tp = tp->next) 727 if (tp->len > n) 728 n = tp->len; 729 730 if (n > ffline_len) 731 expandline(n); 732 } 733 734 735 /* 736 * Scan this path part. This is done at most once. 737 * We look for // or wild cards or {}. 738 */ 739 740 static const char * 741 ff_prescan(p) 742 const char *p; 743 { 744 struct steprec *sp; 745 746 *steppp = sp = xmalloc(sizeof(struct steprec)); 747 sp->next = NULL; 748 sp->flags = 0; 749 sp->atom = NULL; 750 sp->nextstep = NULL; 751 752 /* 753 * Scan the string, looking for // or wild cards or braces. 754 */ 755 756 sp->str = p; 757 return prescan2(sp, p, True); 758 } 759 760 /* 761 * This routine handles the translation of the '%' modifiers. 762 * It returns the new number string length (pos). 763 * It makes sure that at least one free byte remains in ffline[]. 764 */ 765 766 static unsigned 767 xlat(stat_in, stat_ret, pos0, p, lastp) 768 const struct statrec *stat_in; 769 struct statrec *stat_ret; 770 unsigned pos0; 771 const char *p; 772 const char *lastp; 773 { 774 struct statrec status; 775 const char *q; 776 int l; 777 778 status.pos = pos0; 779 if (stat_ret != NULL) 780 status = *stat_in; 781 782 while (p < lastp) { 783 q = memchr(p, '%', lastp - p); 784 l = (q != NULL ? q : lastp) - p; 785 if (status.pos + l >= ffline_len) 786 expandline(status.pos + l); 787 bcopy(p, ffline + status.pos, l); 788 status.pos += l; 789 p = q; 790 if (p == NULL) break; 791 do { 792 ++p; 793 if (p >= lastp) break; 794 q = index(lrec.fF_etc, *p); 795 if (q != NULL) { 796 const char *str = fF_values[q - lrec.fF_etc]; 797 798 if (str != NULL) { 799 l = strlen(str); 800 if (status.pos + l >= ffline_len) 801 expandline(status.pos + l); 802 bcopy(str, ffline + status.pos, l); 803 status.pos += l; 804 } 805 else { /* eliminate a possible double slash */ 806 if (p[1] == '/' && (status.pos == 0 807 || ffline[status.pos - 1] == '/')) 808 --status.pos; 809 } 810 } 811 else if (*p == 'q' || *p == 'Q') { 812 status.quickchar = *p; 813 status.quickpos = status.pos; 814 } 815 else { 816 if (status.pos + 1 >= ffline_len) 817 expandline(ffline_len); 818 ffline[status.pos++] = *p; 819 } 820 ++p; 821 } 822 while (p < lastp && *p == '%'); 823 } 824 825 if (stat_ret != NULL) 826 *stat_ret = status; 827 return status.pos; 828 } 829 830 831 /* 832 * Try to open the file. Exit via longjmp() if success. 833 */ 834 static void 835 try_to_open(unsigned pos, int flags) 836 { 837 ffline[pos] = '\0'; 838 839 if (FFDEBUG) 840 printf("Trying %s file %s\n", lrec.type, ffline); 841 842 file_found = xfopen(ffline, "r"); 843 if (file_found != NULL) 844 longjmp(got_it, 1); 845 } 846 847 848 /* 849 * lsrgetline - Read a line from the (ls-R) file and return: 850 * minus its length if it ends in a colon (including the colon); 851 * its length otherwise; 852 * zero for end of file. 853 * Blank lines are skipped. 854 */ 855 856 static int 857 lsrgetline(f, pos0) 858 FILE *f; /* ls-R file */ 859 unsigned pos0; /* relative position in line to start reading */ 860 { 861 unsigned pos = pos0; 862 863 for (;;) { 864 if (pos + 4 >= ffline_len) 865 expandline(pos); 866 if (fgets(ffline + pos, ffline_len - pos, f) == NULL) 867 break; 868 pos += strlen(ffline + pos); 869 if (pos > pos0 && ffline[pos - 1] == '\n') { 870 --pos; /* trim newline */ 871 if (pos > pos0) break; /* if ffline still nonempty */ 872 } 873 } 874 if (pos > pos0 && ffline[pos - 1] == ':') { 875 ffline[pos - 1] = '/'; /* change trailing colon to slash */ 876 return pos0 - pos; 877 } 878 else 879 return pos - pos0; 880 } 881 882 883 /* 884 * lsrput - Put a key/value pair into the ls-R database. 885 */ 886 887 static void 888 lsrput(key, keylen, value) 889 const char *key; 890 unsigned keylen; 891 const char *value; 892 { 893 struct lsr **lpp; 894 struct lsr *lp; 895 896 #if 0 897 fputs("lsrput: key = `", stdout); 898 fwrite(key, 1, keylen, stdout); 899 printf("'; value = `%s'.\n", value); 900 #endif 901 902 /* make the new record */ 903 lp = xmalloc(sizeof(*lp)); 904 lp->next = NULL; 905 lp->frecp = frecp; 906 lp->seqno = seqno; 907 lp->keylen = keylen; 908 lp->key = xmemdup(key, keylen); 909 lp->value = value; 910 911 /* insert it */ 912 lpp = lsrtab + prehash(key, keylen) % XtNumber(lsrtab); 913 while (*lpp != NULL) /* skip to end of chain (performance reasons) */ 914 lpp = &(*lpp)->next; 915 *lpp = lp; 916 } 917 918 919 /* 920 * init_quick_find - Read ls-R database for this particular search. 921 */ 922 923 static void 924 init_quick_find(root, pos, atom, pos1) 925 struct rootrec *root; /* pointer to the governing rootrec */ 926 unsigned pos; /* position of %[qQ] in ffline[] */ 927 const struct atomrec *atom; /* the beginning of the atomlist */ 928 unsigned pos1; /* next free byte in ffline[] */ 929 { 930 #define LS_R_LEN 5 /* length of the string "ls-R\0" */ 931 char tmpsav[LS_R_LEN]; 932 int keypos; /* position of start of key pattern */ 933 int keyend; /* last + 1 character of the key */ 934 int keypos_a; /* position + 1 of last slash */ 935 unsigned pos2; /* end + 1 of the decoded thing */ 936 int nslashes; /* how many slashes in bbbcccddd */ 937 FILE *f; /* ls-R file */ 938 const char *fullpat; /* fields in *root */ 939 const char *keypat; /* " */ 940 const char *keypat_end; /* " */ 941 const char *fullpat_end; /* " */ 942 const char *keypat_a; /* position of end+1 of restricted 943 * initial fixed part */ 944 int retval; /* return value of lsrgetline */ 945 946 /* 947 * Reencode the string of atoms into a character string 948 * When we're done, ffline[] contains: 949 * /aa/aa/a...a/ bbb/bb...bb/bbb %f/cc/c...c/%d ddd/ddd/.../ddd 950 * 951 * where the aaa part is what's already in ffline[], and 952 * the rest is the decoded atom chain. The ccc part of this 953 * string contains all the variable specifiers (not replaced by 954 * anything), and the bbb and ddd parts are character strings that 955 * should occur verbatim in the file name. 956 */ 957 958 root->flags &= ~F_QUICKFIND; /* clear it for now; set it later */ 959 960 if (atom->flags & F_PCT_S) 961 atom = treeatom; 962 963 if (atom->flags & F_WILD) return; /* not allowed */ 964 965 pos2 = pos1; 966 keypos = -1; 967 nslashes = 0; 968 969 for (;;) { 970 const char *p; 971 972 keypos_a = pos2; 973 /* translate the atom, leaving %f and maybe %d alone */ 974 for (p = atom->p; p < atom->p_end; ++p) { 975 if (p >= atom->p_end) break; 976 if (*p == '%') { 977 const char *q; 978 979 ++p; 980 if (p >= atom->p_end) break; 981 q = index(lrec.fF_etc, *p); 982 if (q != NULL) { 983 if (q - lrec.fF_etc < lrec.n_var_opts) { 984 /* it's a variable specifier (%f or ...) */ 985 --p; /* copy it over */ 986 if (keypos < 0) keypos = pos2; 987 keyend = pos2 + 2; 988 } 989 else { 990 const char *str = fF_values[q - lrec.fF_etc]; 991 992 if (str != NULL) { 993 int l = strlen(str); 994 995 if (pos2 + l >= ffline_len) 996 expandline(pos2 + l); 997 bcopy(str, ffline + pos2, l); 998 pos2 += l; 999 } 1000 continue; 1001 } 1002 } 1003 } 1004 if (pos2 + 1 >= ffline_len) 1005 expandline(pos2); 1006 ffline[pos2++] = *p; 1007 } 1008 1009 atom = atom->next; 1010 if (atom == NULL) break; 1011 1012 if (atom->flags & F_PCT_S) atom = treeatom; 1013 if (atom->flags & (F_SLASH_SLASH | F_WILD)) 1014 return; /* not supported */ 1015 1016 if (pos2 == keypos_a) continue; /* if /%m/ with no mode */ 1017 ffline[pos2++] = '/'; 1018 ++nslashes; 1019 } 1020 1021 if (keypos_a > keypos) keypos_a = keypos; 1022 1023 root->flags |= F_QUICKFIND; /* it's eligible */ 1024 1025 if (FFDEBUG) { 1026 #define DDD(a,b,c) \ 1027 fputs(a, stdout); \ 1028 fwrite(ffline + (b), 1, (c) - (b), stdout); \ 1029 puts("'"); 1030 1031 DDD("\nReading ls-R.\nOriginal string = `", 0, pos1); 1032 DDD("Restricted initial fixed part = `", pos1, keypos_a); 1033 DDD("Initial fixed part = `", pos1, keypos); 1034 DDD("Key pattern = `", keypos, keyend); 1035 DDD("Final fixed part = `", keyend, pos2); 1036 #undef DDD 1037 } 1038 1039 /* 1040 * Open ls-R and start reading it. 1041 */ 1042 1043 bcopy(ffline + pos, tmpsav, sizeof(tmpsav)); 1044 bcopy("ls-R", ffline + pos, sizeof(tmpsav)); 1045 if (FFDEBUG) printf("Opening ls-R file %s\n", ffline); 1046 f = xfopen(ffline, "r"); 1047 bcopy(tmpsav, ffline + pos, sizeof(tmpsav)); 1048 if (f == NULL) { 1049 if (FFDEBUG) printf("xfopen: %s\n", strerror(errno)); 1050 return; 1051 } 1052 1053 fullpat = xmemdup(ffline + pos1, pos2 - pos1); 1054 keypat = fullpat + (keypos - pos1); 1055 keypat_a = fullpat + (keypos_a - pos1); 1056 keypat_end = fullpat + (keyend - pos1); 1057 fullpat_end = fullpat + (pos2 - pos1); 1058 1059 do { /* skip initial comment lines */ 1060 retval = lsrgetline(f, pos1); 1061 if (retval == 0) { 1062 fclose(f); 1063 return; /* premature end of file */ 1064 } 1065 } while (ffline[pos1] == '%'); 1066 1067 /* First take care of file names without a directory: these are 1068 * those in the same directory as the ls-R file */ 1069 1070 while (retval > 0) { 1071 if (nslashes == 0 /* if there are no slashes in the pattern */ 1072 /* and if ls-R is in the same directory 1073 * as the //: */ 1074 && pos1 == pos 1075 /* and if the initial fixed part matches: */ 1076 && memcmp(ffline + pos1, fullpat, keypat - fullpat) == 0 1077 /* and if the final fixed part matches: */ 1078 && memcmp(ffline + pos1 + retval 1079 - (fullpat_end - keypat_end), 1080 keypat_end, fullpat_end - keypat_end) == 0) 1081 /* then create a directory entry */ 1082 lsrput(ffline + pos1 + (keypat - fullpat), 1083 retval - (keypat - fullpat) - (fullpat_end - keypat_end), ""); 1084 retval = lsrgetline(f, pos1); /* read next line */ 1085 } 1086 1087 /* 1088 * Now do whole directories. 1089 */ 1090 1091 for (;;) { 1092 int i; 1093 const char *p; 1094 const char *p_begin; 1095 const char *p_end; 1096 unsigned pos3; 1097 unsigned pos4; 1098 unsigned pos5; 1099 unsigned retpos; 1100 char *value; 1101 1102 while (retval > 0) /* skip to next directory */ 1103 retval = lsrgetline(f, pos1); 1104 if (retval == 0) break; /* if end of file */ 1105 retval = -retval; /* length of the string */ 1106 /* also, retval is now > 0, so a continue statement won't lead to 1107 * an infinite loop. */ 1108 retpos = pos1 + retval; 1109 if (ffline[pos1] == '/') { /* if absolute path */ 1110 /* these should be referring to the same directory */ 1111 if (retval < pos1 || memcmp(ffline + pos1, ffline, pos1) != 0) 1112 continue; 1113 pos2 = pos1 + pos1; 1114 } 1115 else { /* relative path */ 1116 pos2 = pos1; 1117 if (retval >= 2 && ffline[pos1] == '.' 1118 && ffline[pos1 + 1] == '/') 1119 pos2 += 2; /* eliminate "./" */ 1120 /* these should be referring to the same directory */ 1121 if (retpos - pos2 < pos1 - pos 1122 || memcmp(ffline + pos2, ffline + pos, pos1 - pos) != 0) 1123 continue; 1124 pos2 += pos1 - pos; 1125 } 1126 /* now pos2 points to what should follow the // */ 1127 /* count backwards from the end of the string to just after the 1128 (nslashes + 1)-st slash */ 1129 p_begin = ffline + pos2; 1130 p_end = ffline + retpos; 1131 i = nslashes; 1132 for (p = p_end; p > p_begin; ) 1133 if (*--p == '/') { 1134 if (i == 0) { 1135 ++p; 1136 break; 1137 } 1138 else --i; 1139 } 1140 if (i > 0) continue; /* if too few slashes */ 1141 pos3 = p - ffline; 1142 /* the next part of the directory name should match the initial 1143 fixed part of the pattern */ 1144 if (p_end - p < keypat_a - fullpat 1145 || memcmp(p, fullpat, keypat_a - fullpat) != 0) 1146 continue; 1147 pos4 = pos3 + (keypat - fullpat); /* start of key */ 1148 value = NULL; 1149 for (;;) { /* read the files in this directory */ 1150 retval = lsrgetline(f, retpos); 1151 if (retval <= 0) { /* if done with list*/ 1152 bcopy(ffline + retpos, ffline + pos1, -retval); 1153 break; 1154 } 1155 /* check the remaining part of the initial fixed part */ 1156 /* (the first test is only for performance) */ 1157 if (keypat_a != keypat 1158 && memcmp(ffline + retpos, keypat_a, keypat - keypat_a) != 0) 1159 continue; 1160 /* end of key */ 1161 pos5 = retpos + retval - (fullpat_end - keypat_end); 1162 /* check the final fixed part */ 1163 if (pos5 < pos4 || (fullpat_end != keypat_end 1164 && memcmp(ffline + pos5, keypat_end, 1165 fullpat_end - keypat_end) != 0)) 1166 continue; 1167 if (value == NULL) { 1168 value = xmemdup(ffline + pos2, pos3 - pos2 + 1); 1169 value[pos3 - pos2] = '\0'; 1170 } 1171 lsrput(ffline + pos4, pos5 - pos4, value); 1172 } 1173 } 1174 1175 fclose(f); /* close the file */ 1176 1177 /* Fill in the requisite entries in *root: we have ls-R. */ 1178 root->fullpat = fullpat; 1179 root->fullpat_end = fullpat_end; 1180 root->keypat = keypat; 1181 root->keypat_end = keypat_end; 1182 } 1183 1184 /* 1185 * wildmatch - Tell whether the string matches the pattern. 1186 * 1187 * pat pointer to first character of pattern 1188 * pat_end pointer to last + 1 character of pattern 1189 * candidate the string 1190 * allowvar how many characters of fF_etc should be treated 1191 * as * 1192 */ 1193 1194 static Boolean 1195 wildmatch(pat, pat_end, candidate, allowvar) 1196 const char *pat; 1197 const char *pat_end; 1198 const char *candidate; 1199 int allowvar; /* How many of %f, %F, %d, ... to */ 1200 /* treat as '*' */ 1201 { 1202 const char *p; 1203 const char *q = candidate; 1204 1205 for (p = pat; p < pat_end; ++p) 1206 switch (*p) { 1207 1208 case '%': { 1209 const char *q1; 1210 1211 if (p + 1 >= pat_end) continue; 1212 ++p; 1213 q1 = index(lrec.fF_etc, *p); 1214 if (q1 == NULL) { /* %[ or something */ 1215 if (*q != *p) 1216 return False; 1217 ++q; 1218 break; 1219 } 1220 1221 if (q1 - lrec.fF_etc < allowvar) { /* treat it as '*' */ 1222 for (q1 = q + strlen(q); q1 >= q; --q1) 1223 if (wildmatch(p + 1, pat_end, q1, allowvar)) 1224 return True; 1225 return False; 1226 } 1227 else { 1228 const char *str = fF_values[q1 - lrec.fF_etc]; 1229 int l; 1230 1231 if (str == NULL) str = ""; 1232 l = strlen(str); 1233 if (bcmp(q, str, l) != 0) 1234 return False; 1235 q += l; 1236 } 1237 break; 1238 } 1239 1240 case '*': { 1241 const char *q1; 1242 1243 for (q1 = q + strlen(q); q1 >= q; --q1) 1244 if (wildmatch(p + 1, pat_end, q1, allowvar)) 1245 return True; 1246 return False; 1247 } 1248 1249 case '?': 1250 if (*q == '\0') 1251 return False; 1252 ++q; 1253 break; 1254 1255 case '[': { 1256 char c; 1257 Boolean reverse = False; 1258 1259 c = *q++; 1260 if (c == '\0') 1261 return False; 1262 1263 ++p; 1264 if (*p == '^') { 1265 reverse = True; 1266 ++p; 1267 } 1268 1269 for (;;) { 1270 char c1; 1271 char c2; 1272 1273 if (p >= pat_end) 1274 return False; /* syntax error */ 1275 c1 = *p; 1276 if (c1 == ']') { 1277 if (reverse) 1278 break; /* success */ 1279 else 1280 return False; 1281 } 1282 c2 = c1; 1283 ++p; 1284 if (*p == '-' && p[1] != ']') { 1285 ++p; 1286 c2 = *p++; 1287 if (c2 == '%') c2 = *p++; 1288 } 1289 if (p >= pat_end) 1290 return False; /* syntax error */ 1291 if (c >= c1 && c <= c2) { 1292 if (reverse) 1293 return False; 1294 else { /* success */ 1295 while (*p != ']') { 1296 if (*p == '%') ++p; 1297 ++p; 1298 if (p >= pat_end) 1299 return False; /* syntax error */ 1300 } 1301 break; 1302 } 1303 } 1304 } 1305 break; 1306 } 1307 1308 default: 1309 if (*q != *p) 1310 return False; 1311 ++q; 1312 } 1313 1314 if (*q != '\0') 1315 return False; 1316 return True; 1317 } 1318 1319 1320 /* 1321 * Read in the given directory. 1322 */ 1323 1324 static void 1325 filltree(pos, treepp, atom, slashslash) 1326 unsigned pos; 1327 struct treerec **treepp; 1328 const struct atomrec *atom; 1329 Boolean slashslash; 1330 { 1331 DIR *f; 1332 struct dirent *dp; 1333 1334 if (pos == 0) { 1335 if (ffline_len == 0) 1336 expandline(2); 1337 ffline[0] = '.'; 1338 ffline[1] = '\0'; 1339 } 1340 else 1341 ffline[pos - 1] = '\0'; 1342 1343 if (FFDEBUG) 1344 printf("Opening directory %s\n", ffline); 1345 1346 f = opendir(ffline); 1347 if (f == NULL) { 1348 if (FFDEBUG) printf("%s: %s\n", ffline, strerror(errno)); 1349 *treepp = NULL; 1350 return; 1351 } 1352 1353 if (pos != 0) ffline[pos - 1] = '/'; 1354 for (;;) { 1355 char *line1; 1356 struct stat statbuf; 1357 struct treerec *leaf; 1358 Boolean isdir; 1359 unsigned len; 1360 #if ST_NLINK_TRICK 1361 struct treerec *child; 1362 #endif 1363 1364 dp = readdir(f); 1365 if (dp == NULL) break; /* done */ 1366 1367 len = strlen((dp)->d_name); 1368 if (pos + len >= ffline_len) 1369 expandline(pos + (int) len); 1370 line1 = ffline + pos; 1371 bcopy(dp->d_name, line1, len); 1372 line1[len] = '\0'; 1373 if (*line1 == '.' && (line1[1] == '\0' 1374 || (line1[1] == '.' && line1[2] == '\0'))) 1375 continue; /* skip . and .. */ 1376 1377 if (lstat(ffline, &statbuf) != 0) { 1378 warn("lstat: filltree ffline"); 1379 continue; 1380 } 1381 isdir = False; 1382 #if ST_NLINK_TRICK 1383 child = ¬_read_yet; 1384 #endif 1385 if (S_ISDIR(statbuf.st_mode)) { 1386 isdir = True; 1387 if (!slashslash 1388 && (atom->next == NULL 1389 || !wildmatch(atom->p, atom->p_end, line1, 1390 lrec.n_var_opts))) 1391 continue; 1392 #if ST_NLINK_TRICK 1393 if (statbuf.st_nlink <= 2) 1394 child = NULL; 1395 #endif 1396 } 1397 else if (S_ISREG(statbuf.st_mode)) { 1398 if (atom->next != NULL 1399 || (slashslash && !(atom->flags & F_WILD)) 1400 || !wildmatch(atom->p, atom->p_end, line1, 1401 lrec.n_var_opts)) 1402 continue; 1403 } 1404 else continue; /* something else */ 1405 1406 leaf = xmalloc(sizeof(struct treerec)); 1407 { 1408 char *str; 1409 1410 str = xmemdup(dp->d_name, (unsigned) len + 1); 1411 str[len] = '\0'; 1412 leaf->dirname = str; 1413 } 1414 #if ST_NLINK_TRICK 1415 leaf->child = child; 1416 #else 1417 leaf->child = ¬_read_yet; 1418 #endif 1419 leaf->isdir = isdir; 1420 *treepp = leaf; 1421 treepp = &leaf->next; 1422 } 1423 closedir(f); 1424 1425 *treepp = NULL; 1426 } 1427 1428 1429 /* 1430 * do_tree_search() - recursive routine for // and wild card searches. 1431 */ 1432 1433 static void 1434 do_tree_search(treepp, atom, goback, pos, flags) 1435 struct treerec **treepp; 1436 const struct atomrec *atom; 1437 const struct atomrec *goback; 1438 unsigned pos; 1439 int flags; 1440 { 1441 int aflags; 1442 struct treerec *tp; 1443 1444 /* 1445 * If we're at the end of the chain of atoms, try to open the file. 1446 */ 1447 1448 if (atom == NULL) { 1449 try_to_open(pos - 1, flags); 1450 return; 1451 } 1452 1453 /* 1454 * Otherwise, we're still forming the path. 1455 */ 1456 1457 aflags = atom->flags; 1458 if (aflags & F_PCT_S) { /* if it's a dummy record */ 1459 atom = treeatom; 1460 aflags |= atom->flags; 1461 } 1462 1463 if (aflags & F_SLASH_SLASH) 1464 goback = atom; 1465 1466 if (goback == NULL) { /* wild card search, no // yet */ 1467 /* 1468 * If the next atom is neither wild nor variable, then we don't 1469 * need to do a readdir() on the directory. Just add the 1470 * appropriate characters to the path and recurse. 1471 */ 1472 if (!(atom->flags & (F_WILD | F_VARIABLE))) { 1473 pos = xlat(NULL, NULL, pos, atom->p, atom->p_end); 1474 ffline[pos++] = '/'; 1475 do_tree_search(treepp, atom->next, goback, pos, 1476 flags | atom->flags); 1477 return; 1478 } 1479 /* 1480 * Otherwise, go down one level in the tree. 1481 */ 1482 if (*treepp == ¬_read_yet) /* if readdir() not done yet */ 1483 filltree(pos, treepp, atom, False); /* do it */ 1484 1485 for (tp = *treepp; tp != NULL; tp = tp->next) 1486 if (wildmatch(atom->p, atom->p_end, tp->dirname, 0)) { 1487 int len = strlen(tp->dirname); 1488 bcopy(tp->dirname, ffline + pos, len); 1489 ffline[pos + len++] = '/'; 1490 do_tree_search(&tp->child, atom->next, goback, pos + len, 1491 flags | atom->flags); 1492 } 1493 1494 return; 1495 } 1496 1497 /* 1498 * If we got here, then we're past a // 1499 */ 1500 1501 if (atom->next == NULL && !(atom->flags & F_WILD)) 1502 /* if we can try a file */ 1503 try_to_open(xlat(NULL, NULL, pos, atom->p, atom->p_end), 1504 flags | atom->flags); 1505 1506 if (*treepp == ¬_read_yet) /* if readdir() not done yet */ 1507 filltree(pos, treepp, atom, True); /* do it */ 1508 1509 for (tp = *treepp; tp != NULL; tp = tp->next) { 1510 tp->tried_already = False; 1511 if ((atom->next != NULL || (atom->flags & F_WILD)) 1512 && wildmatch(atom->p, atom->p_end, tp->dirname, 0)) { 1513 int len = strlen(tp->dirname); 1514 bcopy(tp->dirname, ffline + pos, len); 1515 ffline[pos + len++] = '/'; 1516 do_tree_search(&tp->child, atom->next, goback, pos + len, 1517 flags | atom->flags); 1518 tp->tried_already = True; 1519 } 1520 } 1521 1522 for (tp = *treepp; tp != NULL; tp = tp->next) 1523 if (!tp->tried_already && tp->isdir) { 1524 int len = strlen(tp->dirname); 1525 bcopy(tp->dirname, ffline + pos, len); 1526 ffline[pos + len++] = '/'; 1527 do_tree_search(&tp->child, goback, goback, pos + len, flags); 1528 } 1529 } 1530 1531 1532 /* 1533 * begin_tree_search() - start a wild card or // search. 1534 */ 1535 1536 static void 1537 begin_tree_search(status, oneatom, twoatom) 1538 const struct statrec *status; 1539 const struct atomrec *oneatom; /* initial atom */ 1540 const struct atomrec *twoatom; /* if F_PCT_S set */ 1541 { 1542 struct treerec **tpp; 1543 1544 #if 0 1545 { 1546 const struct atomrec *atom; 1547 1548 puts("begin_tree_search():"); 1549 fwrite(ffline, 1, status->pos, stdout); 1550 putchar('\n'); 1551 atom = oneatom; 1552 for (;;) { 1553 fputs("+ ", stdout); 1554 if (atom->flags & F_SLASH_SLASH) fputs("(//) ", stdout); 1555 if (atom->flags & F_PCT_S) atom = twoatom; 1556 fwrite(atom->p, 1, atom->p_end - atom->p, stdout); 1557 if (atom->flags & F_WILD) fputs(" (wild)", stdout); 1558 putchar('\n'); 1559 atom = atom->next; 1560 if (atom == NULL) break; 1561 } 1562 putchar('\n'); 1563 } 1564 #endif 1565 1566 ++seqno; 1567 treeatom = twoatom; /* save this globally */ 1568 1569 /* 1570 * Find the record controlling this mess. Create one, if necessary. 1571 */ 1572 1573 if (status->flags & F_VARIABLE) { 1574 struct vrootrec { 1575 struct rootrec *next; /* link to next in hash chain */ 1576 struct treerec *tree; /* the tree */ 1577 const char *path; 1578 int seqno; 1579 }; 1580 struct rtab { 1581 struct rtab *next; 1582 int seqno; 1583 int len; 1584 const char *tag; 1585 struct vrootrec *r; 1586 }; 1587 static struct rtab *varroot[32]; 1588 struct rtab **tpp2; 1589 struct rtab *t; 1590 struct vrootrec *r; 1591 1592 tpp2 = varroot + prehash(ffline, status->pos) % XtNumber(varroot); 1593 for (;;) { 1594 t = *tpp2; 1595 if (t == NULL) { 1596 t = xmalloc(sizeof(*t)); 1597 t->seqno = seqno; 1598 t->len = status->pos; 1599 t->tag = xmemdup(ffline, status->pos); 1600 r = xmalloc(sizeof(*r)); 1601 r->next = NULL; 1602 r->tree = ¬_read_yet; /* readdir() not done yet */ 1603 t->r = r; 1604 t->next = NULL; 1605 *tpp2 = t; 1606 break; 1607 } 1608 if (t->seqno == seqno && t->len == status->pos 1609 && memcmp(t->tag, ffline, t->len) == 0) { 1610 r = t->r; 1611 break; 1612 } 1613 tpp2 = &t->next; 1614 } 1615 tpp = &r->tree; 1616 } 1617 else { 1618 struct rootrec *r; 1619 1620 r = *rootpp; 1621 if (r == NULL) { 1622 r = xmalloc(sizeof(*r)); 1623 r->next = NULL; 1624 r->tree = ¬_read_yet; /* readdir() not done yet */ 1625 r->fullpat = NULL; /* no ls-R yet */ 1626 *rootpp = r; 1627 r->flags = status->flags | 1628 ((oneatom->flags & F_PCT_S) ? twoatom : oneatom) ->flags; 1629 if (r->flags & F_QUICKFIND) 1630 init_quick_find(r, status->pos, oneatom, status->pos); 1631 else if (status->quickchar != '\0') { 1632 r->flags |= (status->quickchar == 'Q' ? F_QUICKONLY : 0); 1633 /* (init_quick_find() will set F_QUICKFIND in r->flags) */ 1634 init_quick_find(r, status->quickpos, oneatom, status->pos); 1635 } 1636 } 1637 rootpp = &r->next; 1638 tpp = &r->tree; 1639 /* do ls-R search, if appropriate */ 1640 if ((r->flags & F_QUICKFIND) && r->fullpat != NULL) { 1641 unsigned pos; 1642 struct lsr *lp; 1643 1644 pos = xlat(NULL, NULL, status->pos, r->keypat, r->keypat_end); 1645 lp = lsrtab[prehash(ffline + status->pos, pos - status->pos) 1646 % XtNumber(lsrtab)]; 1647 for (;;) { 1648 if (lp == NULL) break; /* no match */ 1649 if (lp->frecp == frecp && lp->seqno == seqno 1650 && lp->keylen == pos - status->pos 1651 && memcmp(lp->key, ffline + status->pos, lp->keylen) == 0) 1652 { /* if match */ 1653 struct statrec stat1; 1654 int len = strlen(lp->value); 1655 1656 stat1 = *status; 1657 if (stat1.pos + len > ffline_len) 1658 expandline(stat1.pos + len); 1659 bcopy(lp->value, ffline + stat1.pos, len); 1660 stat1.pos += len; 1661 (void) xlat(&stat1, &stat1, 0, 1662 r->fullpat, r->fullpat_end); 1663 try_to_open(stat1.pos, stat1.flags); 1664 return; 1665 } 1666 lp = lp->next; 1667 } 1668 /* if there's an ls-R database, and our file is not there, 1669 * then we don't look recursively this time. */ 1670 return; 1671 } 1672 if ((r->flags & (F_QUICKFIND | F_QUICKONLY)) 1673 == (F_QUICKFIND | F_QUICKONLY)) { 1674 return; 1675 } 1676 } 1677 1678 do_tree_search(tpp, oneatom, NULL, status->pos, status->flags); 1679 } 1680 1681 1682 /* 1683 * dostep() - Process a steprec record. 1684 */ 1685 1686 static void 1687 dostep(sp, stat0) 1688 const struct steprec *sp; 1689 const struct statrec *stat0; 1690 { 1691 struct statrec status; 1692 1693 (void) xlat(stat0, &status, 0, sp->str, sp->strend); 1694 1695 status.flags |= sp->flags; 1696 1697 if (sp->atom != NULL) { /* if wild cards or // */ 1698 if (sp->flags & F_PCT_S) { 1699 int i; 1700 1701 for (i = 0; i < lrec.v.pct_s_count; ++i) 1702 begin_tree_search(&status, sp->atom, lrec.v.pct_s_atom[i]); 1703 } 1704 else 1705 begin_tree_search(&status, sp->atom, NULL); 1706 } 1707 else if (sp->nextstep != NULL) { /* if {} list */ 1708 struct steprec *sp1; 1709 1710 for (sp1 = sp->nextstep; sp1 != NULL; sp1 = sp1->next) 1711 dostep(sp1, &status); 1712 } 1713 else { /* end of string */ 1714 if (!(status.flags & F_FILE_USED)) { 1715 (void) xlat(&status, &status, 0, 1716 lrec.no_f_str, lrec.no_f_str_end); 1717 } 1718 try_to_open(status.pos, status.flags); 1719 } 1720 } 1721 1722 1723 /* 1724 * fixbegin() - Handle !!, %t, %S, and ~ at the beginning of a specifier. 1725 */ 1726 1727 static void 1728 fixbegin(sp) 1729 struct steprec *sp; 1730 { 1731 const char *p = sp->str; 1732 1733 /* 1734 * Initial !!. 1735 */ 1736 1737 if (*p == '!' && p[1] == '!' && sp->strend >= p + 2) { 1738 sp->flags |= F_QUICKONLY; 1739 p += 2; 1740 } 1741 1742 /* 1743 * Take care of %S, %t, and ~. 1744 */ 1745 1746 sp->home = NULL; 1747 1748 if (*p == '%') { 1749 if (p[1] == 'S' && sp->strend == p + 2 && sp->atom == NULL 1750 && sp->nextstep == NULL) { /* %S */ 1751 init_texmf(); 1752 sp->flags |= F_PCT_T; 1753 p = "/"; 1754 sp->strend = p + 1; 1755 sp->nextstep = scan_pct_s(); 1756 } 1757 if (p[1] == 't' && sp->strend >= p + 2) { /* %t */ 1758 init_texmf(); 1759 sp->flags |= F_PCT_T; 1760 p += 2; 1761 } 1762 } 1763 else if (*p == '~') 1764 gethome(&p, sp->strend, &sp->home); 1765 1766 sp->str = p; 1767 } 1768 1769 1770 /* 1771 * pathpart() - Handle one (colon-separated) part of the search path. 1772 */ 1773 1774 static const char * 1775 pathpart(p) 1776 const char *p; 1777 { 1778 struct steprec *sp; 1779 static const struct statrec stat_ini = {0, 0, '\0', 0}; 1780 struct statrec status; 1781 1782 sp = *steppp; 1783 if (sp == NULL) { /* do prescanning (lazy evaluation) */ 1784 const char *p_end; 1785 p_end = ff_prescan(p); 1786 /* skip to next colon or end of string */ 1787 for (; *p_end != '\0' && *p_end != ':'; ++p_end) 1788 if (*p_end == '%' && p_end[1] != '\0') 1789 ++p_end; 1790 1791 sp = *steppp; 1792 if (sp->str == sp->strend && sp->nextstep != NULL) { 1793 /* If it's braces right off the bat, then pretend these are */ 1794 /* all different specifiers (so %t, %S, !!, and ~ can work). */ 1795 /* There's a memory leak here, but it's bounded. */ 1796 struct steprec *sp1 = sp->nextstep; 1797 1798 for (;;) { 1799 fixbegin(sp1); 1800 if (sp1->next == NULL) { 1801 sp1->nextpart = p_end; 1802 break; 1803 } 1804 sp1->nextpart = p; 1805 sp1 = sp1->next; 1806 } 1807 *steppp = sp = sp->nextstep; /* relink the chain */ 1808 } 1809 else { 1810 fixbegin(sp); 1811 sp->nextpart = p_end; 1812 } 1813 } 1814 steppp = &sp->next; 1815 1816 if (sp->flags & F_PCT_T) { 1817 struct texmfrec *tp; 1818 1819 for (tp = texmfhead; tp != NULL; tp = tp->next) { 1820 status = stat_ini; 1821 status.flags = tp->flags; 1822 if (tp->home != NULL) { 1823 status.pos = strlen(tp->home); 1824 if (status.pos + tp->len >= ffline_len) 1825 expandline(status.pos + tp->len); 1826 bcopy(tp->home, ffline, status.pos); 1827 } 1828 bcopy(tp->str, ffline + status.pos, tp->len); 1829 status.pos += tp->len; 1830 dostep(sp, &status); 1831 } 1832 } 1833 else { 1834 status = stat_ini; 1835 if (sp->home != NULL) { 1836 unsigned len = strlen(sp->home); 1837 1838 if (len >= ffline_len) 1839 expandline(len); 1840 bcopy(sp->home, ffline, len); 1841 status.pos = len; 1842 } 1843 dostep(sp, &status); 1844 } 1845 1846 return sp->nextpart; 1847 } 1848 1849 #if FFDUMP 1850 1851 /* 1852 * Debugging routine. Dump those crazy step records. 1853 */ 1854 1855 static void 1856 dumpsteps(sp, indent) 1857 const struct steprec *sp; 1858 int indent; 1859 { 1860 const struct steprec *sp1; 1861 int i; 1862 1863 while (sp != NULL) { 1864 for (i = 0; i < indent; ++i) putchar(' '); 1865 printf("%04o `", sp->flags); 1866 fwrite(sp->str, 1, sp->strend - sp->str, stdout); 1867 puts("'"); 1868 1869 if (sp->home != NULL) { 1870 for (i = 0; i < indent; ++i) putchar(' '); 1871 printf(" Home = %s\n", sp->home); 1872 } 1873 1874 /* there's an atomrec dumper in begin_tree_search() */ 1875 1876 dumpsteps(sp->nextstep, indent + 2); 1877 1878 sp = sp->next; 1879 } 1880 } 1881 1882 #endif 1883 1884 1885 /* 1886 * This is the main search routine. It calls pathpath() for each 1887 * component of the path. Substitution for the default path is done here. 1888 */ 1889 1890 FILE * 1891 filefind(name, srchtype, path_ret) 1892 const char *name; /* name of the font or file */ 1893 struct findrec *srchtype; /* what type of search to perform */ 1894 const char **path_ret; /* put the name of the file here */ 1895 { 1896 lrec = *srchtype; 1897 fF_values[0] = fF_values[1] = name; 1898 1899 if (setjmp(got_it) == 0) { 1900 if (*name == '/' || *name == '~') { /* if absolute path */ 1901 unsigned pos; 1902 unsigned pos0 = 0; 1903 const struct passwd *pw = NULL; 1904 1905 if (*name == '~') 1906 pw = ff_getpw(&name, name + strlen(name)); 1907 if (pw != NULL) { 1908 pos0 = strlen(pw->pw_dir); 1909 if (pos0 >= ffline_len) 1910 expandline(pos0); 1911 bcopy(pw->pw_dir, ffline, pos0); 1912 fF_values[0] = fF_values[1] = name; 1913 } 1914 pos = xlat(NULL, NULL, pos0, lrec.abs_str, 1915 lrec.abs_str + strlen(lrec.abs_str)); 1916 try_to_open(pos, 0); 1917 } 1918 else { 1919 const char *p; 1920 1921 steppp = &lrec.v.stephead; 1922 frecp = srchtype; 1923 seqno = 0; 1924 rootpp = &lrec.v.rootp; 1925 1926 for (p = lrec.path1;;) { 1927 if (*p == '\0' || *p == ':') { 1928 /* bring in the default path */ 1929 if (lrec.path2 != NULL) { 1930 const char *p1 = lrec.path2; 1931 1932 for (;;) { 1933 if (*p1 == '\0') break; 1934 if (*p1 == ':') continue; 1935 p1 = pathpart(p1); 1936 if (*p1 == '\0') break; 1937 ++p1; /* skip the colon */ 1938 } 1939 } 1940 } 1941 else 1942 p = pathpart(p); 1943 if (*p == '\0') break; 1944 ++p; /* skip the colon */ 1945 } 1946 1947 file_found = NULL; 1948 } 1949 } 1950 else { 1951 /* it longjmp()s here when it finds the file */ 1952 if (FFDEBUG) puts("--Success--\n"); 1953 if (path_ret != NULL) 1954 *path_ret = xstrdup(ffline); 1955 } 1956 1957 srchtype->v = lrec.v; /* restore volatile parameters of *srchtype */ 1958 1959 #if FFDUMP 1960 dumpsteps(srchtype->v.stephead, 0); 1961 putchar('\n'); 1962 #endif 1963 1964 return file_found; 1965 }