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