xref: /original-bsd/usr.bin/find/find.c (revision 8ac030d2)
1 static char *sccsid = "@(#)find.c	4.7 (Berkeley) 08/02/82";
2 /*	find	COMPILE:	cc -o find -s -O -i find.c -lS	*/
3 #include <stdio.h>
4 #include <sys/param.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' ? S_IFREG :
207 		    s=='l' ? S_IFLNK :
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 	}
238 err:	fprintf(stderr, "find: bad option < %s >\n", a);
239 	exit(1);
240 }
241 struct anode *mk(f, l, r)
242 int (*f)();
243 struct 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 
251 char *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 */
267 and(p)
268 register struct anode *p;
269 {
270 	return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0);
271 }
272 or(p)
273 register struct anode *p;
274 {
275 	 return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0);
276 }
277 not(p)
278 register struct anode *p;
279 {
280 	return( !((*p->L->F)(p->L)));
281 }
282 glob(p)
283 register struct { int f; char *pat; } *p;
284 {
285 	return(gmatch(Fname, p->pat));
286 }
287 print()
288 {
289 	puts(Pathname);
290 	return(1);
291 }
292 mtime(p)
293 register struct { int f, t, s; } *p;
294 {
295 	return(scomp((int)((Now - Statb.st_mtime) / A_DAY), p->t, p->s));
296 }
297 atime(p)
298 register struct { int f, t, s; } *p;
299 {
300 	return(scomp((int)((Now - Statb.st_atime) / A_DAY), p->t, p->s));
301 }
302 user(p)
303 register struct { int f, u, s; } *p;
304 {
305 	return(scomp(Statb.st_uid, p->u, p->s));
306 }
307 ino(p)
308 register struct { int f, u, s; } *p;
309 {
310 	return(scomp((int)Statb.st_ino, p->u, p->s));
311 }
312 group(p)
313 register struct { int f, u; } *p;
314 {
315 	return(p->u == Statb.st_gid);
316 }
317 links(p)
318 register struct { int f, link, s; } *p;
319 {
320 	return(scomp(Statb.st_nlink, p->link, p->s));
321 }
322 size(p)
323 register struct { int f, sz, s; } *p;
324 {
325 	return(scomp((int)((Statb.st_size+511)>>9), p->sz, p->s));
326 }
327 perm(p)
328 register 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 }
334 type(p)
335 register struct { int f, per, s; } *p;
336 {
337 	return((Statb.st_mode&S_IFMT)==p->per);
338 }
339 exeq(p)
340 register struct { int f, com; } *p;
341 {
342 	fflush(stdout); /* to flush possible `-print' */
343 	return(doex(p->com));
344 }
345 ok(p)
346 struct { 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];}
360 union { long l; short s[2]; char c[4]; } U;
361 long mklong(v)
362 short 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 }
371 cpio()
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) {
414 cerror:
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 }
428 newer()
429 {
430 	return Statb.st_mtime > Newer;
431 }
432 
433 /* support functions */
434 scomp(a, b, s) /* funny signed compare */
435 register a, b;
436 register char s;
437 {
438 	if(s == '+')
439 		return(a > b);
440 	if(s == '-')
441 		return(a < (b * -1));
442 	return(a == b);
443 }
444 
445 doex(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);
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*/
468 		chdir(Home);
469 		execvp(nargv[0], nargv, np);
470 		exit(1);
471 	}
472 	return(ccode ? 0:1);
473 }
474 
475 getunum(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 
507 descend(name, fname, exlist)
508 	struct anode *exlist;
509 	char *name, *fname;
510 {
511 	DIR	*dir = NULL;
512 	register struct direct	*dp;
513 	register char *c1;
514 	int rv = 0;
515 	char *endofname;
516 
517 	if (lstat(fname, &Statb)<0) {
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 
525 	for (c1 = name; *c1; ++c1);
526 	if (*(c1-1) == '/')
527 		--c1;
528 	endofname = c1;
529 
530 	if (chdir(fname) == -1)
531 		return(0);
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);
551 			}
552 		}
553 	}
554 	rv = 1;
555 ret:
556 	if(dir)
557 		closedir(dir);
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 
566 gmatch(s, p) /* string match as in glob */
567 register char *s, *p;
568 {
569 	if (*s=='.' && *p!='.') return(0);
570 	return amatch(s, p);
571 }
572 
573 amatch(s, p)
574 register 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 
615 umatch(s, p)
616 register 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 
624 bwrite(rp, c)
625 register short *rp;
626 register c;
627 {
628 	register short *wp = Wp;
629 
630 	c = (c+1) >> 1;
631 	while(c--) {
632 		if(!Wct) {
633 again:
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 }
647 chgreel(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);
660 again:
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 }
676