xref: /original-bsd/usr.bin/find/find.c (revision b3b53e97)
1 static char *sccsid = "@(#)find.c	4.5 (Berkeley) 03/31/82";
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 
10 int	Randlast;
11 char	Pathname[200];
12 
13 struct anode {
14 	int (*F)();
15 	struct anode *L, *R;
16 } Node[100];
17 int Nn;  /* number of nodes */
18 char	*Fname;
19 long	Now;
20 int	Argc,
21 	Ai,
22 	Pi;
23 char	**Argv;
24 /* cpio stuff */
25 int	Cpio;
26 short	*Buf, *Dbuf, *Wp;
27 int	Bufsize = 5120;
28 int	Wct = 2560;
29 
30 long	Newer;
31 
32 struct stat Statb;
33 
34 struct	anode	*exp(),
35 		*e1(),
36 		*e2(),
37 		*e3(),
38 		*mk();
39 char	*nxtarg();
40 char	Home[128];
41 long	Blocks;
42 char *rindex();
43 char *sbrk();
44 main(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) {
58 usage:		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 
101 struct 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 }
113 struct 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")) {
121 And:
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 }
130 struct 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 }
143 struct 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]*"))
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]*"))
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 	}
237 err:	fprintf(stderr, "find: bad option < %s >\n", a);
238 	exit(1);
239 }
240 struct anode *mk(f, l, r)
241 int (*f)();
242 struct 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 
250 char *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 */
266 and(p)
267 register struct anode *p;
268 {
269 	return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0);
270 }
271 or(p)
272 register struct anode *p;
273 {
274 	 return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0);
275 }
276 not(p)
277 register struct anode *p;
278 {
279 	return( !((*p->L->F)(p->L)));
280 }
281 glob(p)
282 register struct { int f; char *pat; } *p;
283 {
284 	return(gmatch(Fname, p->pat));
285 }
286 print()
287 {
288 	puts(Pathname);
289 	return(1);
290 }
291 mtime(p)
292 register struct { int f, t, s; } *p;
293 {
294 	return(scomp((int)((Now - Statb.st_mtime) / A_DAY), p->t, p->s));
295 }
296 atime(p)
297 register struct { int f, t, s; } *p;
298 {
299 	return(scomp((int)((Now - Statb.st_atime) / A_DAY), p->t, p->s));
300 }
301 user(p)
302 register struct { int f, u, s; } *p;
303 {
304 	return(scomp(Statb.st_uid, p->u, p->s));
305 }
306 ino(p)
307 register struct { int f, u, s; } *p;
308 {
309 	return(scomp((int)Statb.st_ino, p->u, p->s));
310 }
311 group(p)
312 register struct { int f, u; } *p;
313 {
314 	return(p->u == Statb.st_gid);
315 }
316 links(p)
317 register struct { int f, link, s; } *p;
318 {
319 	return(scomp(Statb.st_nlink, p->link, p->s));
320 }
321 size(p)
322 register struct { int f, sz, s; } *p;
323 {
324 	return(scomp((int)((Statb.st_size+511)>>9), p->sz, p->s));
325 }
326 perm(p)
327 register 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 }
333 type(p)
334 register struct { int f, per, s; } *p;
335 {
336 	return((Statb.st_mode&S_IFMT)==p->per);
337 }
338 exeq(p)
339 register struct { int f, com; } *p;
340 {
341 	fflush(stdout); /* to flush possible `-print' */
342 	return(doex(p->com));
343 }
344 ok(p)
345 struct { 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];}
359 union { long l; short s[2]; char c[4]; } U;
360 long mklong(v)
361 short 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 }
370 cpio()
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) {
413 cerror:
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 }
427 newer()
428 {
429 	return Statb.st_mtime > Newer;
430 }
431 
432 /* support functions */
433 scomp(a, b, s) /* funny signed compare */
434 register a, b;
435 register char s;
436 {
437 	if(s == '+')
438 		return(a > b);
439 	if(s == '-')
440 		return(a < (b * -1));
441 	return(a == b);
442 }
443 
444 doex(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*/ {
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*/
467 		chdir(Home);
468 		execvp(nargv[0], nargv, np);
469 		exit(1);
470 	}
471 	return(ccode ? 0:1);
472 }
473 
474 getunum(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 
506 descend(name, fname, exlist)
507 struct anode *exlist;
508 char *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 
522 	if(lstat(fname, &Statb)<0) {
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;
595 ret:
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 
606 gmatch(s, p) /* string match as in glob */
607 register char *s, *p;
608 {
609 	if (*s=='.' && *p!='.') return(0);
610 	return amatch(s, p);
611 }
612 
613 amatch(s, p)
614 register 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 
655 umatch(s, p)
656 register 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 
664 bwrite(rp, c)
665 register short *rp;
666 register c;
667 {
668 	register short *wp = Wp;
669 
670 	c = (c+1) >> 1;
671 	while(c--) {
672 		if(!Wct) {
673 again:
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 }
687 chgreel(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);
700 again:
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 }
716