1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2010 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * Phong Vo <kpv@research.att.com> * 20 * * 21 ***********************************************************************/ 22 #pragma prototyped 23 /* 24 * original code 25 * 26 * James A. Woods, Informatics General Corporation, 27 * NASA Ames Research Center, 6/81. 28 * Usenix ;login:, February/March, 1983, p. 8. 29 * 30 * discipline/method interface 31 * 32 * Glenn Fowler 33 * AT&T Research 34 * modified from the original BSD source 35 * 36 * 'fastfind' scans a file list for the full pathname of a file 37 * given only a piece of the name. The list is processed with 38 * with "front-compression" and bigram coding. Front compression reduces 39 * space by a factor of 4-5, bigram coding by a further 20-25%. 40 * 41 * there are 4 methods: 42 * 43 * FF_old original with 7 bit bigram encoding (no magic) 44 * FF_gnu 8 bit clean front compression (FF_gnu_magic) 45 * FF_dir FF_gnu with sfgetl/sfputl and trailing / on dirs (FF_dir_magic) 46 * FF_typ FF_dir with (mime) types (FF_typ_magic) 47 * 48 * the bigram encoding steals the eighth bit (that's why its FF_old) 49 * maybe one day we'll limit it to readonly: 50 * 51 * 0-2*FF_OFF likeliest differential counts + offset to make nonnegative 52 * FF_ESC 4 byte big-endian out-of-range count+FF_OFF follows 53 * FF_MIN-FF_MAX ascii residue 54 * >=FF_MAX bigram codes 55 * 56 * a two-tiered string search technique is employed 57 * 58 * a metacharacter-free subpattern and partial pathname is matched 59 * backwards to avoid full expansion of the pathname list 60 * 61 * then the actual shell glob-style regular expression (if in this form) 62 * is matched against the candidate pathnames using the slower regexec() 63 * 64 * The original BSD code is covered by the BSD license: 65 * 66 * Copyright (c) 1985, 1993, 1999 67 * The Regents of the University of California. All rights reserved. 68 * 69 * Redistribution and use in source and binary forms, with or without 70 * modification, are permitted provided that the following conditions 71 * are met: 72 * 1. Redistributions of source code must retain the above copyright 73 * notice, this list of conditions and the following disclaimer. 74 * 2. Redistributions in binary form must reproduce the above copyright 75 * notice, this list of conditions and the following disclaimer in the 76 * documentation and/or other materials provided with the distribution. 77 * 3. Neither the name of the University nor the names of its contributors 78 * may be used to endorse or promote products derived from this software 79 * without specific prior written permission. 80 * 81 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 82 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 83 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 84 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 85 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 86 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 87 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 88 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 89 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 90 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 91 * SUCH DAMAGE. 92 */ 93 94 static const char id[] = "\n@(#)$Id: fastfind (AT&T Research) 2002-10-02 $\0\n"; 95 96 static const char lib[] = "libast:fastfind"; 97 98 #include "findlib.h" 99 100 #define FIND_MATCH "*/(find|locate)/*" 101 102 /* 103 * this db could be anywhere 104 * findcodes[] directories are checked for findnames[i] 105 */ 106 107 static char* findcodes[] = 108 { 109 0, 110 0, 111 FIND_CODES, 112 "/usr/local/share/lib", 113 "/usr/local/lib", 114 "/usr/share/lib", 115 "/usr/lib", 116 "/var/spool", 117 "/usr/local/var", 118 "/var/lib", 119 "/var/lib/slocate", 120 "/var/db", 121 }; 122 123 static char* findnames[] = 124 { 125 "find/codes", 126 "find/find.codes", 127 "locate/locatedb", 128 "locatedb", 129 "locate.database", 130 "slocate.db", 131 }; 132 133 /* 134 * convert t to lower case and drop leading x- and x- after / 135 * converted value copied to b of size n 136 */ 137 138 char* 139 typefix(char* buf, size_t n, register const char* t) 140 { 141 register int c; 142 register char* b = buf; 143 144 if ((*t == 'x' || *t == 'X') && *(t + 1) == '-') 145 t += 2; 146 while (c = *t++) 147 { 148 if (isupper(c)) 149 c = tolower(c); 150 if ((*b++ = c) == '/' && (*t == 'x' || *t == 'X') && *(t + 1) == '-') 151 t += 2; 152 } 153 *b = 0; 154 return buf; 155 } 156 157 /* 158 * return a fastfind stream handle for pattern 159 */ 160 161 Find_t* 162 findopen(const char* file, const char* pattern, const char* type, Finddisc_t* disc) 163 { 164 register Find_t* fp; 165 register char* p; 166 register char* s; 167 register char* b; 168 register int i; 169 register int j; 170 char* path; 171 int brace = 0; 172 int paren = 0; 173 int k; 174 int q; 175 int fd; 176 int uid; 177 Vmalloc_t* vm; 178 Type_t* tp; 179 struct stat st; 180 181 182 if (!(vm = vmopen(Vmdcheap, Vmbest, 0))) 183 goto nospace; 184 185 /* 186 * NOTE: searching for FIND_CODES would be much simpler if we 187 * just stuck with our own, but we also support GNU 188 * locate codes and have to search for the one of a 189 * bazillion possible names for that file 190 */ 191 192 if (!findcodes[1]) 193 findcodes[1] = getenv(FIND_CODES_ENV); 194 if (disc->flags & FIND_GENERATE) 195 { 196 if (!(fp = (Find_t*)vmnewof(vm, 0, Find_t, 1, sizeof(Encode_t) - sizeof(Code_t)))) 197 goto nospace; 198 fp->vm = vm; 199 fp->id = lib; 200 fp->disc = disc; 201 fp->generate = 1; 202 if (file && (!*file || streq(file, "-"))) 203 file = 0; 204 uid = geteuid(); 205 j = (findcodes[0] = (char*)file) && *file == '/' ? 1 : elementsof(findcodes); 206 207 /* 208 * look for the codes file, but since it may not exist yet, 209 * also look for the containing directory if i<2 or if 210 * it is sufficiently qualified (FIND_MATCH) 211 */ 212 213 for (i = 0; i < j; i++) 214 if (path = findcodes[i]) 215 { 216 if (*path == '/') 217 { 218 if (!stat(path, &st)) 219 { 220 if (S_ISDIR(st.st_mode)) 221 { 222 for (k = 0; k < elementsof(findnames); k++) 223 { 224 sfsprintf(fp->encode.file, sizeof(fp->encode.file), "%s/%s", path, findnames[k]); 225 if (!eaccess(fp->encode.file, R_OK|W_OK)) 226 { 227 path = fp->encode.file; 228 break; 229 } 230 if (strchr(findnames[k], '/') && (b = strrchr(fp->encode.file, '/'))) 231 { 232 *b = 0; 233 if (!stat(fp->encode.file, &st) && st.st_uid == uid && (st.st_mode & S_IWUSR)) 234 { 235 *b = '/'; 236 path = fp->encode.file; 237 break; 238 } 239 } 240 } 241 if (k < elementsof(findnames)) 242 break; 243 } 244 else if (st.st_uid == uid && (st.st_mode & S_IWUSR)) 245 { 246 sfsprintf(fp->encode.file, sizeof(fp->encode.file), "%s", path); 247 path = fp->encode.file; 248 break; 249 } 250 } 251 else if (i < 2 || strmatch(path, FIND_MATCH)) 252 { 253 sfsprintf(fp->encode.file, sizeof(fp->encode.file), "%s", path); 254 if (b = strrchr(fp->encode.file, '/')) 255 { 256 *b = 0; 257 if (!stat(fp->encode.file, &st) && st.st_uid == uid && (st.st_mode & S_IWUSR)) 258 { 259 *b = '/'; 260 path = fp->encode.file; 261 break; 262 } 263 } 264 } 265 } 266 else if (pathpath(fp->encode.file, path, "", PATH_REGULAR|PATH_READ|PATH_WRITE)) 267 { 268 path = fp->encode.file; 269 break; 270 } 271 else if (b = strrchr(path, '/')) 272 { 273 sfsprintf(fp->encode.file, sizeof(fp->encode.file), "%-.*s", b - path, path); 274 if (pathpath(fp->encode.temp, fp->encode.file, "", PATH_EXECUTE|PATH_READ|PATH_WRITE) && 275 !stat(fp->encode.temp, &st) && st.st_uid == uid && (st.st_mode & S_IWUSR)) 276 { 277 sfsprintf(fp->encode.file, sizeof(fp->encode.file), "%s%s", fp->encode.temp, b); 278 path = fp->encode.file; 279 break; 280 } 281 } 282 } 283 if (i >= j) 284 { 285 if (fp->disc->errorf) 286 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: cannot locate codes", file ? file : findcodes[2]); 287 goto drop; 288 } 289 if (fp->disc->flags & FIND_OLD) 290 { 291 /* 292 * FF_old generates temp data that is read 293 * in a second pass to generate the real codes 294 */ 295 296 fp->method = FF_old; 297 if (!(fp->fp = sftmp(32 * PATH_MAX))) 298 { 299 if (fp->disc->errorf) 300 (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "cannot create tmp file"); 301 goto drop; 302 } 303 } 304 else 305 { 306 /* 307 * the rest generate into a temp file that 308 * is simply renamed on completion 309 */ 310 311 if (s = strrchr(path, '/')) 312 { 313 *s = 0; 314 p = path; 315 } 316 else 317 p = "."; 318 if (!pathtemp(fp->encode.temp, sizeof(fp->encode.temp), p, "ff", &fd)) 319 { 320 if (fp->disc->errorf) 321 (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "%s: cannot create tmp file in this directory", p ? p : "."); 322 goto drop; 323 } 324 if (s) 325 *s = '/'; 326 if (!(fp->fp = sfnew(NiL, NiL, (size_t)SF_UNBOUND, fd, SF_WRITE))) 327 { 328 if (fp->disc->errorf) 329 (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "%s: cannot open tmp file", fp->encode.temp); 330 close(fd); 331 goto drop; 332 } 333 if (fp->disc->flags & FIND_TYPE) 334 { 335 fp->method = FF_typ; 336 fp->encode.namedisc.key = offsetof(Type_t, name); 337 fp->encode.namedisc.link = offsetof(Type_t, byname); 338 fp->encode.indexdisc.key = offsetof(Type_t, index); 339 fp->encode.indexdisc.size = sizeof(unsigned long); 340 fp->encode.indexdisc.link = offsetof(Type_t, byindex); 341 s = "system/dir"; 342 if (!(fp->encode.namedict = dtopen(&fp->encode.namedisc, Dttree)) || !(fp->encode.indexdict = dtopen(&fp->encode.indexdisc, Dttree)) || !(tp = newof(0, Type_t, 1, strlen(s) + 1))) 343 { 344 if (fp->encode.namedict) 345 dtclose(fp->encode.namedict); 346 if (fp->disc->errorf) 347 (*fp->disc->errorf)(fp, fp->disc, 2, "cannot allocate type table"); 348 goto drop; 349 } 350 351 /* 352 * type index 1 is always system/dir 353 */ 354 355 tp->index = ++fp->types; 356 strcpy(tp->name, s); 357 dtinsert(fp->encode.namedict, tp); 358 dtinsert(fp->encode.indexdict, tp); 359 } 360 else if (fp->disc->flags & FIND_GNU) 361 { 362 fp->method = FF_gnu; 363 sfputc(fp->fp, 0); 364 sfputr(fp->fp, FF_gnu_magic, 0); 365 } 366 else 367 { 368 fp->method = FF_dir; 369 sfputc(fp->fp, 0); 370 sfputr(fp->fp, FF_dir_magic, 0); 371 } 372 } 373 } 374 else 375 { 376 i = sizeof(Decode_t) + sizeof(Code_t); 377 if (!pattern || !*pattern) 378 pattern = "*"; 379 i += (j = 2 * (strlen(pattern) + 1)); 380 if (!(fp = (Find_t*)vmnewof(vm, 0, Find_t, 1, i))) 381 { 382 vmclose(vm); 383 return 0; 384 } 385 fp->vm = vm; 386 fp->id = lib; 387 fp->disc = disc; 388 if (disc->flags & FIND_ICASE) 389 fp->decode.ignorecase = 1; 390 j = (findcodes[0] = (char*)file) && *file == '/' ? 1 : elementsof(findcodes); 391 for (i = 0; i < j; i++) 392 if (path = findcodes[i]) 393 { 394 if (*path == '/') 395 { 396 if (!stat(path, &st)) 397 { 398 if (S_ISDIR(st.st_mode)) 399 { 400 for (k = 0; k < elementsof(findnames); k++) 401 { 402 sfsprintf(fp->decode.path, sizeof(fp->decode.path), "%s/%s", path, findnames[k]); 403 if (fp->fp = sfopen(NiL, fp->decode.path, "r")) 404 { 405 path = fp->decode.path; 406 break; 407 } 408 } 409 if (fp->fp) 410 break; 411 } 412 else if (fp->fp = sfopen(NiL, path, "r")) 413 break; 414 } 415 } 416 else if ((path = pathpath(fp->decode.path, path, "", PATH_REGULAR|PATH_READ)) && (fp->fp = sfopen(NiL, path, "r"))) 417 break; 418 } 419 if (!fp->fp) 420 { 421 if (fp->disc->errorf) 422 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: cannot locate codes", file ? file : findcodes[2]); 423 goto drop; 424 } 425 if (fstat(sffileno(fp->fp), &st)) 426 { 427 if (fp->disc->errorf) 428 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: cannot stat codes", path); 429 goto drop; 430 } 431 if (fp->secure = ((st.st_mode & (S_IRGRP|S_IROTH)) == S_IRGRP) && st.st_gid == getegid() && getegid() != getgid()) 432 setgid(getgid()); 433 fp->stamp = st.st_mtime; 434 b = (s = fp->decode.temp) + 1; 435 for (i = 0; i < elementsof(fp->decode.bigram1); i++) 436 { 437 if ((j = sfgetc(fp->fp)) == EOF) 438 goto invalid; 439 if (!(*s++ = fp->decode.bigram1[i] = j) && i) 440 { 441 i = -i; 442 break; 443 } 444 if ((j = sfgetc(fp->fp)) == EOF) 445 goto invalid; 446 if (!(*s++ = fp->decode.bigram2[i] = j) && (i || fp->decode.bigram1[0] >= '0' && fp->decode.bigram1[0] <= '1')) 447 break; 448 } 449 if (streq(b, FF_typ_magic)) 450 { 451 if (type) 452 { 453 type = (const char*)typefix(fp->decode.bigram2, sizeof(fp->decode.bigram2), type); 454 memset(fp->decode.bigram1, 0, sizeof(fp->decode.bigram1)); 455 } 456 fp->method = FF_typ; 457 for (j = 0, i = 1;; i++) 458 { 459 if (!(s = sfgetr(fp->fp, 0, 0))) 460 goto invalid; 461 if (!*s) 462 break; 463 if (type && strmatch(s, type)) 464 { 465 FF_SET_TYPE(fp, i); 466 j++; 467 } 468 } 469 if (type && !j) 470 goto drop; 471 fp->types = j; 472 } 473 else if (streq(b, FF_dir_magic)) 474 fp->method = FF_dir; 475 else if (streq(b, FF_gnu_magic)) 476 fp->method = FF_gnu; 477 else if (!*b && *--b >= '0' && *b <= '1') 478 { 479 fp->method = FF_gnu; 480 while (j = sfgetc(fp->fp)) 481 { 482 if (j == EOF || fp->decode.count >= sizeof(fp->decode.path)) 483 goto invalid; 484 fp->decode.path[fp->decode.count++] = j; 485 } 486 } 487 else 488 { 489 fp->method = FF_old; 490 if (i < 0) 491 { 492 if ((j = sfgetc(fp->fp)) == EOF) 493 goto invalid; 494 fp->decode.bigram2[i = -i] = j; 495 } 496 while (++i < elementsof(fp->decode.bigram1)) 497 { 498 if ((j = sfgetc(fp->fp)) == EOF) 499 goto invalid; 500 fp->decode.bigram1[i] = j; 501 if ((j = sfgetc(fp->fp)) == EOF) 502 goto invalid; 503 fp->decode.bigram2[i] = j; 504 } 505 if ((fp->decode.peek = sfgetc(fp->fp)) != FF_OFF) 506 goto invalid; 507 } 508 509 /* 510 * set up the physical dir table 511 */ 512 513 if (disc->version >= 19980301L) 514 { 515 fp->verifyf = disc->verifyf; 516 if (disc->dirs && *disc->dirs) 517 { 518 for (k = 0; disc->dirs[k]; k++); 519 if (k == 1 && streq(disc->dirs[0], "/")) 520 k = 0; 521 if (k) 522 { 523 if (!(fp->dirs = vmnewof(fp->vm, 0, char*, 2 * k + 1, 0))) 524 goto drop; 525 if (!(fp->lens = vmnewof(fp->vm, 0, int, 2 * k, 0))) 526 goto drop; 527 p = 0; 528 b = fp->decode.temp; 529 j = fp->method == FF_old || fp->method == FF_gnu; 530 531 /* 532 * fill the dir list with logical and 533 * physical names since we don't know 534 * which way the db was encoded (it 535 * could be *both* ways) 536 */ 537 538 for (i = q = 0; i < k; i++) 539 { 540 if (*(s = disc->dirs[i]) == '/') 541 sfsprintf(b, sizeof(fp->decode.temp) - 1, "%s", s); 542 else if (!p && !(p = getcwd(fp->decode.path, sizeof(fp->decode.path)))) 543 goto nospace; 544 else 545 sfsprintf(b, sizeof(fp->decode.temp) - 1, "%s/%s", p, s); 546 s = pathcanon(b, 0); 547 *s = '/'; 548 *(s + 1) = 0; 549 if (!(fp->dirs[q] = vmstrdup(fp->vm, b))) 550 goto nospace; 551 if (j) 552 (fp->dirs[q])[s - b] = 0; 553 q++; 554 *s = 0; 555 s = pathcanon(b, PATH_PHYSICAL); 556 *s = '/'; 557 *(s + 1) = 0; 558 if (!strneq(b, fp->dirs[q - 1], s - b)) 559 { 560 if (!(fp->dirs[q] = vmstrdup(fp->vm, b))) 561 goto nospace; 562 if (j) 563 (fp->dirs[q])[s - b] = 0; 564 q++; 565 } 566 } 567 strsort(fp->dirs, q, strcasecmp); 568 for (i = 0; i < q; i++) 569 fp->lens[i] = strlen(fp->dirs[i]); 570 } 571 } 572 } 573 if (fp->verifyf || (disc->flags & FIND_VERIFY)) 574 { 575 if (fp->method != FF_dir && fp->method != FF_typ) 576 { 577 if (fp->disc->errorf) 578 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: %s code format does not support directory verification", path, fp->method == FF_gnu ? FF_gnu_magic : "OLD-BIGRAM"); 579 goto drop; 580 } 581 fp->verify = 1; 582 } 583 584 /* 585 * extract last glob-free subpattern in name for fast pre-match 586 * prepend 0 for backwards match 587 */ 588 589 if (p = s = (char*)pattern) 590 { 591 b = fp->decode.pattern; 592 for (;;) 593 { 594 switch (*b++ = *p++) 595 { 596 case 0: 597 break; 598 case '\\': 599 s = p; 600 if (!*p++) 601 break; 602 continue; 603 case '[': 604 if (!brace) 605 { 606 brace++; 607 if (*p == ']') 608 p++; 609 } 610 continue; 611 case ']': 612 if (brace) 613 { 614 brace--; 615 s = p; 616 } 617 continue; 618 case '(': 619 if (!brace) 620 paren++; 621 continue; 622 case ')': 623 if (!brace && paren > 0 && !--paren) 624 s = p; 625 continue; 626 case '|': 627 case '&': 628 if (!brace && !paren) 629 { 630 s = ""; 631 break; 632 } 633 continue; 634 case '*': 635 case '?': 636 s = p; 637 continue; 638 default: 639 continue; 640 } 641 break; 642 } 643 if (s != pattern && !streq(pattern, "*")) 644 { 645 fp->decode.match = 1; 646 if (i = regcomp(&fp->decode.re, pattern, REG_SHELL|REG_AUGMENTED|(fp->decode.ignorecase?REG_ICASE:0))) 647 { 648 if (disc->errorf) 649 { 650 regerror(i, &fp->decode.re, fp->decode.temp, sizeof(fp->decode.temp)); 651 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: %s", pattern, fp->decode.temp); 652 } 653 goto drop; 654 } 655 } 656 if (*s) 657 { 658 *b++ = 0; 659 while (i = *s++) 660 *b++ = i; 661 *b-- = 0; 662 fp->decode.end = b; 663 if (fp->decode.ignorecase) 664 for (s = fp->decode.pattern; s <= b; s++) 665 if (isupper(*s)) 666 *s = tolower(*s); 667 } 668 } 669 } 670 return fp; 671 nospace: 672 if (disc->errorf) 673 (*fp->disc->errorf)(fp, fp->disc, 2, "out of space"); 674 if (!vm) 675 return 0; 676 if (!fp) 677 { 678 vmclose(vm); 679 return 0; 680 } 681 goto drop; 682 invalid: 683 if (fp->disc->errorf) 684 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: invalid codes", path); 685 drop: 686 if (!fp->generate && fp->decode.match) 687 regfree(&fp->decode.re); 688 if (fp->fp) 689 sfclose(fp->fp); 690 vmclose(fp->vm); 691 return 0; 692 } 693 694 /* 695 * return the next fastfind path 696 * 0 returned when list exhausted 697 */ 698 699 char* 700 findread(register Find_t* fp) 701 { 702 register char* p; 703 register char* q; 704 register char* s; 705 register char* b; 706 register char* e; 707 register int c; 708 register int n; 709 register int m; 710 int ignorecase; 711 int t; 712 unsigned char w[4]; 713 struct stat st; 714 715 if (fp->generate) 716 return 0; 717 if (fp->decode.restore) 718 { 719 *fp->decode.restore = '/'; 720 fp->decode.restore = 0; 721 } 722 ignorecase = fp->decode.ignorecase ? STR_ICASE : 0; 723 c = fp->decode.peek; 724 next: 725 for (;;) 726 { 727 switch (fp->method) 728 { 729 case FF_dir: 730 t = 0; 731 n = sfgetl(fp->fp); 732 goto grab; 733 case FF_gnu: 734 if ((c = sfgetc(fp->fp)) == EOF) 735 return 0; 736 if (c == 0x80) 737 { 738 if ((c = sfgetc(fp->fp)) == EOF) 739 return 0; 740 n = c << 8; 741 if ((c = sfgetc(fp->fp)) == EOF) 742 return 0; 743 n |= c; 744 if (n & 0x8000) 745 n = (n - 0xffff) - 1; 746 } 747 else if ((n = c) & 0x80) 748 n = (n - 0xff) - 1; 749 t = 0; 750 goto grab; 751 case FF_typ: 752 t = sfgetu(fp->fp); 753 n = sfgetl(fp->fp); 754 grab: 755 p = fp->decode.path + (fp->decode.count += n); 756 do 757 { 758 if ((c = sfgetc(fp->fp)) == EOF) 759 return 0; 760 } while (*p++ = c); 761 p -= 2; 762 break; 763 case FF_old: 764 if (c == EOF) 765 { 766 fp->decode.peek = c; 767 return 0; 768 } 769 if (c == FF_ESC) 770 { 771 if (sfread(fp->fp, w, sizeof(w)) != sizeof(w)) 772 return 0; 773 if (fp->decode.swap >= 0) 774 { 775 c = (int32_t)((w[0] << 24) | (w[1] << 16) | (w[2] << 8) | w[3]); 776 if (!fp->decode.swap) 777 { 778 /* 779 * the old format uses machine 780 * byte order; this test uses 781 * the smallest magnitude of 782 * both byte orders on the 783 * first encoded path motion 784 * to determine the original 785 * byte order 786 */ 787 788 m = c; 789 if (m < 0) 790 m = -m; 791 n = (int32_t)((w[3] << 24) | (w[2] << 16) | (w[1] << 8) | w[0]); 792 if (n < 0) 793 n = -n; 794 if (m < n) 795 fp->decode.swap = 1; 796 else 797 { 798 fp->decode.swap = -1; 799 c = (int32_t)((w[3] << 24) | (w[2] << 16) | (w[1] << 8) | w[0]); 800 } 801 } 802 } 803 else 804 c = (int32_t)((w[3] << 24) | (w[2] << 16) | (w[1] << 8) | w[0]); 805 } 806 fp->decode.count += c - FF_OFF; 807 for (p = fp->decode.path + fp->decode.count; (c = sfgetc(fp->fp)) > FF_ESC;) 808 if (c & (1<<(CHAR_BIT-1))) 809 { 810 *p++ = fp->decode.bigram1[c & ((1<<(CHAR_BIT-1))-1)]; 811 *p++ = fp->decode.bigram2[c & ((1<<(CHAR_BIT-1))-1)]; 812 } 813 else 814 *p++ = c; 815 *p-- = 0; 816 t = 0; 817 break; 818 } 819 b = fp->decode.path; 820 if (fp->decode.found) 821 fp->decode.found = 0; 822 else 823 b += fp->decode.count; 824 if (fp->dirs) 825 for (;;) 826 { 827 if (!*fp->dirs) 828 return 0; 829 830 /* 831 * use the ordering and lengths to prune 832 * comparison function calls 833 * (*fp->dirs)[*fp->lens]=='/' if its 834 * already been matched 835 */ 836 837 if ((n = p - fp->decode.path + 1) > (m = *fp->lens)) 838 { 839 if (!(*fp->dirs)[m]) 840 goto next; 841 if (!strncasecmp(*fp->dirs, fp->decode.path, m)) 842 break; 843 } 844 else if (n == m) 845 { 846 if (!(*fp->dirs)[m]) 847 { 848 if (!(n = strcasecmp(*fp->dirs, fp->decode.path)) && (ignorecase || !strcmp(*fp->dirs, fp->decode.path))) 849 { 850 if (m > 0) 851 { 852 (*fp->dirs)[m] = '/'; 853 if ((*fp->dirs)[m - 1] != '/') 854 (*fp->dirs)[++(*fp->lens)] = '/'; 855 } 856 break; 857 } 858 if (n >= 0) 859 goto next; 860 } 861 } 862 else if (!(*fp->dirs)[m]) 863 goto next; 864 fp->dirs++; 865 fp->lens++; 866 } 867 if (fp->verify && (*p == '/' || t == 1)) 868 { 869 if ((n = p - fp->decode.path)) 870 *p = 0; 871 else 872 n = 1; 873 if (fp->verifyf) 874 n = (*fp->verifyf)(fp, fp->decode.path, n, fp->disc); 875 else if (stat(fp->decode.path, &st)) 876 n = -1; 877 else if ((unsigned long)st.st_mtime > fp->stamp) 878 n = 1; 879 else 880 n = 0; 881 *p = '/'; 882 883 /* 884 * n<0 skip this subtree 885 * n==0 keep as is 886 * n>0 read this dir now 887 */ 888 889 /* NOT IMPLEMENTED YET */ 890 } 891 if (FF_OK_TYPE(fp, t)) 892 { 893 if (fp->decode.end) 894 { 895 if (*(s = p) == '/') 896 s--; 897 if (*fp->decode.pattern == '/' && b > fp->decode.path) 898 b--; 899 for (; s >= b; s--) 900 if (*s == *fp->decode.end || ignorecase && tolower(*s) == *fp->decode.end) 901 { 902 if (ignorecase) 903 for (e = fp->decode.end - 1, q = s - 1; *e && (*q == *e || tolower(*q) == *e); e--, q--); 904 else 905 for (e = fp->decode.end - 1, q = s - 1; *e && *q == *e; e--, q--); 906 if (!*e) 907 { 908 fp->decode.found = 1; 909 if (!fp->decode.match || strgrpmatch(fp->decode.path, fp->decode.pattern, NiL, 0, STR_MAXIMAL|STR_LEFT|STR_RIGHT|ignorecase)) 910 { 911 fp->decode.peek = c; 912 if (*p == '/') 913 *(fp->decode.restore = p) = 0; 914 if (!fp->secure || !access(fp->decode.path, F_OK)) 915 return fp->decode.path; 916 } 917 break; 918 } 919 } 920 } 921 else if (!fp->decode.match || !(n = regexec(&fp->decode.re, fp->decode.path, 0, NiL, 0))) 922 { 923 fp->decode.peek = c; 924 if (*p == '/' && p > fp->decode.path) 925 *(fp->decode.restore = p) = 0; 926 if (!fp->secure || !access(fp->decode.path, F_OK)) 927 return fp->decode.path; 928 } 929 else if (n != REG_NOMATCH) 930 { 931 if (fp->disc->errorf) 932 { 933 regerror(n, &fp->decode.re, fp->decode.temp, sizeof(fp->decode.temp)); 934 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: %s", fp->decode.pattern, fp->decode.temp); 935 } 936 return 0; 937 } 938 } 939 } 940 } 941 942 /* 943 * add path to the code table 944 * paths are assumed to be in sort order 945 */ 946 947 int 948 findwrite(register Find_t* fp, const char* path, size_t len, const char* type) 949 { 950 register unsigned char* s; 951 register unsigned char* e; 952 register unsigned char* p; 953 register int n; 954 register int d; 955 register Type_t* x; 956 register unsigned long u; 957 958 if (!fp->generate) 959 return -1; 960 if (type && fp->method == FF_dir) 961 { 962 len = sfsprintf(fp->encode.mark, sizeof(fp->encode.mark), "%-.*s/", len, path); 963 path = fp->encode.mark; 964 } 965 s = (unsigned char*)path; 966 if (len <= 0) 967 len = strlen(path); 968 if (len < sizeof(fp->encode.path)) 969 e = s + len++; 970 else 971 { 972 len = sizeof(fp->encode.path) - 1; 973 e = s + len; 974 } 975 p = (unsigned char*)fp->encode.path; 976 while (s < e) 977 { 978 if (*s != *p++) 979 break; 980 s++; 981 } 982 n = s - (unsigned char*)path; 983 switch (fp->method) 984 { 985 case FF_gnu: 986 d = n - fp->encode.prefix; 987 if (d >= -127 && d <= 127) 988 sfputc(fp->fp, d & 0xff); 989 else 990 { 991 sfputc(fp->fp, 0x80); 992 sfputc(fp->fp, (d >> 8) & 0xff); 993 sfputc(fp->fp, d & 0xff); 994 } 995 fp->encode.prefix = n; 996 sfputr(fp->fp, (char*)s, 0); 997 break; 998 case FF_old: 999 sfprintf(fp->fp, "%ld", n - fp->encode.prefix + FF_OFF); 1000 fp->encode.prefix = n; 1001 sfputc(fp->fp, ' '); 1002 p = s; 1003 while (s < e) 1004 { 1005 n = *s++; 1006 if (s >= e) 1007 break; 1008 fp->encode.code[n][*s++]++; 1009 } 1010 while (p < e) 1011 { 1012 if ((n = *p++) < FF_MIN || n >= FF_MAX) 1013 n = '?'; 1014 sfputc(fp->fp, n); 1015 } 1016 sfputc(fp->fp, 0); 1017 break; 1018 case FF_typ: 1019 if (type) 1020 { 1021 type = (const char*)typefix((char*)fp->encode.bigram, sizeof(fp->encode.bigram), type); 1022 if (x = (Type_t*)dtmatch(fp->encode.namedict, type)) 1023 u = x->index; 1024 else if (!(x = newof(0, Type_t, 1, strlen(type) + 1))) 1025 u = 0; 1026 else 1027 { 1028 u = x->index = ++fp->types; 1029 strcpy(x->name, type); 1030 dtinsert(fp->encode.namedict, x); 1031 dtinsert(fp->encode.indexdict, x); 1032 } 1033 } 1034 else 1035 u = 0; 1036 sfputu(fp->fp, u); 1037 /*FALLTHROUGH...*/ 1038 case FF_dir: 1039 d = n - fp->encode.prefix; 1040 sfputl(fp->fp, d); 1041 fp->encode.prefix = n; 1042 sfputr(fp->fp, (char*)s, 0); 1043 break; 1044 } 1045 memcpy(fp->encode.path, path, len); 1046 return 0; 1047 } 1048 1049 /* 1050 * findsync() helper 1051 */ 1052 1053 static int 1054 finddone(register Find_t* fp) 1055 { 1056 int r; 1057 1058 if (sfsync(fp->fp)) 1059 { 1060 if (fp->disc->errorf) 1061 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: write error [sfsync]", fp->encode.file); 1062 return -1; 1063 } 1064 if (sferror(fp->fp)) 1065 { 1066 if (fp->disc->errorf) 1067 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: write error [sferror]", fp->encode.file); 1068 return -1; 1069 } 1070 r = sfclose(fp->fp); 1071 fp->fp = 0; 1072 if (r) 1073 { 1074 if (fp->disc->errorf) 1075 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: write error [sfclose]", fp->encode.file); 1076 return -1; 1077 } 1078 return 0; 1079 } 1080 1081 /* 1082 * finish the code table 1083 */ 1084 1085 static int 1086 findsync(register Find_t* fp) 1087 { 1088 register char* s; 1089 register int n; 1090 register int m; 1091 register int d; 1092 register Type_t* x; 1093 char* t; 1094 int b; 1095 long z; 1096 Sfio_t* sp; 1097 1098 switch (fp->method) 1099 { 1100 case FF_dir: 1101 case FF_gnu: 1102 /* 1103 * replace the real file with the temp file 1104 */ 1105 1106 if (finddone(fp)) 1107 goto bad; 1108 remove(fp->encode.file); 1109 if (rename(fp->encode.temp, fp->encode.file)) 1110 { 1111 if (fp->disc->errorf) 1112 (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "%s: cannot rename from tmp file %s", fp->encode.file, fp->encode.temp); 1113 remove(fp->encode.temp); 1114 return -1; 1115 } 1116 break; 1117 case FF_old: 1118 /* 1119 * determine the top FF_MAX bigrams 1120 */ 1121 1122 for (n = 0; n < FF_MAX; n++) 1123 for (m = 0; m < FF_MAX; m++) 1124 fp->encode.hits[fp->encode.code[n][m]]++; 1125 fp->encode.hits[0] = 0; 1126 m = 1; 1127 for (n = USHRT_MAX; n >= 0; n--) 1128 if (d = fp->encode.hits[n]) 1129 { 1130 fp->encode.hits[n] = m; 1131 if ((m += d) > FF_MAX) 1132 break; 1133 } 1134 while (--n >= 0) 1135 fp->encode.hits[n] = 0; 1136 for (n = FF_MAX - 1; n >= 0; n--) 1137 for (m = FF_MAX - 1; m >= 0; m--) 1138 if (fp->encode.hits[fp->encode.code[n][m]]) 1139 { 1140 d = fp->encode.code[n][m]; 1141 b = fp->encode.hits[d] - 1; 1142 fp->encode.code[n][m] = b + FF_MAX; 1143 if (fp->encode.hits[d]++ >= FF_MAX) 1144 fp->encode.hits[d] = 0; 1145 fp->encode.bigram[b *= 2] = n; 1146 fp->encode.bigram[b + 1] = m; 1147 } 1148 else 1149 fp->encode.code[n][m] = 0; 1150 1151 /* 1152 * commit the real file 1153 */ 1154 1155 if (sfseek(fp->fp, (Sfoff_t)0, SEEK_SET)) 1156 { 1157 if (fp->disc->errorf) 1158 (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "cannot rewind tmp file"); 1159 return -1; 1160 } 1161 if (!(sp = sfopen(NiL, fp->encode.file, "w"))) 1162 goto badcreate; 1163 1164 /* 1165 * dump the bigrams 1166 */ 1167 1168 sfwrite(sp, fp->encode.bigram, sizeof(fp->encode.bigram)); 1169 1170 /* 1171 * encode the massaged paths 1172 */ 1173 1174 while (s = sfgetr(fp->fp, 0, 0)) 1175 { 1176 z = strtol(s, &t, 0); 1177 s = t; 1178 if (z < 0 || z > 2 * FF_OFF) 1179 { 1180 sfputc(sp, FF_ESC); 1181 sfputc(sp, (z >> 24)); 1182 sfputc(sp, (z >> 16)); 1183 sfputc(sp, (z >> 8)); 1184 sfputc(sp, z); 1185 } 1186 else 1187 sfputc(sp, z); 1188 while (n = *s++) 1189 { 1190 if (!(m = *s++)) 1191 { 1192 sfputc(sp, n); 1193 break; 1194 } 1195 if (d = fp->encode.code[n][m]) 1196 sfputc(sp, d); 1197 else 1198 { 1199 sfputc(sp, n); 1200 sfputc(sp, m); 1201 } 1202 } 1203 } 1204 sfclose(fp->fp); 1205 fp->fp = sp; 1206 if (finddone(fp)) 1207 goto bad; 1208 break; 1209 case FF_typ: 1210 if (finddone(fp)) 1211 goto bad; 1212 if (!(fp->fp = sfopen(NiL, fp->encode.temp, "r"))) 1213 { 1214 if (fp->disc->errorf) 1215 (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "%s: cannot read tmp file", fp->encode.temp); 1216 remove(fp->encode.temp); 1217 return -1; 1218 } 1219 1220 /* 1221 * commit the output file 1222 */ 1223 1224 if (!(sp = sfopen(NiL, fp->encode.file, "w"))) 1225 goto badcreate; 1226 1227 /* 1228 * write the header magic 1229 */ 1230 1231 sfputc(sp, 0); 1232 sfputr(sp, FF_typ_magic, 0); 1233 1234 /* 1235 * write the type table in index order starting with 1 1236 */ 1237 1238 for (x = (Type_t*)dtfirst(fp->encode.indexdict); x; x = (Type_t*)dtnext(fp->encode.indexdict, x)) 1239 sfputr(sp, x->name, 0); 1240 sfputc(sp, 0); 1241 1242 /* 1243 * append the front compressed strings 1244 */ 1245 1246 if (sfmove(fp->fp, sp, SF_UNBOUND, -1) < 0 || !sfeof(fp->fp)) 1247 { 1248 sfclose(sp); 1249 if (fp->disc->errorf) 1250 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: cannot append codes", fp->encode.file); 1251 goto bad; 1252 } 1253 sfclose(fp->fp); 1254 fp->fp = sp; 1255 if (finddone(fp)) 1256 goto bad; 1257 remove(fp->encode.temp); 1258 break; 1259 } 1260 return 0; 1261 badcreate: 1262 if (fp->disc->errorf) 1263 (*fp->disc->errorf)(fp, fp->disc, 2, "%s: cannot write codes", fp->encode.file); 1264 bad: 1265 if (fp->fp) 1266 { 1267 sfclose(fp->fp); 1268 fp->fp = 0; 1269 } 1270 remove(fp->encode.temp); 1271 return -1; 1272 } 1273 1274 /* 1275 * close an open fastfind stream 1276 */ 1277 1278 int 1279 findclose(register Find_t* fp) 1280 { 1281 int n = 0; 1282 1283 if (!fp) 1284 return -1; 1285 if (fp->generate) 1286 { 1287 n = findsync(fp); 1288 if (fp->encode.indexdict) 1289 dtclose(fp->encode.indexdict); 1290 if (fp->encode.namedict) 1291 dtclose(fp->encode.namedict); 1292 } 1293 else 1294 { 1295 if (fp->decode.match) 1296 regfree(&fp->decode.re); 1297 n = 0; 1298 } 1299 if (fp->fp) 1300 sfclose(fp->fp); 1301 vmclose(fp->vm); 1302 return n; 1303 } 1304