date and time created 83/07/20 19:32:22 by edward
[unix-history] / usr / src / usr.bin / find / find.c
CommitLineData
773a4280 1static char *sccsid = "@(#)find.c 4.7 (Berkeley) %G%";
c08d5d47
BJ
2/* find COMPILE: cc -o find -s -O -i find.c -lS */
3#include <stdio.h>
365a571b 4#include <sys/param.h>
c08d5d47
BJ
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 :
773a4280
KM
206 s=='f' ? S_IFREG :
207 s=='l' ? S_IFLNK :
c08d5d47
BJ
208 0;
209 return(mk(type, (struct anode *)i, (struct anode *)0));
210 }
211 else if (EQ(a, "-exec")) {
212 i = Ai - 1;
213 while(!EQ(nxtarg(), ";"));
214 return(mk(exeq, (struct anode *)i, (struct anode *)0));
215 }
216 else if (EQ(a, "-ok")) {
217 i = Ai - 1;
218 while(!EQ(nxtarg(), ";"));
219 return(mk(ok, (struct anode *)i, (struct anode *)0));
220 }
221 else if(EQ(a, "-cpio")) {
222 if((Cpio = creat(b, 0666)) < 0) {
223 fprintf(stderr, "find: cannot create < %s >\n", s);
224 exit(1);
225 }
226 Buf = (short *)sbrk(512);
227 Wp = Dbuf = (short *)sbrk(5120);
228 return(mk(cpio, (struct anode *)0, (struct anode *)0));
229 }
230 else if(EQ(a, "-newer")) {
231 if(stat(b, &Statb) < 0) {
232 fprintf(stderr, "find: cannot access < %s >\n", b);
233 exit(1);
234 }
235 Newer = Statb.st_mtime;
236 return mk(newer, (struct anode *)0, (struct anode *)0);
237 }
238err: fprintf(stderr, "find: bad option < %s >\n", a);
239 exit(1);
240}
241struct anode *mk(f, l, r)
242int (*f)();
243struct anode *l, *r;
244{
245 Node[Nn].F = f;
246 Node[Nn].L = l;
247 Node[Nn].R = r;
248 return(&(Node[Nn++]));
249}
250
251char *nxtarg() { /* get next arg from command line */
252 static strikes = 0;
253
254 if(strikes==3) {
255 fprintf(stderr, "find: incomplete statement\n");
256 exit(1);
257 }
258 if(Ai>=Argc) {
259 strikes++;
260 Ai = Argc + 1;
261 return("");
262 }
263 return(Argv[Ai++]);
264}
265
266/* execution time functions */
267and(p)
268register struct anode *p;
269{
270 return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0);
271}
272or(p)
273register struct anode *p;
274{
275 return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0);
276}
277not(p)
278register struct anode *p;
279{
280 return( !((*p->L->F)(p->L)));
281}
282glob(p)
283register struct { int f; char *pat; } *p;
284{
285 return(gmatch(Fname, p->pat));
286}
287print()
288{
289 puts(Pathname);
290 return(1);
291}
292mtime(p)
293register struct { int f, t, s; } *p;
294{
295 return(scomp((int)((Now - Statb.st_mtime) / A_DAY), p->t, p->s));
296}
297atime(p)
298register struct { int f, t, s; } *p;
299{
300 return(scomp((int)((Now - Statb.st_atime) / A_DAY), p->t, p->s));
301}
302user(p)
303register struct { int f, u, s; } *p;
304{
305 return(scomp(Statb.st_uid, p->u, p->s));
306}
307ino(p)
308register struct { int f, u, s; } *p;
309{
310 return(scomp((int)Statb.st_ino, p->u, p->s));
311}
312group(p)
313register struct { int f, u; } *p;
314{
315 return(p->u == Statb.st_gid);
316}
317links(p)
318register struct { int f, link, s; } *p;
319{
320 return(scomp(Statb.st_nlink, p->link, p->s));
321}
322size(p)
323register struct { int f, sz, s; } *p;
324{
325 return(scomp((int)((Statb.st_size+511)>>9), p->sz, p->s));
326}
327perm(p)
328register struct { int f, per, s; } *p;
329{
330 register i;
331 i = (p->s=='-') ? p->per : 07777; /* '-' means only arg bits */
332 return((Statb.st_mode & i & 07777) == p->per);
333}
334type(p)
335register struct { int f, per, s; } *p;
336{
337 return((Statb.st_mode&S_IFMT)==p->per);
338}
339exeq(p)
340register struct { int f, com; } *p;
341{
342 fflush(stdout); /* to flush possible `-print' */
343 return(doex(p->com));
344}
345ok(p)
346struct { int f, com; } *p;
347{
348 char c; int yes;
349 yes = 0;
350 fflush(stdout); /* to flush possible `-print' */
351 fprintf(stderr, "< %s ... %s > ? ", Argv[p->com], Pathname);
352 fflush(stderr);
353 if((c=getchar())=='y') yes = 1;
354 while(c!='\n') c = getchar();
355 if(yes) return(doex(p->com));
356 return(0);
357}
358
359#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];}
360union { long l; short s[2]; char c[4]; } U;
361long mklong(v)
362short v[];
363{
364 U.l = 1;
365 if(U.c[0] /* VAX */)
366 U.s[0] = v[1], U.s[1] = v[0];
367 else
368 U.s[0] = v[0], U.s[1] = v[1];
369 return U.l;
370}
371cpio()
372{
373#define MAGIC 070707
374 struct header {
375 short h_magic,
376 h_dev,
377 h_ino,
378 h_mode,
379 h_uid,
380 h_gid,
381 h_nlink,
382 h_rdev;
383 short h_mtime[2];
384 short h_namesize;
385 short h_filesize[2];
386 char h_name[256];
387 } hdr;
388 register ifile, ct;
389 static long fsz;
390 register i;
391
392 hdr.h_magic = MAGIC;
393 strcpy(hdr.h_name, !strncmp(Pathname, "./", 2)? Pathname+2: Pathname);
394 hdr.h_namesize = strlen(hdr.h_name) + 1;
395 hdr.h_uid = Statb.st_uid;
396 hdr.h_gid = Statb.st_gid;
397 hdr.h_dev = Statb.st_dev;
398 hdr.h_ino = Statb.st_ino;
399 hdr.h_mode = Statb.st_mode;
400 MKSHORT(hdr.h_mtime, Statb.st_mtime);
401 hdr.h_nlink = Statb.st_nlink;
402 fsz = hdr.h_mode & S_IFREG? Statb.st_size: 0L;
403 MKSHORT(hdr.h_filesize, fsz);
404 hdr.h_rdev = Statb.st_rdev;
405 if(EQ(hdr.h_name, "TRAILER!!!")) {
406 bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
407 for(i = 0; i < 10; ++i)
408 bwrite(Buf, 512);
409 return;
410 }
411 if(!mklong(hdr.h_filesize))
412 return;
413 if((ifile = open(Fname, 0)) < 0) {
414cerror:
415 fprintf(stderr, "find: cannot copy < %s >\n", hdr.h_name);
416 return;
417 }
418 bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
419 for(fsz = mklong(hdr.h_filesize); fsz > 0; fsz -= 512) {
420 ct = fsz>512? 512: fsz;
421 if(read(ifile, (char *)Buf, ct) < 0)
422 goto cerror;
423 bwrite(Buf, ct);
424 }
425 close(ifile);
426 return;
427}
428newer()
429{
430 return Statb.st_mtime > Newer;
431}
432
433/* support functions */
434scomp(a, b, s) /* funny signed compare */
435register a, b;
436register char s;
437{
438 if(s == '+')
439 return(a > b);
440 if(s == '-')
441 return(a < (b * -1));
442 return(a == b);
443}
444
445doex(com)
446{
447 register np;
448 register char *na;
449 static char *nargv[50];
450 static ccode;
451
452 ccode = np = 0;
453 while (na=Argv[com++]) {
454 if(strcmp(na, ";")==0) break;
455 if(strcmp(na, "{}")==0) nargv[np++] = Pathname;
456 else nargv[np++] = na;
457 }
458 nargv[np] = 0;
459 if (np==0) return(9);
7f2b31d9
BJ
460 if(fork()) /*parent*/ {
461#include <signal.h>
462 int (*old)() = signal(SIGINT, SIG_IGN);
463 int (*oldq)() = signal(SIGQUIT, SIG_IGN);
464 wait(&ccode);
465 signal(SIGINT, old);
466 signal(SIGQUIT, oldq);
467 } else { /*child*/
c08d5d47
BJ
468 chdir(Home);
469 execvp(nargv[0], nargv, np);
470 exit(1);
471 }
472 return(ccode ? 0:1);
473}
474
475getunum(f, s) char *f, *s; { /* find user/group name and return number */
476 register i;
477 register char *sp;
478 register c;
479 char str[20];
480 FILE *pin;
481
482 i = -1;
483 pin = fopen(f, "r");
484 c = '\n'; /* prime with a CR */
485 do {
486 if(c=='\n') {
487 sp = str;
488 while((c = *sp++ = getc(pin)) != ':')
489 if(c == EOF) goto RET;
490 *--sp = '\0';
491 if(EQ(str, s)) {
492 while((c=getc(pin)) != ':')
493 if(c == EOF) goto RET;
494 sp = str;
495 while((*sp = getc(pin)) != ':') sp++;
496 *sp = '\0';
497 i = atoi(str);
498 goto RET;
499 }
500 }
501 } while((c = getc(pin)) != EOF);
502 RET:
503 fclose(pin);
504 return(i);
505}
506
507descend(name, fname, exlist)
365a571b
KM
508 struct anode *exlist;
509 char *name, *fname;
c08d5d47 510{
365a571b 511 DIR *dir = NULL;
c08d5d47 512 register struct direct *dp;
365a571b 513 register char *c1;
c08d5d47
BJ
514 int rv = 0;
515 char *endofname;
516
365a571b 517 if (lstat(fname, &Statb)<0) {
c08d5d47
BJ
518 fprintf(stderr, "find: bad status < %s >\n", name);
519 return(0);
520 }
521 (*exlist->F)(exlist);
522 if((Statb.st_mode&S_IFMT)!=S_IFDIR)
523 return(1);
524
365a571b
KM
525 for (c1 = name; *c1; ++c1);
526 if (*(c1-1) == '/')
c08d5d47
BJ
527 --c1;
528 endofname = c1;
c08d5d47 529
365a571b 530 if (chdir(fname) == -1)
c08d5d47 531 return(0);
365a571b
KM
532 if ((dir = opendir(".")) == NULL) {
533 fprintf(stderr, "find: cannot open < %s >\n", name);
534 rv = 0;
535 goto ret;
536 }
537 for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) {
538 if ((dp->d_name[0]=='.' && dp->d_name[1]=='\0') ||
539 (dp->d_name[0]=='.' && dp->d_name[1]=='.' && dp->d_name[2]=='\0'))
540 continue;
541 c1 = endofname;
542 *c1++ = '/';
543 strcpy(c1, dp->d_name);
544 Fname = endofname+1;
545 if(!descend(name, Fname, exlist)) {
546 *endofname = '\0';
547 chdir(Home);
548 if(chdir(Pathname) == -1) {
549 fprintf(stderr, "find: bad directory tree\n");
550 exit(1);
c08d5d47
BJ
551 }
552 }
553 }
554 rv = 1;
555ret:
556 if(dir)
365a571b 557 closedir(dir);
c08d5d47
BJ
558 if(chdir("..") == -1) {
559 *endofname = '\0';
560 fprintf(stderr, "find: bad directory <%s>\n", name);
561 rv = 1;
562 }
563 return(rv);
564}
565
566gmatch(s, p) /* string match as in glob */
567register char *s, *p;
568{
569 if (*s=='.' && *p!='.') return(0);
570 return amatch(s, p);
571}
572
573amatch(s, p)
574register char *s, *p;
575{
576 register cc;
577 int scc, k;
578 int c, lc;
579
580 scc = *s;
581 lc = 077777;
582 switch (c = *p) {
583
584 case '[':
585 k = 0;
586 while (cc = *++p) {
587 switch (cc) {
588
589 case ']':
590 if (k)
591 return(amatch(++s, ++p));
592 else
593 return(0);
594
595 case '-':
596 k |= lc <= scc & scc <= (cc=p[1]);
597 }
598 if (scc==(lc=cc)) k++;
599 }
600 return(0);
601
602 case '?':
603 caseq:
604 if(scc) return(amatch(++s, ++p));
605 return(0);
606 case '*':
607 return(umatch(s, ++p));
608 case 0:
609 return(!scc);
610 }
611 if (c==scc) goto caseq;
612 return(0);
613}
614
615umatch(s, p)
616register char *s, *p;
617{
618 if(*p==0) return(1);
619 while(*s)
620 if (amatch(s++, p)) return(1);
621 return(0);
622}
623
624bwrite(rp, c)
625register short *rp;
626register c;
627{
628 register short *wp = Wp;
629
630 c = (c+1) >> 1;
631 while(c--) {
632 if(!Wct) {
633again:
634 if(write(Cpio, (char *)Dbuf, Bufsize)<0) {
635 Cpio = chgreel(1, Cpio);
636 goto again;
637 }
638 Wct = Bufsize >> 1;
639 wp = Dbuf;
640 ++Blocks;
641 }
642 *wp++ = *rp++;
643 --Wct;
644 }
645 Wp = wp;
646}
647chgreel(x, fl)
648{
649 register f;
650 char str[22];
651 FILE *devtty;
652 struct stat statb;
653 extern errno;
654
655 fprintf(stderr, "find: errno: %d, ", errno);
656 fprintf(stderr, "find: can't %s\n", x? "write output": "read input");
657 fstat(fl, &statb);
658 if((statb.st_mode&S_IFMT) != S_IFCHR)
659 exit(1);
660again:
661 fprintf(stderr, "If you want to go on, type device/file name %s\n",
662 "when ready");
663 devtty = fopen("/dev/tty", "r");
664 fgets(str, 20, devtty);
665 str[strlen(str) - 1] = '\0';
666 if(!*str)
667 exit(1);
668 close(fl);
669 if((f = open(str, x? 1: 0)) < 0) {
670 fprintf(stderr, "That didn't work");
671 fclose(devtty);
672 goto again;
673 }
674 return f;
675}