1: #include <stdio.h>
2: #include <sys/param.h>
3: #include <sys/dir.h>
4: #include <sys/stat.h>
5: #include <sys/file.h>
6:
7: #define A_DAY 86400L /* a day full of seconds */
8: #define EQ(x, y) (strcmp(x, y)==0)
9:
10: int Randlast;
11: char Pathname[200];
12:
13: struct anode {
14: int (*F)();
15: struct anode *L, *R;
16: } Node[100];
17: int Nn; /* number of nodes */
18: char *Fname;
19: long Now;
20: int Argc,
21: Ai,
22: Pi;
23: char **Argv;
24: /* cpio stuff */
25: int Cpio;
26: short *Buf, *Dbuf, *Wp;
27: int Bufsize = 5120;
28: int Wct = 2560;
29:
30: long Newer;
31:
32: struct stat Statb;
33:
34: struct anode *exp(),
35: *e1(),
36: *e2(),
37: *e3(),
38: *mk();
39: char *nxtarg();
40: char Home[128];
41: long Blocks;
42: char *rindex();
43: char *sbrk();
44: main(argc, argv) char *argv[];
45: {
46: struct anode *exlist;
47: int paths;
48: register char *cp, *sp = 0;
49: FILE *pwd, *popen();
50:
51: time(&Now);
52: pwd = popen("pwd", "r");
53: fgets(Home, 128, pwd);
54: pclose(pwd);
55: Home[strlen(Home) - 1] = '\0';
56: Argc = argc; Argv = argv;
57: if(argc<3) {
58: usage:
59: pr("Usage: find path-list predicate-list\n");
60: exit(1);
61: }
62: for(Ai = paths = 1; Ai < (argc-1); ++Ai, ++paths)
63: if(*Argv[Ai] == '-' || EQ(Argv[Ai], "(") || EQ(Argv[Ai], "!"))
64: break;
65: if(paths == 1) /* no path-list */
66: goto usage;
67: if(!(exlist = exp())) { /* parse and compile the arguments */
68: pr("find: parsing error\n");
69: exit(1);
70: }
71: if(Ai<argc) {
72: pr("find: missing conjunction\n");
73: exit(1);
74: }
75: for(Pi = 1; Pi < paths; ++Pi) {
76: sp = 0;
77: chdir(Home);
78: strcpy(Pathname, Argv[Pi]);
79: if(cp = rindex(Pathname, '/')) {
80: sp = cp + 1;
81: *cp = '\0';
82: if(chdir(*Pathname? Pathname: "/") == -1) {
83: perror(*Pathname? Pathname: "/");
84: exit(2);
85: }
86: *cp = '/';
87: }
88: Fname = sp? sp: Pathname;
89: descend(Pathname, Fname, exlist); /* to find files that match */
90: }
91: if(Cpio) {
92: strcpy(Pathname, "TRAILER!!!");
93: Statb.st_size = 0;
94: cpio();
95: }
96: exit(0);
97: }
98:
99: /* compile time functions: priority is exp()<e1()<e2()<e3() */
100:
101: struct anode *exp() { /* parse ALTERNATION (-o) */
102: int or();
103: register struct anode * p1;
104:
105: p1 = e1() /* get left operand */ ;
106: if(EQ(nxtarg(), "-o")) {
107: Randlast--;
108: return(mk(or, p1, exp()));
109: }
110: else if(Ai <= Argc) --Ai;
111: return(p1);
112: }
113: struct anode *e1() { /* parse CONCATENATION (formerly -a) */
114: int and();
115: register struct anode * p1;
116: register char *a;
117:
118: p1 = e2();
119: a = nxtarg();
120: if(EQ(a, "-a")) {
121: And:
122: Randlast--;
123: return(mk(and, p1, e1()));
124: } else if(EQ(a, "(") || EQ(a, "!") || (*a=='-' && !EQ(a, "-o"))) {
125: --Ai;
126: goto And;
127: } else if(Ai <= Argc) --Ai;
128: return(p1);
129: }
130: struct anode *e2() { /* parse NOT (!) */
131: int not();
132:
133: if(Randlast) {
134: pr("find: operand follows operand\n");
135: exit(1);
136: }
137: Randlast++;
138: if(EQ(nxtarg(), "!"))
139: return(mk(not, e3(), (struct anode *)0));
140: else if(Ai <= Argc) --Ai;
141: return(e3());
142: }
143: struct anode *e3() { /* parse parens and predicates */
144: int exeq(), ok(), glob(), mtime(), atime(), ctime(), user(),
145: group(), size(), perm(), links(), print(),
146: type(), ino(), cpio(), newer();
147: struct anode *p1;
148: int i;
149: register char *a, *b, s;
150:
151: a = nxtarg();
152: if(EQ(a, "(")) {
153: Randlast--;
154: p1 = exp();
155: a = nxtarg();
156: if(!EQ(a, ")")) goto err;
157: return(p1);
158: }
159: else if(EQ(a, "-print")) {
160: return(mk(print, (struct anode *)0, (struct anode *)0));
161: }
162: b = nxtarg();
163: s = *b;
164: if(s=='+') b++;
165: if(EQ(a, "-name"))
166: return(mk(glob, (struct anode *)b, (struct anode *)0));
167: else if(EQ(a, "-mtime"))
168: return(mk(mtime, (struct anode *)atoi(b), (struct anode *)s));
169: else if(EQ(a, "-atime"))
170: return(mk(atime, (struct anode *)atoi(b), (struct anode *)s));
171: else if(EQ(a, "-ctime"))
172: return(mk(ctime, (struct anode *)atoi(b), (struct anode *)s));
173: else if(EQ(a, "-user")) {
174: if((i=getunum("/etc/passwd", b)) == -1) {
175: if(gmatch(b, "[0-9][0-9][0-9]*")
176: || gmatch(b, "[0-9][0-9]")
177: || gmatch(b, "[0-9]"))
178: return mk(user, (struct anode *)atoi(b), (struct anode *)s);
179: pr("find: cannot find -user name\n");
180: exit(1);
181: }
182: return(mk(user, (struct anode *)i, (struct anode *)s));
183: }
184: else if(EQ(a, "-inum"))
185: return(mk(ino, (struct anode *)atoi(b), (struct anode *)s));
186: else if(EQ(a, "-group")) {
187: if((i=getunum("/etc/group", b)) == -1) {
188: if(gmatch(b, "[0-9][0-9][0-9]*")
189: || gmatch(b, "[0-9][0-9]")
190: || gmatch(b, "[0-9]"))
191: return mk(group, (struct anode *)atoi(b), (struct anode *)s);
192: pr("find: cannot find -group name\n");
193: exit(1);
194: }
195: return(mk(group, (struct anode *)i, (struct anode *)s));
196: } else if(EQ(a, "-size"))
197: return(mk(size, (struct anode *)atoi(b), (struct anode *)s));
198: else if(EQ(a, "-links"))
199: return(mk(links, (struct anode *)atoi(b), (struct anode *)s));
200: else if(EQ(a, "-perm")) {
201: for(i=0; *b ; ++b) {
202: if(*b=='-') continue;
203: i <<= 3;
204: i = i + (*b - '0');
205: }
206: return(mk(perm, (struct anode *)i, (struct anode *)s));
207: }
208: else if(EQ(a, "-type")) {
209: i = s=='d' ? S_IFDIR :
210: s=='b' ? S_IFBLK :
211: s=='c' ? S_IFCHR :
212: s=='l' ? S_IFLNK :
213: s=='q' ? S_IFQUOT :
214: s=='f' ? 0100000 :
215: 0;
216: return(mk(type, (struct anode *)i, (struct anode *)0));
217: }
218: else if (EQ(a, "-exec")) {
219: i = Ai - 1;
220: while(!EQ(nxtarg(), ";"));
221: return(mk(exeq, (struct anode *)i, (struct anode *)0));
222: }
223: else if (EQ(a, "-ok")) {
224: i = Ai - 1;
225: while(!EQ(nxtarg(), ";"));
226: return(mk(ok, (struct anode *)i, (struct anode *)0));
227: }
228: else if(EQ(a, "-cpio")) {
229: if((Cpio = creat(b, 0666)) < 0) {
230: perror(s);
231: exit(1);
232: }
233: Buf = (short *)sbrk(512);
234: Wp = Dbuf = (short *)sbrk(5120);
235: return(mk(cpio, (struct anode *)0, (struct anode *)0));
236: }
237: else if(EQ(a, "-newer")) {
238: if(stat(b, &Statb) < 0) {
239: perror(b);
240: exit(1);
241: }
242: Newer = Statb.st_mtime;
243: return mk(newer, (struct anode *)0, (struct anode *)0);
244: }
245: err: pr("find: bad option "), pr(a), pr("\n");
246: exit(1);
247: }
248: struct anode *mk(f, l, r)
249: int (*f)();
250: struct anode *l, *r;
251: {
252: Node[Nn].F = f;
253: Node[Nn].L = l;
254: Node[Nn].R = r;
255: return(&(Node[Nn++]));
256: }
257:
258: char *nxtarg() { /* get next arg from command line */
259: static strikes = 0;
260:
261: if(strikes==3) {
262: pr("find: incomplete statement\n");
263: exit(1);
264: }
265: if(Ai>=Argc) {
266: strikes++;
267: Ai = Argc + 1;
268: return("");
269: }
270: return(Argv[Ai++]);
271: }
272:
273: /* execution time functions */
274: and(p)
275: register struct anode *p;
276: {
277: return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0);
278: }
279: or(p)
280: register struct anode *p;
281: {
282: return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0);
283: }
284: not(p)
285: register struct anode *p;
286: {
287: return( !((*p->L->F)(p->L)));
288: }
289: glob(p)
290: register struct { int f; char *pat; } *p;
291: {
292: return(gmatch(Fname, p->pat));
293: }
294: print()
295: {
296: puts(Pathname);
297: return(1);
298: }
299: mtime(p)
300: register struct { int f, t, s; } *p;
301: {
302: return(scomp((int)((Now - Statb.st_mtime) / A_DAY), p->t, p->s));
303: }
304: atime(p)
305: register struct { int f, t, s; } *p;
306: {
307: return(scomp((int)((Now - Statb.st_atime) / A_DAY), p->t, p->s));
308: }
309: ctime(p)
310: register struct { int f, t, s; } *p;
311: {
312: return(scomp((int)((Now - Statb.st_ctime) / A_DAY), p->t, p->s));
313: }
314: user(p)
315: register struct { int f, u, s; } *p;
316: {
317: return(scomp(Statb.st_uid, p->u, p->s));
318: }
319: ino(p)
320: register struct { int f, u, s; } *p;
321: {
322: return(scomp((int)Statb.st_ino, p->u, p->s));
323: }
324: group(p)
325: register struct { int f, u; } *p;
326: {
327: return(p->u == Statb.st_gid);
328: }
329: links(p)
330: register struct { int f, link, s; } *p;
331: {
332: return(scomp(Statb.st_nlink, p->link, p->s));
333: }
334: size(p)
335: register struct { int f, sz, s; } *p;
336: {
337: return(scomp((int)((Statb.st_size + 1023) / 1024), p->sz, p->s));
338: }
339: perm(p)
340: register struct { int f, per, s; } *p;
341: {
342: register i;
343: i = (p->s=='-') ? p->per : 07777; /* '-' means only arg bits */
344: return((Statb.st_mode & i & 07777) == p->per);
345: }
346: type(p)
347: register struct { int f, per, s; } *p;
348: {
349: return((Statb.st_mode&S_IFMT)==p->per);
350: }
351: exeq(p)
352: register struct { int f, com; } *p;
353: {
354: fflush(stdout); /* to flush possible `-print' */
355: return(doex(p->com));
356: }
357: ok(p)
358: struct { int f, com; } *p;
359: {
360: int c; int yes;
361: yes = 0;
362: fflush(stdout); /* to flush possible `-print' */
363: pr("< "), pr(Argv[p->com]), pr(" ... "), pr(Pathname), pr(" >? ");
364: fflush(stderr);
365: if((c=getchar())=='y') yes = 1;
366: while(c!='\n')
367: if(c==EOF)
368: exit(2);
369: else
370: c = getchar();
371: if(yes) return(doex(p->com));
372: return(0);
373: }
374:
375: #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];}
376: union { long l; short s[2]; char c[4]; } U;
377: long mklong(v)
378: short v[];
379: {
380: U.l = 1;
381: if(U.c[0] /* VAX */)
382: U.s[0] = v[1], U.s[1] = v[0];
383: else
384: U.s[0] = v[0], U.s[1] = v[1];
385: return U.l;
386: }
387: cpio()
388: {
389: #define MAGIC 070707
390: struct {
391: short h_magic,
392: h_dev,
393: h_ino,
394: h_mode,
395: h_uid,
396: h_gid,
397: h_nlink,
398: h_rdev;
399: short h_mtime[2];
400: short h_namesize;
401: short h_filesize[2];
402: char h_name[256];
403: } hdr;
404: register ifile, ct;
405: static long fsz;
406: register i;
407:
408: hdr.h_magic = MAGIC;
409: strcpy(hdr.h_name, !strncmp(Pathname, "./", 2)? Pathname+2: Pathname);
410: hdr.h_namesize = strlen(hdr.h_name) + 1;
411: hdr.h_uid = Statb.st_uid;
412: hdr.h_gid = Statb.st_gid;
413: hdr.h_dev = Statb.st_dev;
414: hdr.h_ino = Statb.st_ino;
415: hdr.h_mode = Statb.st_mode;
416: MKSHORT(hdr.h_mtime, Statb.st_mtime);
417: hdr.h_nlink = Statb.st_nlink;
418: fsz = hdr.h_mode & S_IFREG? Statb.st_size: 0L;
419: MKSHORT(hdr.h_filesize, fsz);
420: hdr.h_rdev = Statb.st_rdev;
421: if(EQ(hdr.h_name, "TRAILER!!!")) {
422: bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
423: for(i = 0; i < 10; ++i)
424: bwrite(Buf, 512);
425: return;
426: }
427: if(!mklong(hdr.h_filesize)) {
428: bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
429: return;
430: }
431: if((ifile = open(Fname, FATT_RDONLY)) < 0) {
432: cerror:
433: perror(hdr.h_name);
434: return;
435: }
436: bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
437: for(fsz = mklong(hdr.h_filesize); fsz > 0; fsz -= 512) {
438: ct = fsz>512? 512: fsz;
439: if(read(ifile, (char *)Buf, ct) < 0)
440: goto cerror;
441: bwrite(Buf, ct);
442: }
443: close(ifile);
444: return;
445: }
446: newer()
447: {
448: return Statb.st_mtime > Newer;
449: }
450:
451: /* support functions */
452: scomp(a, b, s) /* funny signed compare */
453: register a, b;
454: register char s;
455: {
456: if(s == '+')
457: return(a > b);
458: if(s == '-')
459: return(a < (b * -1));
460: return(a == b);
461: }
462:
463: doex(com)
464: {
465: register np;
466: register char *na;
467: static char *nargv[50];
468: static ccode;
469:
470: ccode = np = 0;
471: while (na=Argv[com++]) {
472: if(strcmp(na, ";")==0) break;
473: if(strcmp(na, "{}")==0) nargv[np++] = Pathname;
474: else nargv[np++] = na;
475: }
476: nargv[np] = 0;
477: if (np==0) return(9);
478: if(fork()) /*parent*/ wait(&ccode);
479: else { /*child*/
480: chdir(Home);
481: execvp(nargv[0], nargv, np);
482: exit(1);
483: }
484: return(ccode ? 0:1);
485: }
486:
487: getunum(f, s) char *f, *s; { /* find user/group name and return number */
488: register i;
489: register char *sp;
490: register c;
491: char str[20];
492: FILE *pin;
493:
494: i = -1;
495: pin = fopen(f, "r");
496: c = '\n'; /* prime with a CR */
497: do {
498: if(c=='\n') {
499: sp = str;
500: while((c = *sp++ = getc(pin)) != ':')
501: if(c == EOF) goto RET;
502: *--sp = '\0';
503: if(EQ(str, s)) {
504: while((c=getc(pin)) != ':')
505: if(c == EOF) goto RET;
506: sp = str;
507: while((*sp = getc(pin)) != ':') sp++;
508: *sp = '\0';
509: i = atoi(str);
510: goto RET;
511: }
512: }
513: } while((c = getc(pin)) != EOF);
514: RET:
515: fclose(pin);
516: return(i);
517: }
518:
519: descend(name, fname, exlist)
520: struct anode *exlist;
521: char *name, *fname;
522: {
523: int dir = 0, /* open directory */
524: dsize,
525: entries,
526: dirsize;
527: off_t offset;
528: struct direct dentry[BSIZE / sizeof (struct direct)];
529: register struct direct *dp;
530: register char *c1, *c2;
531: int i;
532: int rv = 0;
533: char *endofname;
534:
535: #ifdef UCB_SYMLINKS
536: if(lstat(fname, &Statb)<0)
537: #else
538: if(stat(fname, &Statb)<0)
539: #endif
540: {
541: perror(name);
542: return(0);
543: }
544: (*exlist->F)(exlist);
545: if((Statb.st_mode&S_IFMT)!=S_IFDIR)
546: return(1);
547:
548: for(c1 = name; *c1; ++c1);
549: if(*(c1-1) == '/')
550: --c1;
551: endofname = c1;
552: dirsize = Statb.st_size;
553:
554: if(chdir(fname) == -1)
555: return(0);
556: for(offset=0 ; offset < dirsize ; offset += BSIZE) { /* each block */
557: dsize = BSIZE<(dirsize-offset)? BSIZE: (dirsize-offset);
558: if(!dir) {
559: if((dir=open(".", FATT_RDONLY))<0) {
560: perror(name);
561: rv = 0;
562: goto ret;
563: }
564: if(offset) lseek(dir, offset, FSEEK_ABSOLUTE);
565: if(read(dir, (char *)dentry, dsize)<0) {
566: perror(name);
567: rv = 0;
568: goto ret;
569: }
570: if(dir > 10) {
571: close(dir);
572: dir = 0;
573: }
574: } else
575: if(read(dir, (char *)dentry, dsize)<0) {
576: perror(name);
577: rv = 0;
578: goto ret;
579: }
580: for(dp=dentry, entries=dsize>>4; entries; --entries, ++dp) { /* each directory entry */
581: if(dp->d_ino==0
582: || (dp->d_name[0]=='.' && dp->d_name[1]=='\0')
583: || (dp->d_name[0]=='.' && dp->d_name[1]=='.' && dp->d_name[2]=='\0'))
584: continue;
585: c1 = endofname;
586: *c1++ = '/';
587: c2 = dp->d_name;
588: for(i=0; i<14; ++i)
589: if(*c2)
590: *c1++ = *c2++;
591: else
592: break;
593: *c1 = '\0';
594: if(c1 == endofname) { /* ?? */
595: rv = 0;
596: goto ret;
597: }
598: Fname = endofname+1;
599: if(!descend(name, Fname, exlist)) {
600: *endofname = '\0';
601: chdir(Home);
602: if(chdir(Pathname) == -1) {
603: perror(Pathname);
604: exit(1);
605: }
606: }
607: }
608: }
609: rv = 1;
610: ret:
611: if(dir)
612: close(dir);
613: if(chdir("..") == -1) {
614: *endofname = '\0';
615: perror(name);
616: rv = 1;
617: }
618: return(rv);
619: }
620:
621: gmatch(s, p) /* string match as in glob */
622: register char *s, *p;
623: {
624: if (*s=='.' && *p!='.') return(0);
625: return amatch(s, p);
626: }
627:
628: amatch(s, p)
629: register char *s, *p;
630: {
631: register cc;
632: int scc, k;
633: int c, lc;
634:
635: scc = *s;
636: lc = 077777;
637: switch (c = *p) {
638:
639: case '[':
640: k = 0;
641: while (cc = *++p) {
642: switch (cc) {
643:
644: case ']':
645: if (k)
646: return(amatch(++s, ++p));
647: else
648: return(0);
649:
650: case '-':
651: k |= lc <= scc & scc <= (cc=p[1]);
652: }
653: if (scc==(lc=cc)) k++;
654: }
655: return(0);
656:
657: case '?':
658: caseq:
659: if(scc) return(amatch(++s, ++p));
660: return(0);
661: case '*':
662: return(umatch(s, ++p));
663: case 0:
664: return(!scc);
665: }
666: if (c==scc) goto caseq;
667: return(0);
668: }
669:
670: umatch(s, p)
671: register char *s, *p;
672: {
673: if(*p==0) return(1);
674: while(*s)
675: if (amatch(s++, p)) return(1);
676: return(0);
677: }
678:
679: bwrite(rp, c)
680: register short *rp;
681: register c;
682: {
683: register short *wp = Wp;
684:
685: c = (c+1) >> 1;
686: while(c--) {
687: if(!Wct) {
688: again:
689: if(write(Cpio, (char *)Dbuf, Bufsize)<0) {
690: Cpio = chgreel(1, Cpio);
691: goto again;
692: }
693: Wct = Bufsize >> 1;
694: wp = Dbuf;
695: ++Blocks;
696: }
697: *wp++ = *rp++;
698: --Wct;
699: }
700: Wp = wp;
701: }
702: chgreel(x, fl)
703: {
704: register f;
705: char str[22];
706: FILE *devtty;
707: struct stat statb;
708:
709: pr("find: can't "), pr(x? "write output": "read input"), pr("\n");
710: fstat(fl, &statb);
711: if((statb.st_mode&S_IFMT) != S_IFCHR)
712: exit(1);
713: again:
714: pr("If you want to go on, type device/file name when ready\n");
715: devtty = fopen("/dev/tty", "r");
716: fgets(str, 20, devtty);
717: str[strlen(str) - 1] = '\0';
718: if(!*str)
719: exit(1);
720: close(fl);
721: if((f = open(str, x? FATT_WRONLY: FATT_RDONLY)) < 0) {
722: pr("That didn't work");
723: fclose(devtty);
724: goto again;
725: }
726: return f;
727: }
728: pr(s)
729: char *s;
730: {
731: fputs(s, stderr);
732: }