Research V7 development
[unix-history] / usr / src / cmd / find.c
CommitLineData
0ca4c09c
DH
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: 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
99struct 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}
111struct 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")) {
119And:
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}
128struct 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}
141struct 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 }
241err: pr("find: bad option "), pr(a), pr("\n");
242 exit(1);
243}
244struct anode *mk(f, l, r)
245int (*f)();
246struct 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
254char *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 */
270and(p)
271register struct anode *p;
272{
273 return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0);
274}
275or(p)
276register struct anode *p;
277{
278 return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0);
279}
280not(p)
281register struct anode *p;
282{
283 return( !((*p->L->F)(p->L)));
284}
285glob(p)
286register struct { int f; char *pat; } *p;
287{
288 return(gmatch(Fname, p->pat));
289}
290print()
291{
292 puts(Pathname);
293 return(1);
294}
295mtime(p)
296register struct { int f, t, s; } *p;
297{
298 return(scomp((int)((Now - Statb.st_mtime) / A_DAY), p->t, p->s));
299}
300atime(p)
301register struct { int f, t, s; } *p;
302{
303 return(scomp((int)((Now - Statb.st_atime) / A_DAY), p->t, p->s));
304}
305ctime(p)
306register struct { int f, t, s; } *p;
307{
308 return(scomp((int)((Now - Statb.st_ctime) / A_DAY), p->t, p->s));
309}
310user(p)
311register struct { int f, u, s; } *p;
312{
313 return(scomp(Statb.st_uid, p->u, p->s));
314}
315ino(p)
316register struct { int f, u, s; } *p;
317{
318 return(scomp((int)Statb.st_ino, p->u, p->s));
319}
320group(p)
321register struct { int f, u; } *p;
322{
323 return(p->u == Statb.st_gid);
324}
325links(p)
326register struct { int f, link, s; } *p;
327{
328 return(scomp(Statb.st_nlink, p->link, p->s));
329}
330size(p)
331register struct { int f, sz, s; } *p;
332{
333 return(scomp((int)((Statb.st_size+511)>>9), p->sz, p->s));
334}
335perm(p)
336register 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}
342type(p)
343register struct { int f, per, s; } *p;
344{
345 return((Statb.st_mode&S_IFMT)==p->per);
346}
347exeq(p)
348register struct { int f, com; } *p;
349{
350 fflush(stdout); /* to flush possible `-print' */
351 return(doex(p->com));
352}
353ok(p)
354struct { 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];}
372union { long l; short s[2]; char c[4]; } U;
373long mklong(v)
374short 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}
383cpio()
384{
385#define MAGIC 070707
386 struct header {
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) {
428cerror:
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}
442newer()
443{
444 return Statb.st_mtime > Newer;
445}
446
447/* support functions */
448scomp(a, b, s) /* funny signed compare */
449register a, b;
450register char s;
451{
452 if(s == '+')
453 return(a > b);
454 if(s == '-')
455 return(a < (b * -1));
456 return(a == b);
457}
458
459doex(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
483getunum(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
515descend(name, fname, exlist)
516struct anode *exlist;
517char *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;
601ret:
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
612gmatch(s, p) /* string match as in glob */
613register char *s, *p;
614{
615 if (*s=='.' && *p!='.') return(0);
616 return amatch(s, p);
617}
618
619amatch(s, p)
620register 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
661umatch(s, p)
662register 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
670bwrite(rp, c)
671register short *rp;
672register c;
673{
674 register short *wp = Wp;
675
676 c = (c+1) >> 1;
677 while(c--) {
678 if(!Wct) {
679again:
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}
693chgreel(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);
704again:
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}
719pr(s)
720char *s;
721{
722 fputs(s, stderr);
723}