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