update to new fs
[unix-history] / usr / src / usr.bin / find / find.c
CommitLineData
79453cc4 1static char *sccsid = "@(#)find.c 4.5 (Berkeley) %G%";
c08d5d47
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) {
68791ee2 173 if(gmatch(b, "[0-9]*"))
c08d5d47
BJ
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) {
85e30eca 184 if(gmatch(b, "[0-9]*"))
c08d5d47
BJ
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);
7f2b31d9
BJ
459 if(fork()) /*parent*/ {
460#include <signal.h>
461 int (*old)() = signal(SIGINT, SIG_IGN);
462 int (*oldq)() = signal(SIGQUIT, SIG_IGN);
463 wait(&ccode);
464 signal(SIGINT, old);
465 signal(SIGQUIT, oldq);
466 } else { /*child*/
c08d5d47
BJ
467 chdir(Home);
468 execvp(nargv[0], nargv, np);
469 exit(1);
470 }
471 return(ccode ? 0:1);
472}
473
474getunum(f, s) char *f, *s; { /* find user/group name and return number */
475 register i;
476 register char *sp;
477 register c;
478 char str[20];
479 FILE *pin;
480
481 i = -1;
482 pin = fopen(f, "r");
483 c = '\n'; /* prime with a CR */
484 do {
485 if(c=='\n') {
486 sp = str;
487 while((c = *sp++ = getc(pin)) != ':')
488 if(c == EOF) goto RET;
489 *--sp = '\0';
490 if(EQ(str, s)) {
491 while((c=getc(pin)) != ':')
492 if(c == EOF) goto RET;
493 sp = str;
494 while((*sp = getc(pin)) != ':') sp++;
495 *sp = '\0';
496 i = atoi(str);
497 goto RET;
498 }
499 }
500 } while((c = getc(pin)) != EOF);
501 RET:
502 fclose(pin);
503 return(i);
504}
505
506descend(name, fname, exlist)
507struct anode *exlist;
508char *name, *fname;
509{
510 int dir = 0, /* open directory */
511 offset,
512 dsize,
513 entries,
514 dirsize;
515 struct direct dentry[BUFSIZ / sizeof (struct direct)];
516 register struct direct *dp;
517 register char *c1, *c2;
518 int i;
519 int rv = 0;
520 char *endofname;
521
79453cc4 522 if(lstat(fname, &Statb)<0) {
c08d5d47
BJ
523 fprintf(stderr, "find: bad status < %s >\n", name);
524 return(0);
525 }
526 (*exlist->F)(exlist);
527 if((Statb.st_mode&S_IFMT)!=S_IFDIR)
528 return(1);
529
530 for(c1 = name; *c1; ++c1);
531 if(*(c1-1) == '/')
532 --c1;
533 endofname = c1;
534 dirsize = Statb.st_size;
535
536 if(chdir(fname) == -1)
537 return(0);
538 for(offset=0 ; offset < dirsize ; offset += BUFSIZ) { /* each block */
539 dsize = BUFSIZ<(dirsize-offset)? BUFSIZ: (dirsize-offset);
540 if(!dir) {
541 if((dir=open(".", 0))<0) {
542 fprintf(stderr, "find: cannot open < %s >\n",
543 name);
544 rv = 0;
545 goto ret;
546 }
547 if(offset) lseek(dir, (long)offset, 0);
548 if(read(dir, (char *)dentry, dsize)<0) {
549 fprintf(stderr, "find: cannot read < %s >\n",
550 name);
551 rv = 0;
552 goto ret;
553 }
554 if(dir > 10) {
555 close(dir);
556 dir = 0;
557 }
558 } else
559 if(read(dir, (char *)dentry, dsize)<0) {
560 fprintf(stderr, "find: cannot read < %s >\n",
561 name);
562 rv = 0;
563 goto ret;
564 }
565 for(dp=dentry, entries=dsize>>4; entries; --entries, ++dp) { /* each directory entry */
566 if(dp->d_ino==0
567 || (dp->d_name[0]=='.' && dp->d_name[1]=='\0')
568 || (dp->d_name[0]=='.' && dp->d_name[1]=='.' && dp->d_name[2]=='\0'))
569 continue;
570 c1 = endofname;
571 *c1++ = '/';
572 c2 = dp->d_name;
573 for(i=0; i<14; ++i)
574 if(*c2)
575 *c1++ = *c2++;
576 else
577 break;
578 *c1 = '\0';
579 if(c1 == endofname) { /* ?? */
580 rv = 0;
581 goto ret;
582 }
583 Fname = endofname+1;
584 if(!descend(name, Fname, exlist)) {
585 *endofname = '\0';
586 chdir(Home);
587 if(chdir(Pathname) == -1) {
588 fprintf(stderr, "find: bad directory tree\n");
589 exit(1);
590 }
591 }
592 }
593 }
594 rv = 1;
595ret:
596 if(dir)
597 close(dir);
598 if(chdir("..") == -1) {
599 *endofname = '\0';
600 fprintf(stderr, "find: bad directory <%s>\n", name);
601 rv = 1;
602 }
603 return(rv);
604}
605
606gmatch(s, p) /* string match as in glob */
607register char *s, *p;
608{
609 if (*s=='.' && *p!='.') return(0);
610 return amatch(s, p);
611}
612
613amatch(s, p)
614register char *s, *p;
615{
616 register cc;
617 int scc, k;
618 int c, lc;
619
620 scc = *s;
621 lc = 077777;
622 switch (c = *p) {
623
624 case '[':
625 k = 0;
626 while (cc = *++p) {
627 switch (cc) {
628
629 case ']':
630 if (k)
631 return(amatch(++s, ++p));
632 else
633 return(0);
634
635 case '-':
636 k |= lc <= scc & scc <= (cc=p[1]);
637 }
638 if (scc==(lc=cc)) k++;
639 }
640 return(0);
641
642 case '?':
643 caseq:
644 if(scc) return(amatch(++s, ++p));
645 return(0);
646 case '*':
647 return(umatch(s, ++p));
648 case 0:
649 return(!scc);
650 }
651 if (c==scc) goto caseq;
652 return(0);
653}
654
655umatch(s, p)
656register char *s, *p;
657{
658 if(*p==0) return(1);
659 while(*s)
660 if (amatch(s++, p)) return(1);
661 return(0);
662}
663
664bwrite(rp, c)
665register short *rp;
666register c;
667{
668 register short *wp = Wp;
669
670 c = (c+1) >> 1;
671 while(c--) {
672 if(!Wct) {
673again:
674 if(write(Cpio, (char *)Dbuf, Bufsize)<0) {
675 Cpio = chgreel(1, Cpio);
676 goto again;
677 }
678 Wct = Bufsize >> 1;
679 wp = Dbuf;
680 ++Blocks;
681 }
682 *wp++ = *rp++;
683 --Wct;
684 }
685 Wp = wp;
686}
687chgreel(x, fl)
688{
689 register f;
690 char str[22];
691 FILE *devtty;
692 struct stat statb;
693 extern errno;
694
695 fprintf(stderr, "find: errno: %d, ", errno);
696 fprintf(stderr, "find: can't %s\n", x? "write output": "read input");
697 fstat(fl, &statb);
698 if((statb.st_mode&S_IFMT) != S_IFCHR)
699 exit(1);
700again:
701 fprintf(stderr, "If you want to go on, type device/file name %s\n",
702 "when ready");
703 devtty = fopen("/dev/tty", "r");
704 fgets(str, 20, devtty);
705 str[strlen(str) - 1] = '\0';
706 if(!*str)
707 exit(1);
708 close(fl);
709 if((f = open(str, x? 1: 0)) < 0) {
710 fprintf(stderr, "That didn't work");
711 fclose(devtty);
712 goto again;
713 }
714 return f;
715}