1 #ifndef lint 2 static char *sccsid = "@(#)find.c 4.23 (Berkeley) 05/05/89"; 3 #endif 4 5 #include <sys/param.h> 6 #include <sys/dir.h> 7 #include <sys/stat.h> 8 #include <stdio.h> 9 #include "pathnames.h" 10 11 #define A_DAY 86400L /* a day full of seconds */ 12 #define EQ(x, y) (strcmp(x, y)==0) 13 14 int Randlast; 15 char Pathname[MAXPATHLEN+1]; 16 17 #define MAXNODES 100 18 19 struct anode { 20 int (*F)(); 21 struct anode *L, *R; 22 } Node[MAXNODES]; 23 int Nn; /* number of nodes */ 24 char *Fname; 25 long Now; 26 int Argc, 27 Ai, 28 Pi; 29 char **Argv; 30 /* cpio stuff */ 31 int Cpio; 32 short *Buf, *Dbuf, *Wp; 33 int Bufsize = 5120; 34 int Wct = 2560; 35 36 long Newer; 37 38 int Xdev = 1; /* true if SHOULD cross devices (file systems) */ 39 int follow; /* true if SHOULD descend symlinked directories */ 40 struct stat Devstat; /* stats of each argument path's file system */ 41 42 struct stat Statb; 43 44 struct anode *exp(), 45 *e1(), 46 *e2(), 47 *e3(), 48 *mk(); 49 char *nxtarg(); 50 char Home[MAXPATHLEN + 1]; 51 long Blocks; 52 char *rindex(); 53 char *sbrk(); 54 55 /* 56 * SEE ALSO: code.c, updatedb, bigram.c 57 * Usenix ;login:, Vol 8, No 1, February/March, 1983, p. 8. 58 * 59 * REVISIONS: James A. Woods, Informatics General Corporation, 60 * NASA Ames Research Center, 6/81. 61 * 62 * The second form searches a pre-computed filelist 63 * (constructed nightly by /usr/lib/crontab) which is 64 * compressed by updatedb (v.i.z.) The effect of 65 * find <name> 66 * is similar to 67 * find / +0 -name "*<name>*" -print 68 * but much faster. 69 * 70 * 8/82 faster yet + incorporation of bigram coding -- jaw 71 * 72 * 1/83 incorporate glob-style matching -- jaw 73 */ 74 #define AMES 1 75 76 main(argc, argv) 77 int argc; 78 char *argv[]; 79 { 80 struct anode *exlist; 81 int paths; 82 register char *cp, *sp = 0; 83 #ifdef SUID_PWD 84 FILE *pwd, *popen(); 85 #endif 86 87 #ifdef AMES 88 if (argc < 2) { 89 fprintf(stderr, 90 "Usage: find name, or find path-list predicate-list\n"); 91 exit(1); 92 } 93 if (argc == 2) { 94 fastfind(argv[1]); 95 exit(0); 96 } 97 #endif 98 time(&Now); 99 setpassent(1); 100 setgroupent(1); 101 #ifdef SUID_PWD 102 pwd = popen("pwd", "r"); 103 fgets(Home, sizeof Home, pwd); 104 pclose(pwd); 105 Home[strlen(Home) - 1] = '\0'; 106 #else 107 if (getwd(Home) == NULL) { 108 fprintf(stderr, "find: Can't get current working directory\n"); 109 exit(1); 110 } 111 #endif 112 Argc = argc; Argv = argv; 113 if(argc<3) { 114 usage: fprintf(stderr, "Usage: find path-list predicate-list\n"); 115 exit(1); 116 } 117 for(Ai = paths = 1; Ai < (argc-1); ++Ai, ++paths) 118 if(*Argv[Ai] == '-' || EQ(Argv[Ai], "(") || EQ(Argv[Ai], "!")) 119 break; 120 if(paths == 1) /* no path-list */ 121 goto usage; 122 if(!(exlist = exp())) { /* parse and compile the arguments */ 123 fprintf(stderr, "find: parsing error\n"); 124 exit(1); 125 } 126 if(Ai<argc) { 127 fprintf(stderr, "find: missing conjunction\n"); 128 exit(1); 129 } 130 for(Pi = 1; Pi < paths; ++Pi) { 131 sp = 0; 132 chdir(Home); 133 strcpy(Pathname, Argv[Pi]); 134 if(cp = rindex(Pathname, '/')) { 135 sp = cp + 1; 136 *cp = '\0'; 137 if(chdir(*Pathname? Pathname: "/") == -1) { 138 fprintf(stderr, "find: bad starting directory\n"); 139 exit(2); 140 } 141 *cp = '/'; 142 } 143 Fname = sp? sp: Pathname; 144 if (!Xdev) 145 stat(Pathname, &Devstat); 146 descend(Pathname, Fname, exlist); /* to find files that match */ 147 } 148 if(Cpio) { 149 strcpy(Pathname, "TRAILER!!!"); 150 Statb.st_size = 0; 151 cpio(); 152 printf("%D blocks\n", Blocks*10); 153 } 154 exit(0); 155 } 156 157 /* compile time functions: priority is exp()<e1()<e2()<e3() */ 158 159 struct anode *exp() { /* parse ALTERNATION (-o) */ 160 int or(); 161 register struct anode * p1; 162 163 p1 = e1() /* get left operand */ ; 164 if(EQ(nxtarg(), "-o")) { 165 Randlast--; 166 return(mk(or, p1, exp())); 167 } 168 else if(Ai <= Argc) --Ai; 169 return(p1); 170 } 171 struct anode *e1() { /* parse CONCATENATION (formerly -a) */ 172 int and(); 173 register struct anode * p1; 174 register char *a; 175 176 p1 = e2(); 177 a = nxtarg(); 178 if(EQ(a, "-a")) { 179 And: 180 Randlast--; 181 return(mk(and, p1, e1())); 182 } else if(EQ(a, "(") || EQ(a, "!") || (*a=='-' && !EQ(a, "-o"))) { 183 --Ai; 184 goto And; 185 } else if(Ai <= Argc) --Ai; 186 return(p1); 187 } 188 struct anode *e2() { /* parse NOT (!) */ 189 int not(); 190 191 if(Randlast) { 192 fprintf(stderr, "find: operand follows operand\n"); 193 exit(1); 194 } 195 Randlast++; 196 if(EQ(nxtarg(), "!")) 197 return(mk(not, e3(), (struct anode *)0)); 198 else if(Ai <= Argc) --Ai; 199 return(e3()); 200 } 201 struct anode *e3() { /* parse parens and predicates */ 202 int exeq(), ok(), glob(), mtime(), atime(), user(), 203 group(), size(), perm(), links(), print(), 204 type(), ino(), cpio(), newer(), 205 nouser(), nogroup(), ls(), dummy(); 206 struct anode *p1; 207 int i; 208 register char *a, *b; 209 register int s; 210 211 a = nxtarg(); 212 if(EQ(a, "(")) { 213 Randlast--; 214 p1 = exp(); 215 a = nxtarg(); 216 if(!EQ(a, ")")) goto err; 217 return(p1); 218 } 219 else if(EQ(a, "-print")) { 220 return(mk(print, (struct anode *)0, (struct anode *)0)); 221 } 222 else if (EQ(a, "-nouser")) { 223 return (mk(nouser, (struct anode *)0, (struct anode *)0)); 224 } 225 else if (EQ(a, "-nogroup")) { 226 return (mk(nogroup, (struct anode *)0, (struct anode *)0)); 227 } 228 else if (EQ(a, "-ls")) { 229 return (mk(ls, (struct anode *)0, (struct anode *)0)); 230 } 231 else if (EQ(a, "-xdev")) { 232 Xdev = 0; 233 return (mk(dummy, (struct anode *)0, (struct anode *)0)); 234 } 235 else if (EQ(a, "-follow")) { 236 follow=1; 237 return mk(dummy, (struct anode *)0, (struct anode *)0); 238 } 239 b = nxtarg(); 240 s = *b; 241 if(s=='+') b++; 242 if(EQ(a, "-name")) 243 return(mk(glob, (struct anode *)b, (struct anode *)0)); 244 else if(EQ(a, "-mtime")) 245 return(mk(mtime, (struct anode *)atoi(b), (struct anode *)s)); 246 else if(EQ(a, "-atime")) 247 return(mk(atime, (struct anode *)atoi(b), (struct anode *)s)); 248 else if(EQ(a, "-user")) { 249 if((i=getuid(b)) == -1) { 250 if(gmatch(b, "[0-9]*")) 251 return mk(user, (struct anode *)atoi(b), (struct anode *)s); 252 fprintf(stderr, "find: cannot find -user name\n"); 253 exit(1); 254 } 255 return(mk(user, (struct anode *)i, (struct anode *)s)); 256 } 257 else if(EQ(a, "-inum")) 258 return(mk(ino, (struct anode *)atoi(b), (struct anode *)s)); 259 else if(EQ(a, "-group")) { 260 if((i=getgid(b)) == -1) { 261 if(gmatch(b, "[0-9]*")) 262 return mk(group, (struct anode *)atoi(b), (struct anode *)s); 263 fprintf(stderr, "find: cannot find -group name\n"); 264 exit(1); 265 } 266 return(mk(group, (struct anode *)i, (struct anode *)s)); 267 } else if(EQ(a, "-size")) 268 return(mk(size, (struct anode *)atoi(b), (struct anode *)s)); 269 else if(EQ(a, "-links")) 270 return(mk(links, (struct anode *)atoi(b), (struct anode *)s)); 271 else if(EQ(a, "-perm")) { 272 for(i=0; *b ; ++b) { 273 if(*b=='-') continue; 274 i <<= 3; 275 i = i + (*b - '0'); 276 } 277 return(mk(perm, (struct anode *)i, (struct anode *)s)); 278 } 279 else if(EQ(a, "-type")) { 280 i = s=='d' ? S_IFDIR : 281 s=='b' ? S_IFBLK : 282 s=='c' ? S_IFCHR : 283 s=='f' ? S_IFREG : 284 s=='l' ? S_IFLNK : 285 s=='s' ? S_IFSOCK : 286 0; 287 return(mk(type, (struct anode *)i, (struct anode *)0)); 288 } 289 else if (EQ(a, "-exec")) { 290 i = Ai - 1; 291 while(!EQ(nxtarg(), ";")); 292 return(mk(exeq, (struct anode *)i, (struct anode *)0)); 293 } 294 else if (EQ(a, "-ok")) { 295 i = Ai - 1; 296 while(!EQ(nxtarg(), ";")); 297 return(mk(ok, (struct anode *)i, (struct anode *)0)); 298 } 299 else if(EQ(a, "-cpio")) { 300 if((Cpio = creat(b, 0666)) < 0) { 301 fprintf(stderr, "find: cannot create < %s >\n", s); 302 exit(1); 303 } 304 Buf = (short *)sbrk(512); 305 Wp = Dbuf = (short *)sbrk(5120); 306 return(mk(cpio, (struct anode *)0, (struct anode *)0)); 307 } 308 else if(EQ(a, "-newer")) { 309 if(stat(b, &Statb) < 0) { 310 fprintf(stderr, "find: cannot access < %s >\n", b); 311 exit(1); 312 } 313 Newer = Statb.st_mtime; 314 return mk(newer, (struct anode *)0, (struct anode *)0); 315 } 316 err: fprintf(stderr, "find: bad option < %s >\n", a); 317 exit(1); 318 } 319 struct anode *mk(f, l, r) 320 int (*f)(); 321 struct anode *l, *r; 322 { 323 if (Nn >= MAXNODES) { 324 fprintf(stderr, "find: Too many options\n"); 325 exit(1); 326 } 327 328 Node[Nn].F = f; 329 Node[Nn].L = l; 330 Node[Nn].R = r; 331 return(&(Node[Nn++])); 332 } 333 334 char *nxtarg() { /* get next arg from command line */ 335 static strikes = 0; 336 337 if(strikes==3) { 338 fprintf(stderr, "find: incomplete statement\n"); 339 exit(1); 340 } 341 if(Ai>=Argc) { 342 strikes++; 343 Ai = Argc + 1; 344 return(""); 345 } 346 return(Argv[Ai++]); 347 } 348 349 /* execution time functions */ 350 and(p) 351 register struct anode *p; 352 { 353 return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0); 354 } 355 or(p) 356 register struct anode *p; 357 { 358 return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0); 359 } 360 not(p) 361 register struct anode *p; 362 { 363 return( !((*p->L->F)(p->L))); 364 } 365 glob(p) 366 register struct { int f; char *pat; } *p; 367 { 368 return(gmatch(Fname, p->pat)); 369 } 370 print(p) 371 struct anode *p; 372 { 373 puts(Pathname); 374 return(1); 375 } 376 mtime(p) 377 register struct { int f, t, s; } *p; 378 { 379 return(scomp((int)((Now - Statb.st_mtime) / A_DAY), p->t, p->s)); 380 } 381 atime(p) 382 register struct { int f, t, s; } *p; 383 { 384 return(scomp((int)((Now - Statb.st_atime) / A_DAY), p->t, p->s)); 385 } 386 user(p) 387 register struct { int f, u, s; } *p; 388 { 389 return(scomp(Statb.st_uid, p->u, p->s)); 390 } 391 nouser(p) 392 struct anode *p; 393 { 394 char *getname(); 395 396 return (getname(Statb.st_uid) == NULL); 397 } 398 ino(p) 399 register struct { int f, u, s; } *p; 400 { 401 return(scomp((int)Statb.st_ino, p->u, p->s)); 402 } 403 group(p) 404 register struct { int f, u; } *p; 405 { 406 return(p->u == Statb.st_gid); 407 } 408 nogroup(p) 409 struct anode *p; 410 { 411 char *getgroup(); 412 413 return (getgroup(Statb.st_gid) == NULL); 414 } 415 links(p) 416 register struct { int f, link, s; } *p; 417 { 418 return(scomp(Statb.st_nlink, p->link, p->s)); 419 } 420 size(p) 421 register struct { int f, sz, s; } *p; 422 { 423 return(scomp((int)((Statb.st_size+511)>>9), p->sz, p->s)); 424 } 425 perm(p) 426 register struct { int f, per, s; } *p; 427 { 428 register i; 429 i = (p->s=='-') ? p->per : 07777; /* '-' means only arg bits */ 430 return((Statb.st_mode & i & 07777) == p->per); 431 } 432 type(p) 433 register struct { int f, per, s; } *p; 434 { 435 return((Statb.st_mode&S_IFMT)==p->per); 436 } 437 exeq(p) 438 register struct { int f, com; } *p; 439 { 440 fflush(stdout); /* to flush possible `-print' */ 441 return(doex(p->com)); 442 } 443 ok(p) 444 struct { int f, com; } *p; 445 { 446 char c; int yes; 447 yes = 0; 448 fflush(stdout); /* to flush possible `-print' */ 449 fprintf(stderr, "< %s ... %s > ? ", Argv[p->com], Pathname); 450 fflush(stderr); 451 if((c=getchar())=='y') yes = 1; 452 while(c!='\n') c = getchar(); 453 if(yes) return(doex(p->com)); 454 return(0); 455 } 456 457 #define MKSHORT(v, lv) {U.l=1L;if(U.c[0]) U.l=lv, v[0]=U.s[1], v[1]=U.s[0]; else U.l=lv, v[0]=U.s[0], v[1]=U.s[1];} 458 union { long l; short s[2]; char c[4]; } U; 459 long mklong(v) 460 short v[]; 461 { 462 U.l = 1; 463 if(U.c[0] /* VAX */) 464 U.s[0] = v[1], U.s[1] = v[0]; 465 else 466 U.s[0] = v[0], U.s[1] = v[1]; 467 return U.l; 468 } 469 cpio(p) 470 struct anode *p; 471 { 472 #define MAGIC 070707 473 struct header { 474 short h_magic, 475 h_dev, 476 h_ino, 477 h_mode, 478 h_uid, 479 h_gid, 480 h_nlink, 481 h_rdev; 482 short h_mtime[2]; 483 short h_namesize; 484 short h_filesize[2]; 485 char h_name[256]; 486 } hdr; 487 register ifile, ct; 488 static long fsz; 489 register i; 490 491 hdr.h_magic = MAGIC; 492 strcpy(hdr.h_name, !strncmp(Pathname, "./", 2)? Pathname+2: Pathname); 493 hdr.h_namesize = strlen(hdr.h_name) + 1; 494 hdr.h_uid = Statb.st_uid; 495 hdr.h_gid = Statb.st_gid; 496 hdr.h_dev = Statb.st_dev; 497 hdr.h_ino = Statb.st_ino; 498 hdr.h_mode = Statb.st_mode; 499 MKSHORT(hdr.h_mtime, Statb.st_mtime); 500 hdr.h_nlink = Statb.st_nlink; 501 fsz = hdr.h_mode & S_IFREG? Statb.st_size: 0L; 502 MKSHORT(hdr.h_filesize, fsz); 503 hdr.h_rdev = Statb.st_rdev; 504 if(EQ(hdr.h_name, "TRAILER!!!")) { 505 bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize); 506 for(i = 0; i < 10; ++i) 507 bwrite(Buf, 512); 508 return; 509 } 510 if(!mklong(hdr.h_filesize)) 511 return; 512 if((ifile = open(Fname, 0)) < 0) { 513 cerror: 514 fprintf(stderr, "find: cannot copy < %s >\n", hdr.h_name); 515 return; 516 } 517 bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize); 518 for(fsz = mklong(hdr.h_filesize); fsz > 0; fsz -= 512) { 519 ct = fsz>512? 512: fsz; 520 if(read(ifile, (char *)Buf, ct) < 0) 521 goto cerror; 522 bwrite(Buf, ct); 523 } 524 close(ifile); 525 return; 526 } 527 newer(p) 528 struct anode *p; 529 { 530 return Statb.st_mtime > Newer; 531 } 532 ls(p) 533 struct anode *p; 534 { 535 list(Pathname, &Statb); 536 return (1); 537 } 538 dummy(p) 539 struct anode *p; 540 { 541 /* dummy */ 542 return (1); 543 } 544 545 /* support functions */ 546 scomp(a, b, s) /* funny signed compare */ 547 register a, b; 548 register char s; 549 { 550 if(s == '+') 551 return(a > b); 552 if(s == '-') 553 return(a < (b * -1)); 554 return(a == b); 555 } 556 557 doex(com) 558 { 559 register np; 560 register char *na; 561 static char *nargv[50]; 562 static ccode; 563 register int w, pid, omask; 564 565 ccode = np = 0; 566 while (na=Argv[com++]) { 567 if(np >= sizeof nargv / sizeof *nargv - 1) break; 568 if(strcmp(na, ";")==0) break; 569 if(strcmp(na, "{}")==0) nargv[np++] = Pathname; 570 else nargv[np++] = na; 571 } 572 nargv[np] = 0; 573 if (np==0) return(9); 574 switch (pid = vfork()) { 575 case -1: 576 perror("find: Can't fork"); 577 exit(1); 578 break; 579 580 case 0: 581 chdir(Home); 582 execvp(nargv[0], nargv, np); 583 write(2, "find: Can't execute ", 20); 584 perror(nargv[0]); 585 /* 586 * Kill ourselves; our exit status will be a suicide 587 * note indicating we couldn't do the "exec". 588 */ 589 kill(getpid(), SIGUSR1); 590 break; 591 592 default: 593 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)); 594 while ((w = wait(&ccode)) != pid && w != -1) 595 ; 596 (void) sigsetmask(omask); 597 if ((ccode & 0177) == SIGUSR1) 598 exit(1); 599 return (ccode != 0 ? 0 : 1); 600 } 601 } 602 603 getunum(f, s) char *f, *s; { /* find user/group name and return number */ 604 register i; 605 register char *sp; 606 register c; 607 char str[20]; 608 FILE *pin; 609 610 i = -1; 611 pin = fopen(f, "r"); 612 c = '\n'; /* prime with a CR */ 613 do { 614 if(c=='\n') { 615 sp = str; 616 while((c = *sp++ = getc(pin)) != ':') 617 if(c == EOF) goto RET; 618 *--sp = '\0'; 619 if(EQ(str, s)) { 620 while((c=getc(pin)) != ':') 621 if(c == EOF) goto RET; 622 sp = str; 623 while((*sp = getc(pin)) != ':') sp++; 624 *sp = '\0'; 625 i = atoi(str); 626 goto RET; 627 } 628 } 629 } while((c = getc(pin)) != EOF); 630 RET: 631 fclose(pin); 632 return(i); 633 } 634 635 descend(name, fname, exlist) 636 struct anode *exlist; 637 char *name, *fname; 638 { 639 DIR *dir = NULL; 640 register struct direct *dp; 641 register char *c1; 642 int rv = 0; 643 char *endofname; 644 645 if ((follow?stat(fname, &Statb):lstat(fname, &Statb))<0) { 646 fprintf(stderr, "find: bad status < %s >\n", name); 647 return(0); 648 } 649 (*exlist->F)(exlist); 650 if((Statb.st_mode&S_IFMT)!=S_IFDIR || 651 !Xdev && Devstat.st_dev != Statb.st_dev) 652 return(1); 653 654 if (Statb.st_nlink == 2 && exlist->F == and && 655 exlist->L->F == type && ((int) (exlist->L->L)) == S_IFDIR) 656 return(1); 657 658 for (c1 = name; *c1; ++c1); 659 if (*(c1-1) == '/') 660 --c1; 661 endofname = c1; 662 663 if (chdir(fname) == -1) 664 return(0); 665 if ((dir = opendir(".")) == NULL) { 666 fprintf(stderr, "find: cannot open < %s >\n", name); 667 rv = 0; 668 goto ret; 669 } 670 for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) { 671 if ((dp->d_name[0]=='.' && dp->d_name[1]=='\0') || 672 (dp->d_name[0]=='.' && dp->d_name[1]=='.' && dp->d_name[2]=='\0')) 673 continue; 674 c1 = endofname; 675 *c1++ = '/'; 676 strcpy(c1, dp->d_name); 677 Fname = endofname+1; 678 if(!descend(name, Fname, exlist)) { 679 *endofname = '\0'; 680 chdir(Home); 681 if(chdir(Pathname) == -1) { 682 fprintf(stderr, "find: bad directory tree\n"); 683 exit(1); 684 } 685 } 686 } 687 rv = 1; 688 ret: 689 if(dir) 690 closedir(dir); 691 if(chdir("..") == -1) { 692 *endofname = '\0'; 693 fprintf(stderr, "find: bad directory <%s>\n", name); 694 rv = 1; 695 } 696 return(rv); 697 } 698 699 gmatch(s, p) /* string match as in glob */ 700 register char *s, *p; 701 { 702 if (*s=='.' && *p!='.') return(0); 703 return amatch(s, p); 704 } 705 706 amatch(s, p) 707 register char *s, *p; 708 { 709 register cc; 710 int scc, k; 711 int c, lc; 712 713 scc = *s; 714 lc = 077777; 715 switch (c = *p) { 716 717 case '[': 718 k = 0; 719 while (cc = *++p) { 720 switch (cc) { 721 722 case ']': 723 if (k) 724 return(amatch(++s, ++p)); 725 else 726 return(0); 727 728 case '-': 729 cc = p[1]; 730 k |= lc <= scc && scc <= cc; 731 } 732 if (scc==(lc=cc)) k++; 733 } 734 return(0); 735 736 case '?': 737 caseq: 738 if(scc) return(amatch(++s, ++p)); 739 return(0); 740 case '*': 741 return(umatch(s, ++p)); 742 case 0: 743 return(!scc); 744 } 745 if (c==scc) goto caseq; 746 return(0); 747 } 748 749 umatch(s, p) 750 register char *s, *p; 751 { 752 if(*p==0) return(1); 753 while(*s) 754 if (amatch(s++, p)) return(1); 755 return(0); 756 } 757 758 bwrite(rp, c) 759 register short *rp; 760 register c; 761 { 762 register short *wp = Wp; 763 764 c = (c+1) >> 1; 765 while(c--) { 766 if(!Wct) { 767 again: 768 if(write(Cpio, (char *)Dbuf, Bufsize)<0) { 769 Cpio = chgreel(1, Cpio); 770 goto again; 771 } 772 Wct = Bufsize >> 1; 773 wp = Dbuf; 774 ++Blocks; 775 } 776 *wp++ = *rp++; 777 --Wct; 778 } 779 Wp = wp; 780 } 781 chgreel(x, fl) 782 { 783 register f; 784 char str[22]; 785 FILE *devtty; 786 struct stat statb; 787 extern errno; 788 789 fprintf(stderr, "find: errno: %d, ", errno); 790 fprintf(stderr, "find: can't %s\n", x? "write output": "read input"); 791 fstat(fl, &statb); 792 if((statb.st_mode&S_IFMT) != S_IFCHR) 793 exit(1); 794 again: 795 fprintf(stderr, "If you want to go on, type device/file name %s\n", 796 "when ready"); 797 devtty = fopen(_PATH_TTY, "r"); 798 fgets(str, 20, devtty); 799 str[strlen(str) - 1] = '\0'; 800 if(!*str) 801 exit(1); 802 close(fl); 803 if((f = open(str, x? 1: 0)) < 0) { 804 fprintf(stderr, "That didn't work"); 805 fclose(devtty); 806 goto again; 807 } 808 return f; 809 } 810 811 #ifdef AMES 812 /* 813 * 'fastfind' scans a file list for the full pathname of a file 814 * given only a piece of the name. The list has been processed with 815 * with "front-compression" and bigram coding. Front compression reduces 816 * space by a factor of 4-5, bigram coding by a further 20-25%. 817 * The codes are: 818 * 819 * 0-28 likeliest differential counts + offset to make nonnegative 820 * 30 switch code for out-of-range count to follow in next word 821 * 128-255 bigram codes (128 most common, as determined by 'updatedb') 822 * 32-127 single character (printable) ascii residue (ie, literal) 823 * 824 * A novel two-tiered string search technique is employed: 825 * 826 * First, a metacharacter-free subpattern and partial pathname is 827 * matched BACKWARDS to avoid full expansion of the pathname list. 828 * The time savings is 40-50% over forward matching, which cannot efficiently 829 * handle overlapped search patterns and compressed path residue. 830 * 831 * Then, the actual shell glob-style regular expression (if in this form) 832 * is matched against the candidate pathnames using the slower routines 833 * provided in the standard 'find'. 834 */ 835 836 #include "find.h" 837 838 #define YES 1 839 #define NO 0 840 841 fastfind ( pathpart ) 842 char pathpart[]; 843 { 844 register char *p, *s; 845 register int c; 846 char *q, *index(), *patprep(); 847 int count = 0, found = NO, globflag; 848 FILE *fp, *fopen(); 849 char *patend, *cutoff; 850 char path[MAXPATHLEN]; 851 char bigram1[NBG], bigram2[NBG]; 852 853 if ( (fp = fopen ( _PATH_FCODES, "r" )) == NULL ) { 854 perror( _PATH_FCODES ); 855 exit ( 1 ); 856 } 857 for ( c = 0, p = bigram1, s = bigram2; c < NBG; c++ ) 858 p[c] = getc ( fp ), s[c] = getc ( fp ); 859 860 p = pathpart; 861 globflag = index ( p, '*' ) || index ( p, '?' ) || index ( p, '[' ); 862 patend = patprep ( p ); 863 864 for ( c = getc ( fp ); c != EOF; ) { 865 866 count += ( (c == SWITCH) ? getw ( fp ) : c ) - OFFSET; 867 868 for ( p = path + count; (c = getc ( fp )) > SWITCH; ) /* overlay old path */ 869 if ( c < PARITY ) 870 *p++ = c; 871 else { /* bigrams are parity-marked */ 872 c &= PARITY-1; 873 *p++ = bigram1[c], *p++ = bigram2[c]; 874 } 875 *p-- = NULL; 876 cutoff = ( found ? path : path + count ); 877 878 for ( found = NO, s = p; s >= cutoff; s-- ) 879 if ( *s == *patend ) { /* fast first char check */ 880 for ( p = patend - 1, q = s - 1; *p != NULL; p--, q-- ) 881 if ( *q != *p ) 882 break; 883 if ( *p == NULL ) { /* success on fast match */ 884 found = YES; 885 if ( globflag == NO || amatch ( path, pathpart ) ) 886 puts ( path ); 887 break; 888 } 889 } 890 } 891 } 892 893 /* 894 extract last glob-free subpattern in name for fast pre-match; 895 prepend '\0' for backwards match; return end of new pattern 896 */ 897 static char globfree[100]; 898 899 char * 900 patprep ( name ) 901 char *name; 902 { 903 register char *p, *endmark; 904 register char *subp = globfree; 905 906 *subp++ = '\0'; 907 p = name + strlen ( name ) - 1; 908 /* 909 skip trailing metacharacters (and [] ranges) 910 */ 911 for ( ; p >= name; p-- ) 912 if ( index ( "*?", *p ) == 0 ) 913 break; 914 if ( p < name ) 915 p = name; 916 if ( *p == ']' ) 917 for ( p--; p >= name; p-- ) 918 if ( *p == '[' ) { 919 p--; 920 break; 921 } 922 if ( p < name ) 923 p = name; 924 /* 925 if pattern has only metacharacters, 926 check every path (force '/' search) 927 */ 928 if ( (p == name) && index ( "?*[]", *p ) != 0 ) 929 *subp++ = '/'; 930 else { 931 for ( endmark = p; p >= name; p-- ) 932 if ( index ( "]*?", *p ) != 0 ) 933 break; 934 for ( ++p; (p <= endmark) && subp < (globfree + sizeof ( globfree )); ) 935 *subp++ = *p++; 936 } 937 *subp = '\0'; 938 return ( --subp ); 939 } 940 #endif 941 942 /* rest should be done with nameserver or database */ 943 944 #include <pwd.h> 945 #include <grp.h> 946 #include <utmp.h> 947 948 struct utmp utmp; 949 #define NMAX (sizeof (utmp.ut_name)) 950 #define SCPYN(a, b) strncpy(a, b, NMAX) 951 952 #define NUID 64 953 #define NGID 300 954 955 struct ncache { 956 int uid; 957 char name[NMAX+1]; 958 } nc[NUID]; 959 char outrangename[NMAX+1]; 960 int outrangeuid = -1; 961 char groups[NGID][NMAX+1]; 962 char outrangegroup[NMAX+1]; 963 int outrangegid = -1; 964 965 /* 966 * This function assumes that the password file is hashed 967 * (or some such) to allow fast access based on a name key. 968 * If this isn't true, duplicate the code for getgroup(). 969 */ 970 char * 971 getname(uid) 972 { 973 register struct passwd *pw; 974 struct passwd *getpwent(); 975 register int cp; 976 977 #if (((NUID) & ((NUID) - 1)) != 0) 978 cp = uid % (NUID); 979 #else 980 cp = uid & ((NUID) - 1); 981 #endif 982 if (uid >= 0 && nc[cp].uid == uid && nc[cp].name[0]) 983 return (nc[cp].name); 984 pw = getpwuid(uid); 985 if (!pw) 986 return (0); 987 nc[cp].uid = uid; 988 SCPYN(nc[cp].name, pw->pw_name); 989 return (nc[cp].name); 990 } 991 992 char * 993 getgroup(gid) 994 { 995 register struct group *gr; 996 static init; 997 struct group *getgrent(); 998 999 if (gid >= 0 && gid < NGID && groups[gid][0]) 1000 return (&groups[gid][0]); 1001 if (gid >= 0 && gid == outrangegid) 1002 return (outrangegroup); 1003 rescan: 1004 if (init == 2) { 1005 if (gid < NGID) 1006 return (0); 1007 setgrent(); 1008 while (gr = getgrent()) { 1009 if (gr->gr_gid != gid) 1010 continue; 1011 outrangegid = gr->gr_gid; 1012 SCPYN(outrangegroup, gr->gr_name); 1013 endgrent(); 1014 return (outrangegroup); 1015 } 1016 endgrent(); 1017 return (0); 1018 } 1019 if (init == 0) 1020 setgrent(), init = 1; 1021 while (gr = getgrent()) { 1022 if (gr->gr_gid < 0 || gr->gr_gid >= NGID) { 1023 if (gr->gr_gid == gid) { 1024 outrangegid = gr->gr_gid; 1025 SCPYN(outrangegroup, gr->gr_name); 1026 return (outrangegroup); 1027 } 1028 continue; 1029 } 1030 if (groups[gr->gr_gid][0]) 1031 continue; 1032 SCPYN(groups[gr->gr_gid], gr->gr_name); 1033 if (gr->gr_gid == gid) 1034 return (&groups[gid][0]); 1035 } 1036 init = 2; 1037 goto rescan; 1038 } 1039 1040 int 1041 getuid(username) 1042 char *username; 1043 { 1044 register struct passwd *pw; 1045 struct passwd *getpwnam(); 1046 1047 pw = getpwnam(username); 1048 if (pw != NULL) 1049 return (pw->pw_uid); 1050 else 1051 return (-1); 1052 } 1053 1054 int 1055 getgid(groupname) 1056 char *groupname; 1057 { 1058 register struct group *gr; 1059 struct group *getgrnam(); 1060 1061 gr = getgrnam(groupname); 1062 if (gr != NULL) 1063 return (gr->gr_gid); 1064 else 1065 return (-1); 1066 } 1067 1068 #define permoffset(who) ((who) * 3) 1069 #define permission(who, type) ((type) >> permoffset(who)) 1070 #define kbytes(bytes) (((bytes) + 1023) / 1024) 1071 1072 list(file, stp) 1073 char *file; 1074 register struct stat *stp; 1075 { 1076 char pmode[32], uname[32], gname[32], fsize[32], ftime[32]; 1077 char *getname(), *getgroup(), *ctime(); 1078 static long special[] = { S_ISUID, 's', S_ISGID, 's', S_ISVTX, 't' }; 1079 static time_t sixmonthsago = -1; 1080 #ifdef S_IFLNK 1081 char flink[MAXPATHLEN + 1]; 1082 #endif 1083 register int who; 1084 register char *cp; 1085 time_t now; 1086 1087 if (file == NULL || stp == NULL) 1088 return (-1); 1089 1090 time(&now); 1091 if (sixmonthsago == -1) 1092 sixmonthsago = now - 6L*30L*24L*60L*60L; 1093 1094 switch (stp->st_mode & S_IFMT) { 1095 #ifdef S_IFDIR 1096 case S_IFDIR: /* directory */ 1097 pmode[0] = 'd'; 1098 break; 1099 #endif 1100 #ifdef S_IFCHR 1101 case S_IFCHR: /* character special */ 1102 pmode[0] = 'c'; 1103 break; 1104 #endif 1105 #ifdef S_IFBLK 1106 case S_IFBLK: /* block special */ 1107 pmode[0] = 'b'; 1108 break; 1109 #endif 1110 #ifdef S_IFLNK 1111 case S_IFLNK: /* symbolic link */ 1112 pmode[0] = 'l'; 1113 break; 1114 #endif 1115 #ifdef S_IFSOCK 1116 case S_IFSOCK: /* socket */ 1117 pmode[0] = 's'; 1118 break; 1119 #endif 1120 #ifdef S_IFREG 1121 case S_IFREG: /* regular */ 1122 #endif 1123 default: 1124 pmode[0] = '-'; 1125 break; 1126 } 1127 1128 for (who = 0; who < 3; who++) { 1129 if (stp->st_mode & permission(who, S_IREAD)) 1130 pmode[permoffset(who) + 1] = 'r'; 1131 else 1132 pmode[permoffset(who) + 1] = '-'; 1133 1134 if (stp->st_mode & permission(who, S_IWRITE)) 1135 pmode[permoffset(who) + 2] = 'w'; 1136 else 1137 pmode[permoffset(who) + 2] = '-'; 1138 1139 if (stp->st_mode & special[who * 2]) 1140 pmode[permoffset(who) + 3] = special[who * 2 + 1]; 1141 else if (stp->st_mode & permission(who, S_IEXEC)) 1142 pmode[permoffset(who) + 3] = 'x'; 1143 else 1144 pmode[permoffset(who) + 3] = '-'; 1145 } 1146 pmode[permoffset(who) + 1] = '\0'; 1147 1148 cp = getname(stp->st_uid); 1149 if (cp != NULL) 1150 sprintf(uname, "%-9.9s", cp); 1151 else 1152 sprintf(uname, "%-9d", stp->st_uid); 1153 1154 cp = getgroup(stp->st_gid); 1155 if (cp != NULL) 1156 sprintf(gname, "%-9.9s", cp); 1157 else 1158 sprintf(gname, "%-9d", stp->st_gid); 1159 1160 if (pmode[0] == 'b' || pmode[0] == 'c') 1161 sprintf(fsize, "%3d,%4d", 1162 major(stp->st_rdev), minor(stp->st_rdev)); 1163 else { 1164 sprintf(fsize, "%8ld", stp->st_size); 1165 #ifdef S_IFLNK 1166 if (pmode[0] == 'l') { 1167 /* 1168 * Need to get the tail of the file name, since we have 1169 * already chdir()ed into the directory of the file 1170 */ 1171 cp = rindex(file, '/'); 1172 if (cp == NULL) 1173 cp = file; 1174 else 1175 cp++; 1176 who = readlink(cp, flink, sizeof flink - 1); 1177 if (who >= 0) 1178 flink[who] = '\0'; 1179 else 1180 flink[0] = '\0'; 1181 } 1182 #endif 1183 } 1184 1185 cp = ctime(&stp->st_mtime); 1186 if (stp->st_mtime < sixmonthsago || stp->st_mtime > now) 1187 sprintf(ftime, "%-7.7s %-4.4s", cp + 4, cp + 20); 1188 else 1189 sprintf(ftime, "%-12.12s", cp + 4); 1190 1191 printf("%5lu %4ld %s %2d %s%s%s %s %s%s%s\n", 1192 stp->st_ino, /* inode # */ 1193 #ifdef S_IFSOCK 1194 (long) kbytes(dbtob(stp->st_blocks)), /* kbytes */ 1195 #else 1196 (long) kbytes(stp->st_size), /* kbytes */ 1197 #endif 1198 pmode, /* protection */ 1199 stp->st_nlink, /* # of links */ 1200 uname, /* owner */ 1201 gname, /* group */ 1202 fsize, /* # of bytes */ 1203 ftime, /* modify time */ 1204 file, /* name */ 1205 #ifdef S_IFLNK 1206 (pmode[0] == 'l') ? " -> " : "", 1207 (pmode[0] == 'l') ? flink : "" /* symlink */ 1208 #else 1209 "", 1210 "" 1211 #endif 1212 ); 1213 1214 return (0); 1215 } 1216