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