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