xref: /original-bsd/usr.bin/find/find.c (revision c47935e1)
1 #ifndef	lint
2 static char *sccsid = "@(#)find.c	4.23 (Berkeley) 05/05/89";
3 #endif
4 
5 #include <sys/param.h>
6 #include <sys/dir.h>
7 #include <sys/stat.h>
8 #include <stdio.h>
9 #include "pathnames.h"
10 
11 #define A_DAY	86400L /* a day full of seconds */
12 #define EQ(x, y)	(strcmp(x, y)==0)
13 
14 int	Randlast;
15 char	Pathname[MAXPATHLEN+1];
16 
17 #define MAXNODES	100
18 
19 struct anode {
20 	int (*F)();
21 	struct anode *L, *R;
22 } Node[MAXNODES];
23 int Nn;  /* number of nodes */
24 char	*Fname;
25 long	Now;
26 int	Argc,
27 	Ai,
28 	Pi;
29 char	**Argv;
30 /* cpio stuff */
31 int	Cpio;
32 short	*Buf, *Dbuf, *Wp;
33 int	Bufsize = 5120;
34 int	Wct = 2560;
35 
36 long	Newer;
37 
38 int	Xdev = 1;	/* true if SHOULD cross devices (file systems) */
39 int	follow;		/* true if SHOULD descend symlinked directories */
40 struct	stat Devstat;	/* stats of each argument path's file system */
41 
42 struct stat Statb;
43 
44 struct	anode	*exp(),
45 		*e1(),
46 		*e2(),
47 		*e3(),
48 		*mk();
49 char	*nxtarg();
50 char	Home[MAXPATHLEN + 1];
51 long	Blocks;
52 char *rindex();
53 char *sbrk();
54 
55 /*
56  * SEE ALSO:	code.c, updatedb, bigram.c
57  *		Usenix ;login:, Vol 8, No 1, February/March, 1983, p. 8.
58  *
59  * REVISIONS: 	James A. Woods, Informatics General Corporation,
60  *		NASA Ames Research Center, 6/81.
61  *
62  *		The second form searches a pre-computed filelist
63  *		(constructed nightly by /usr/lib/crontab) which is
64  *		compressed by updatedb (v.i.z.)  The effect of
65  *			find <name>
66  *		is similar to
67  *			find / +0 -name "*<name>*" -print
68  *		but much faster.
69  *
70  *		8/82 faster yet + incorporation of bigram coding -- jaw
71  *
72  *		1/83 incorporate glob-style matching -- jaw
73  */
74 #define	AMES	1
75 
76 main(argc, argv)
77 	int argc;
78 	char *argv[];
79 {
80 	struct anode *exlist;
81 	int paths;
82 	register char *cp, *sp = 0;
83 #ifdef	SUID_PWD
84 	FILE *pwd, *popen();
85 #endif
86 
87 #ifdef  AMES
88 	if (argc < 2) {
89 		fprintf(stderr,
90 			"Usage: find name, or find path-list predicate-list\n");
91 		exit(1);
92 	}
93 	if (argc == 2) {
94 		fastfind(argv[1]);
95 		exit(0);
96 	}
97 #endif
98 	time(&Now);
99 	setpassent(1);
100 	setgroupent(1);
101 #ifdef	SUID_PWD
102 	pwd = popen("pwd", "r");
103 	fgets(Home, sizeof Home, pwd);
104 	pclose(pwd);
105 	Home[strlen(Home) - 1] = '\0';
106 #else
107 	if (getwd(Home) == NULL) {
108 		fprintf(stderr, "find: Can't get current working directory\n");
109 		exit(1);
110 	}
111 #endif
112 	Argc = argc; Argv = argv;
113 	if(argc<3) {
114 usage:		fprintf(stderr, "Usage: find path-list predicate-list\n");
115 		exit(1);
116 	}
117 	for(Ai = paths = 1; Ai < (argc-1); ++Ai, ++paths)
118 		if(*Argv[Ai] == '-' || EQ(Argv[Ai], "(") || EQ(Argv[Ai], "!"))
119 			break;
120 	if(paths == 1) /* no path-list */
121 		goto usage;
122 	if(!(exlist = exp())) { /* parse and compile the arguments */
123 		fprintf(stderr, "find: parsing error\n");
124 		exit(1);
125 	}
126 	if(Ai<argc) {
127 		fprintf(stderr, "find: missing conjunction\n");
128 		exit(1);
129 	}
130 	for(Pi = 1; Pi < paths; ++Pi) {
131 		sp = 0;
132 		chdir(Home);
133 		strcpy(Pathname, Argv[Pi]);
134 		if(cp = rindex(Pathname, '/')) {
135 			sp = cp + 1;
136 			*cp = '\0';
137 			if(chdir(*Pathname? Pathname: "/") == -1) {
138 				fprintf(stderr, "find: bad starting directory\n");
139 				exit(2);
140 			}
141 			*cp = '/';
142 		}
143 		Fname = sp? sp: Pathname;
144 		if (!Xdev)
145 			stat(Pathname, &Devstat);
146 		descend(Pathname, Fname, exlist); /* to find files that match  */
147 	}
148 	if(Cpio) {
149 		strcpy(Pathname, "TRAILER!!!");
150 		Statb.st_size = 0;
151 		cpio();
152 		printf("%D blocks\n", Blocks*10);
153 	}
154 	exit(0);
155 }
156 
157 /* compile time functions:  priority is  exp()<e1()<e2()<e3()  */
158 
159 struct anode *exp() { /* parse ALTERNATION (-o)  */
160 	int or();
161 	register struct anode * p1;
162 
163 	p1 = e1() /* get left operand */ ;
164 	if(EQ(nxtarg(), "-o")) {
165 		Randlast--;
166 		return(mk(or, p1, exp()));
167 	}
168 	else if(Ai <= Argc) --Ai;
169 	return(p1);
170 }
171 struct anode *e1() { /* parse CONCATENATION (formerly -a) */
172 	int and();
173 	register struct anode * p1;
174 	register char *a;
175 
176 	p1 = e2();
177 	a = nxtarg();
178 	if(EQ(a, "-a")) {
179 And:
180 		Randlast--;
181 		return(mk(and, p1, e1()));
182 	} else if(EQ(a, "(") || EQ(a, "!") || (*a=='-' && !EQ(a, "-o"))) {
183 		--Ai;
184 		goto And;
185 	} else if(Ai <= Argc) --Ai;
186 	return(p1);
187 }
188 struct anode *e2() { /* parse NOT (!) */
189 	int not();
190 
191 	if(Randlast) {
192 		fprintf(stderr, "find: operand follows operand\n");
193 		exit(1);
194 	}
195 	Randlast++;
196 	if(EQ(nxtarg(), "!"))
197 		return(mk(not, e3(), (struct anode *)0));
198 	else if(Ai <= Argc) --Ai;
199 	return(e3());
200 }
201 struct anode *e3() { /* parse parens and predicates */
202 	int exeq(), ok(), glob(),  mtime(), atime(), user(),
203 		group(), size(), perm(), links(), print(),
204 		type(), ino(), cpio(), newer(),
205 		nouser(), nogroup(), ls(), dummy();
206 	struct anode *p1;
207 	int i;
208 	register char *a, *b;
209 	register int s;
210 
211 	a = nxtarg();
212 	if(EQ(a, "(")) {
213 		Randlast--;
214 		p1 = exp();
215 		a = nxtarg();
216 		if(!EQ(a, ")")) goto err;
217 		return(p1);
218 	}
219 	else if(EQ(a, "-print")) {
220 		return(mk(print, (struct anode *)0, (struct anode *)0));
221 	}
222 	else if (EQ(a, "-nouser")) {
223 		return (mk(nouser, (struct anode *)0, (struct anode *)0));
224 	}
225 	else if (EQ(a, "-nogroup")) {
226 		return (mk(nogroup, (struct anode *)0, (struct anode *)0));
227 	}
228 	else if (EQ(a, "-ls")) {
229 		return (mk(ls, (struct anode *)0, (struct anode *)0));
230 	}
231 	else if (EQ(a, "-xdev")) {
232 		Xdev = 0;
233 		return (mk(dummy, (struct anode *)0, (struct anode *)0));
234 	}
235 	else if (EQ(a, "-follow")) {
236 		follow=1;
237 		return mk(dummy, (struct anode *)0, (struct anode *)0);
238 	}
239 	b = nxtarg();
240 	s = *b;
241 	if(s=='+') b++;
242 	if(EQ(a, "-name"))
243 		return(mk(glob, (struct anode *)b, (struct anode *)0));
244 	else if(EQ(a, "-mtime"))
245 		return(mk(mtime, (struct anode *)atoi(b), (struct anode *)s));
246 	else if(EQ(a, "-atime"))
247 		return(mk(atime, (struct anode *)atoi(b), (struct anode *)s));
248 	else if(EQ(a, "-user")) {
249 		if((i=getuid(b)) == -1) {
250 			if(gmatch(b, "[0-9]*"))
251 				return mk(user, (struct anode *)atoi(b), (struct anode *)s);
252 			fprintf(stderr, "find: cannot find -user name\n");
253 			exit(1);
254 		}
255 		return(mk(user, (struct anode *)i, (struct anode *)s));
256 	}
257 	else if(EQ(a, "-inum"))
258 		return(mk(ino, (struct anode *)atoi(b), (struct anode *)s));
259 	else if(EQ(a, "-group")) {
260 		if((i=getgid(b)) == -1) {
261 			if(gmatch(b, "[0-9]*"))
262 				return mk(group, (struct anode *)atoi(b), (struct anode *)s);
263 			fprintf(stderr, "find: cannot find -group name\n");
264 			exit(1);
265 		}
266 		return(mk(group, (struct anode *)i, (struct anode *)s));
267 	} else if(EQ(a, "-size"))
268 		return(mk(size, (struct anode *)atoi(b), (struct anode *)s));
269 	else if(EQ(a, "-links"))
270 		return(mk(links, (struct anode *)atoi(b), (struct anode *)s));
271 	else if(EQ(a, "-perm")) {
272 		for(i=0; *b ; ++b) {
273 			if(*b=='-') continue;
274 			i <<= 3;
275 			i = i + (*b - '0');
276 		}
277 		return(mk(perm, (struct anode *)i, (struct anode *)s));
278 	}
279 	else if(EQ(a, "-type")) {
280 		i = s=='d' ? S_IFDIR :
281 		    s=='b' ? S_IFBLK :
282 		    s=='c' ? S_IFCHR :
283 		    s=='f' ? S_IFREG :
284 		    s=='l' ? S_IFLNK :
285 		    s=='s' ? S_IFSOCK :
286 		    0;
287 		return(mk(type, (struct anode *)i, (struct anode *)0));
288 	}
289 	else if (EQ(a, "-exec")) {
290 		i = Ai - 1;
291 		while(!EQ(nxtarg(), ";"));
292 		return(mk(exeq, (struct anode *)i, (struct anode *)0));
293 	}
294 	else if (EQ(a, "-ok")) {
295 		i = Ai - 1;
296 		while(!EQ(nxtarg(), ";"));
297 		return(mk(ok, (struct anode *)i, (struct anode *)0));
298 	}
299 	else if(EQ(a, "-cpio")) {
300 		if((Cpio = creat(b, 0666)) < 0) {
301 			fprintf(stderr, "find: cannot create < %s >\n", s);
302 			exit(1);
303 		}
304 		Buf = (short *)sbrk(512);
305 		Wp = Dbuf = (short *)sbrk(5120);
306 		return(mk(cpio, (struct anode *)0, (struct anode *)0));
307 	}
308 	else if(EQ(a, "-newer")) {
309 		if(stat(b, &Statb) < 0) {
310 			fprintf(stderr, "find: cannot access < %s >\n", b);
311 			exit(1);
312 		}
313 		Newer = Statb.st_mtime;
314 		return mk(newer, (struct anode *)0, (struct anode *)0);
315 	}
316 err:	fprintf(stderr, "find: bad option < %s >\n", a);
317 	exit(1);
318 }
319 struct anode *mk(f, l, r)
320 int (*f)();
321 struct anode *l, *r;
322 {
323 	if (Nn >= MAXNODES) {
324 		fprintf(stderr, "find: Too many options\n");
325 		exit(1);
326 	}
327 
328 	Node[Nn].F = f;
329 	Node[Nn].L = l;
330 	Node[Nn].R = r;
331 	return(&(Node[Nn++]));
332 }
333 
334 char *nxtarg() { /* get next arg from command line */
335 	static strikes = 0;
336 
337 	if(strikes==3) {
338 		fprintf(stderr, "find: incomplete statement\n");
339 		exit(1);
340 	}
341 	if(Ai>=Argc) {
342 		strikes++;
343 		Ai = Argc + 1;
344 		return("");
345 	}
346 	return(Argv[Ai++]);
347 }
348 
349 /* execution time functions */
350 and(p)
351 register struct anode *p;
352 {
353 	return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0);
354 }
355 or(p)
356 register struct anode *p;
357 {
358 	 return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0);
359 }
360 not(p)
361 register struct anode *p;
362 {
363 	return( !((*p->L->F)(p->L)));
364 }
365 glob(p)
366 register struct { int f; char *pat; } *p;
367 {
368 	return(gmatch(Fname, p->pat));
369 }
370 print(p)
371 struct anode *p;
372 {
373 	puts(Pathname);
374 	return(1);
375 }
376 mtime(p)
377 register struct { int f, t, s; } *p;
378 {
379 	return(scomp((int)((Now - Statb.st_mtime) / A_DAY), p->t, p->s));
380 }
381 atime(p)
382 register struct { int f, t, s; } *p;
383 {
384 	return(scomp((int)((Now - Statb.st_atime) / A_DAY), p->t, p->s));
385 }
386 user(p)
387 register struct { int f, u, s; } *p;
388 {
389 	return(scomp(Statb.st_uid, p->u, p->s));
390 }
391 nouser(p)
392 struct anode *p;
393 {
394 	char *getname();
395 
396 	return (getname(Statb.st_uid) == NULL);
397 }
398 ino(p)
399 register struct { int f, u, s; } *p;
400 {
401 	return(scomp((int)Statb.st_ino, p->u, p->s));
402 }
403 group(p)
404 register struct { int f, u; } *p;
405 {
406 	return(p->u == Statb.st_gid);
407 }
408 nogroup(p)
409 struct anode *p;
410 {
411 	char *getgroup();
412 
413 	return (getgroup(Statb.st_gid) == NULL);
414 }
415 links(p)
416 register struct { int f, link, s; } *p;
417 {
418 	return(scomp(Statb.st_nlink, p->link, p->s));
419 }
420 size(p)
421 register struct { int f, sz, s; } *p;
422 {
423 	return(scomp((int)((Statb.st_size+511)>>9), p->sz, p->s));
424 }
425 perm(p)
426 register struct { int f, per, s; } *p;
427 {
428 	register i;
429 	i = (p->s=='-') ? p->per : 07777; /* '-' means only arg bits */
430 	return((Statb.st_mode & i & 07777) == p->per);
431 }
432 type(p)
433 register struct { int f, per, s; } *p;
434 {
435 	return((Statb.st_mode&S_IFMT)==p->per);
436 }
437 exeq(p)
438 register struct { int f, com; } *p;
439 {
440 	fflush(stdout); /* to flush possible `-print' */
441 	return(doex(p->com));
442 }
443 ok(p)
444 struct { int f, com; } *p;
445 {
446 	char c;  int yes;
447 	yes = 0;
448 	fflush(stdout); /* to flush possible `-print' */
449 	fprintf(stderr, "< %s ... %s > ?   ", Argv[p->com], Pathname);
450 	fflush(stderr);
451 	if((c=getchar())=='y') yes = 1;
452 	while(c!='\n') c = getchar();
453 	if(yes) return(doex(p->com));
454 	return(0);
455 }
456 
457 #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];}
458 union { long l; short s[2]; char c[4]; } U;
459 long mklong(v)
460 short v[];
461 {
462 	U.l = 1;
463 	if(U.c[0] /* VAX */)
464 		U.s[0] = v[1], U.s[1] = v[0];
465 	else
466 		U.s[0] = v[0], U.s[1] = v[1];
467 	return U.l;
468 }
469 cpio(p)
470 struct anode *p;
471 {
472 #define MAGIC 070707
473 	struct header {
474 		short	h_magic,
475 			h_dev,
476 			h_ino,
477 			h_mode,
478 			h_uid,
479 			h_gid,
480 			h_nlink,
481 			h_rdev;
482 		short	h_mtime[2];
483 		short	h_namesize;
484 		short	h_filesize[2];
485 		char	h_name[256];
486 	} hdr;
487 	register ifile, ct;
488 	static long fsz;
489 	register i;
490 
491 	hdr.h_magic = MAGIC;
492 	strcpy(hdr.h_name, !strncmp(Pathname, "./", 2)? Pathname+2: Pathname);
493 	hdr.h_namesize = strlen(hdr.h_name) + 1;
494 	hdr.h_uid = Statb.st_uid;
495 	hdr.h_gid = Statb.st_gid;
496 	hdr.h_dev = Statb.st_dev;
497 	hdr.h_ino = Statb.st_ino;
498 	hdr.h_mode = Statb.st_mode;
499 	MKSHORT(hdr.h_mtime, Statb.st_mtime);
500 	hdr.h_nlink = Statb.st_nlink;
501 	fsz = hdr.h_mode & S_IFREG? Statb.st_size: 0L;
502 	MKSHORT(hdr.h_filesize, fsz);
503 	hdr.h_rdev = Statb.st_rdev;
504 	if(EQ(hdr.h_name, "TRAILER!!!")) {
505 		bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
506 		for(i = 0; i < 10; ++i)
507 			bwrite(Buf, 512);
508 		return;
509 	}
510 	if(!mklong(hdr.h_filesize))
511 		return;
512 	if((ifile = open(Fname, 0)) < 0) {
513 cerror:
514 		fprintf(stderr, "find: cannot copy < %s >\n", hdr.h_name);
515 		return;
516 	}
517 	bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
518 	for(fsz = mklong(hdr.h_filesize); fsz > 0; fsz -= 512) {
519 		ct = fsz>512? 512: fsz;
520 		if(read(ifile, (char *)Buf, ct) < 0)
521 			goto cerror;
522 		bwrite(Buf, ct);
523 	}
524 	close(ifile);
525 	return;
526 }
527 newer(p)
528 struct anode *p;
529 {
530 	return Statb.st_mtime > Newer;
531 }
532 ls(p)
533 struct anode *p;
534 {
535 	list(Pathname, &Statb);
536 	return (1);
537 }
538 dummy(p)
539 struct anode *p;
540 {
541 	/* dummy */
542 	return (1);
543 }
544 
545 /* support functions */
546 scomp(a, b, s) /* funny signed compare */
547 register a, b;
548 register char s;
549 {
550 	if(s == '+')
551 		return(a > b);
552 	if(s == '-')
553 		return(a < (b * -1));
554 	return(a == b);
555 }
556 
557 doex(com)
558 {
559 	register np;
560 	register char *na;
561 	static char *nargv[50];
562 	static ccode;
563 	register int w, pid, omask;
564 
565 	ccode = np = 0;
566 	while (na=Argv[com++]) {
567 		if(np >= sizeof nargv / sizeof *nargv - 1) break;
568 		if(strcmp(na, ";")==0) break;
569 		if(strcmp(na, "{}")==0) nargv[np++] = Pathname;
570 		else nargv[np++] = na;
571 	}
572 	nargv[np] = 0;
573 	if (np==0) return(9);
574 	switch (pid = vfork()) {
575 	case -1:
576 		perror("find: Can't fork");
577 		exit(1);
578 		break;
579 
580 	case 0:
581 		chdir(Home);
582 		execvp(nargv[0], nargv, np);
583 		write(2, "find: Can't execute ", 20);
584 		perror(nargv[0]);
585 		/*
586 		 * Kill ourselves; our exit status will be a suicide
587 		 * note indicating we couldn't do the "exec".
588 		 */
589 		kill(getpid(), SIGUSR1);
590 		break;
591 
592 	default:
593 		omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT));
594 		while ((w = wait(&ccode)) != pid && w != -1)
595 			;
596 		(void) sigsetmask(omask);
597 		if ((ccode & 0177) == SIGUSR1)
598 			exit(1);
599 		return (ccode != 0 ? 0 : 1);
600 	}
601 }
602 
603 getunum(f, s) char *f, *s; { /* find user/group name and return number */
604 	register i;
605 	register char *sp;
606 	register c;
607 	char str[20];
608 	FILE *pin;
609 
610 	i = -1;
611 	pin = fopen(f, "r");
612 	c = '\n'; /* prime with a CR */
613 	do {
614 		if(c=='\n') {
615 			sp = str;
616 			while((c = *sp++ = getc(pin)) != ':')
617 				if(c == EOF) goto RET;
618 			*--sp = '\0';
619 			if(EQ(str, s)) {
620 				while((c=getc(pin)) != ':')
621 					if(c == EOF) goto RET;
622 				sp = str;
623 				while((*sp = getc(pin)) != ':') sp++;
624 				*sp = '\0';
625 				i = atoi(str);
626 				goto RET;
627 			}
628 		}
629 	} while((c = getc(pin)) != EOF);
630  RET:
631 	fclose(pin);
632 	return(i);
633 }
634 
635 descend(name, fname, exlist)
636 	struct anode *exlist;
637 	char *name, *fname;
638 {
639 	DIR	*dir = NULL;
640 	register struct direct	*dp;
641 	register char *c1;
642 	int rv = 0;
643 	char *endofname;
644 
645 	if ((follow?stat(fname, &Statb):lstat(fname, &Statb))<0) {
646 		fprintf(stderr, "find: bad status < %s >\n", name);
647 		return(0);
648 	}
649 	(*exlist->F)(exlist);
650 	if((Statb.st_mode&S_IFMT)!=S_IFDIR ||
651 	   !Xdev && Devstat.st_dev != Statb.st_dev)
652 		return(1);
653 
654 	if (Statb.st_nlink == 2 && exlist->F == and &&
655 		exlist->L->F == type && ((int) (exlist->L->L)) == S_IFDIR)
656 			return(1);
657 
658 	for (c1 = name; *c1; ++c1);
659 	if (*(c1-1) == '/')
660 		--c1;
661 	endofname = c1;
662 
663 	if (chdir(fname) == -1)
664 		return(0);
665 	if ((dir = opendir(".")) == NULL) {
666 		fprintf(stderr, "find: cannot open < %s >\n", name);
667 		rv = 0;
668 		goto ret;
669 	}
670 	for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) {
671 		if ((dp->d_name[0]=='.' && dp->d_name[1]=='\0') ||
672 		    (dp->d_name[0]=='.' && dp->d_name[1]=='.' && dp->d_name[2]=='\0'))
673 			continue;
674 		c1 = endofname;
675 		*c1++ = '/';
676 		strcpy(c1, dp->d_name);
677 		Fname = endofname+1;
678 		if(!descend(name, Fname, exlist)) {
679 			*endofname = '\0';
680 			chdir(Home);
681 			if(chdir(Pathname) == -1) {
682 				fprintf(stderr, "find: bad directory tree\n");
683 				exit(1);
684 			}
685 		}
686 	}
687 	rv = 1;
688 ret:
689 	if(dir)
690 		closedir(dir);
691 	if(chdir("..") == -1) {
692 		*endofname = '\0';
693 		fprintf(stderr, "find: bad directory <%s>\n", name);
694 		rv = 1;
695 	}
696 	return(rv);
697 }
698 
699 gmatch(s, p) /* string match as in glob */
700 register char *s, *p;
701 {
702 	if (*s=='.' && *p!='.') return(0);
703 	return amatch(s, p);
704 }
705 
706 amatch(s, p)
707 register char *s, *p;
708 {
709 	register cc;
710 	int scc, k;
711 	int c, lc;
712 
713 	scc = *s;
714 	lc = 077777;
715 	switch (c = *p) {
716 
717 	case '[':
718 		k = 0;
719 		while (cc = *++p) {
720 			switch (cc) {
721 
722 			case ']':
723 				if (k)
724 					return(amatch(++s, ++p));
725 				else
726 					return(0);
727 
728 			case '-':
729 				cc = p[1];
730 				k |= lc <= scc && scc <= cc;
731 			}
732 			if (scc==(lc=cc)) k++;
733 		}
734 		return(0);
735 
736 	case '?':
737 	caseq:
738 		if(scc) return(amatch(++s, ++p));
739 		return(0);
740 	case '*':
741 		return(umatch(s, ++p));
742 	case 0:
743 		return(!scc);
744 	}
745 	if (c==scc) goto caseq;
746 	return(0);
747 }
748 
749 umatch(s, p)
750 register char *s, *p;
751 {
752 	if(*p==0) return(1);
753 	while(*s)
754 		if (amatch(s++, p)) return(1);
755 	return(0);
756 }
757 
758 bwrite(rp, c)
759 register short *rp;
760 register c;
761 {
762 	register short *wp = Wp;
763 
764 	c = (c+1) >> 1;
765 	while(c--) {
766 		if(!Wct) {
767 again:
768 			if(write(Cpio, (char *)Dbuf, Bufsize)<0) {
769 				Cpio = chgreel(1, Cpio);
770 				goto again;
771 			}
772 			Wct = Bufsize >> 1;
773 			wp = Dbuf;
774 			++Blocks;
775 		}
776 		*wp++ = *rp++;
777 		--Wct;
778 	}
779 	Wp = wp;
780 }
781 chgreel(x, fl)
782 {
783 	register f;
784 	char str[22];
785 	FILE *devtty;
786 	struct stat statb;
787 	extern errno;
788 
789 	fprintf(stderr, "find: errno: %d, ", errno);
790 	fprintf(stderr, "find: can't %s\n", x? "write output": "read input");
791 	fstat(fl, &statb);
792 	if((statb.st_mode&S_IFMT) != S_IFCHR)
793 		exit(1);
794 again:
795 	fprintf(stderr, "If you want to go on, type device/file name %s\n",
796 		"when ready");
797 	devtty = fopen(_PATH_TTY, "r");
798 	fgets(str, 20, devtty);
799 	str[strlen(str) - 1] = '\0';
800 	if(!*str)
801 		exit(1);
802 	close(fl);
803 	if((f = open(str, x? 1: 0)) < 0) {
804 		fprintf(stderr, "That didn't work");
805 		fclose(devtty);
806 		goto again;
807 	}
808 	return f;
809 }
810 
811 #ifdef	AMES
812 /*
813  * 'fastfind' scans a file list for the full pathname of a file
814  * given only a piece of the name.  The list has been processed with
815  * with "front-compression" and bigram coding.  Front compression reduces
816  * space by a factor of 4-5, bigram coding by a further 20-25%.
817  * The codes are:
818  *
819  *	0-28	likeliest differential counts + offset to make nonnegative
820  *	30	switch code for out-of-range count to follow in next word
821  *	128-255 bigram codes (128 most common, as determined by 'updatedb')
822  *	32-127  single character (printable) ascii residue (ie, literal)
823  *
824  * A novel two-tiered string search technique is employed:
825  *
826  * First, a metacharacter-free subpattern and partial pathname is
827  * matched BACKWARDS to avoid full expansion of the pathname list.
828  * The time savings is 40-50% over forward matching, which cannot efficiently
829  * handle overlapped search patterns and compressed path residue.
830  *
831  * Then, the actual shell glob-style regular expression (if in this form)
832  * is matched against the candidate pathnames using the slower routines
833  * provided in the standard 'find'.
834  */
835 
836 #include "find.h"
837 
838 #define	YES	1
839 #define	NO	0
840 
841 fastfind ( pathpart )
842 	char pathpart[];
843 {
844 	register char *p, *s;
845 	register int c;
846 	char *q, *index(), *patprep();
847 	int count = 0, found = NO, globflag;
848 	FILE *fp, *fopen();
849 	char *patend, *cutoff;
850 	char path[MAXPATHLEN];
851 	char bigram1[NBG], bigram2[NBG];
852 
853 	if ( (fp = fopen ( _PATH_FCODES, "r" )) == NULL ) {
854 		perror( _PATH_FCODES );
855 		exit ( 1 );
856 	}
857 	for ( c = 0, p = bigram1, s = bigram2; c < NBG; c++ )
858 		p[c] = getc ( fp ),  s[c] = getc ( fp );
859 
860 	p = pathpart;
861 	globflag = index ( p, '*' ) || index ( p, '?' ) || index ( p, '[' );
862 	patend = patprep ( p );
863 
864 	for ( c = getc ( fp ); c != EOF; ) {
865 
866 		count += ( (c == SWITCH) ? getw ( fp ) : c ) - OFFSET;
867 
868 		for ( p = path + count; (c = getc ( fp )) > SWITCH; )	/* overlay old path */
869 			if ( c < PARITY )
870 				*p++ = c;
871 			else {			/* bigrams are parity-marked */
872 				c &= PARITY-1;
873 				*p++ = bigram1[c], *p++ = bigram2[c];
874 			}
875 		*p-- = NULL;
876 		cutoff = ( found ? path : path + count );
877 
878 		for ( found = NO, s = p; s >= cutoff; s-- )
879 			if ( *s == *patend ) {		/* fast first char check */
880 				for ( p = patend - 1, q = s - 1; *p != NULL; p--, q-- )
881 					if ( *q != *p )
882 						break;
883 				if ( *p == NULL ) {	/* success on fast match */
884 					found = YES;
885 					if ( globflag == NO || amatch ( path, pathpart ) )
886 						puts ( path );
887 					break;
888 				}
889 			}
890 	}
891 }
892 
893 /*
894     extract last glob-free subpattern in name for fast pre-match;
895     prepend '\0' for backwards match; return end of new pattern
896 */
897 static char globfree[100];
898 
899 char *
900 patprep ( name )
901 	char *name;
902 {
903 	register char *p, *endmark;
904 	register char *subp = globfree;
905 
906 	*subp++ = '\0';
907 	p = name + strlen ( name ) - 1;
908 	/*
909 	   skip trailing metacharacters (and [] ranges)
910 	*/
911 	for ( ; p >= name; p-- )
912 		if ( index ( "*?", *p ) == 0 )
913 			break;
914 	if ( p < name )
915 		p = name;
916 	if ( *p == ']' )
917 		for ( p--; p >= name; p-- )
918 			if ( *p == '[' ) {
919 				p--;
920 				break;
921 			}
922 	if ( p < name )
923 		p = name;
924 	/*
925 	   if pattern has only metacharacters,
926 	   check every path (force '/' search)
927 	*/
928 	if ( (p == name) && index ( "?*[]", *p ) != 0 )
929 		*subp++ = '/';
930 	else {
931 		for ( endmark = p; p >= name; p-- )
932 			if ( index ( "]*?", *p ) != 0 )
933 				break;
934 		for ( ++p; (p <= endmark) && subp < (globfree + sizeof ( globfree )); )
935 			*subp++ = *p++;
936 	}
937 	*subp = '\0';
938 	return ( --subp );
939 }
940 #endif
941 
942 /* rest should be done with nameserver or database */
943 
944 #include <pwd.h>
945 #include <grp.h>
946 #include <utmp.h>
947 
948 struct	utmp utmp;
949 #define	NMAX	(sizeof (utmp.ut_name))
950 #define SCPYN(a, b)	strncpy(a, b, NMAX)
951 
952 #define NUID	64
953 #define NGID	300
954 
955 struct ncache {
956 	int	uid;
957 	char	name[NMAX+1];
958 } nc[NUID];
959 char	outrangename[NMAX+1];
960 int	outrangeuid = -1;
961 char	groups[NGID][NMAX+1];
962 char	outrangegroup[NMAX+1];
963 int	outrangegid = -1;
964 
965 /*
966  * This function assumes that the password file is hashed
967  * (or some such) to allow fast access based on a name key.
968  * If this isn't true, duplicate the code for getgroup().
969  */
970 char *
971 getname(uid)
972 {
973 	register struct passwd *pw;
974 	struct passwd *getpwent();
975 	register int cp;
976 
977 #if	(((NUID) & ((NUID) - 1)) != 0)
978 	cp = uid % (NUID);
979 #else
980 	cp = uid & ((NUID) - 1);
981 #endif
982 	if (uid >= 0 && nc[cp].uid == uid && nc[cp].name[0])
983 		return (nc[cp].name);
984 	pw = getpwuid(uid);
985 	if (!pw)
986 		return (0);
987 	nc[cp].uid = uid;
988 	SCPYN(nc[cp].name, pw->pw_name);
989 	return (nc[cp].name);
990 }
991 
992 char *
993 getgroup(gid)
994 {
995 	register struct group *gr;
996 	static init;
997 	struct group *getgrent();
998 
999 	if (gid >= 0 && gid < NGID && groups[gid][0])
1000 		return (&groups[gid][0]);
1001 	if (gid >= 0 && gid == outrangegid)
1002 		return (outrangegroup);
1003 rescan:
1004 	if (init == 2) {
1005 		if (gid < NGID)
1006 			return (0);
1007 		setgrent();
1008 		while (gr = getgrent()) {
1009 			if (gr->gr_gid != gid)
1010 				continue;
1011 			outrangegid = gr->gr_gid;
1012 			SCPYN(outrangegroup, gr->gr_name);
1013 			endgrent();
1014 			return (outrangegroup);
1015 		}
1016 		endgrent();
1017 		return (0);
1018 	}
1019 	if (init == 0)
1020 		setgrent(), init = 1;
1021 	while (gr = getgrent()) {
1022 		if (gr->gr_gid < 0 || gr->gr_gid >= NGID) {
1023 			if (gr->gr_gid == gid) {
1024 				outrangegid = gr->gr_gid;
1025 				SCPYN(outrangegroup, gr->gr_name);
1026 				return (outrangegroup);
1027 			}
1028 			continue;
1029 		}
1030 		if (groups[gr->gr_gid][0])
1031 			continue;
1032 		SCPYN(groups[gr->gr_gid], gr->gr_name);
1033 		if (gr->gr_gid == gid)
1034 			return (&groups[gid][0]);
1035 	}
1036 	init = 2;
1037 	goto rescan;
1038 }
1039 
1040 int
1041 getuid(username)
1042 	char *username;
1043 {
1044 	register struct passwd *pw;
1045 	struct passwd *getpwnam();
1046 
1047 	pw = getpwnam(username);
1048 	if (pw != NULL)
1049 		return (pw->pw_uid);
1050 	else
1051 		return (-1);
1052 }
1053 
1054 int
1055 getgid(groupname)
1056 	char *groupname;
1057 {
1058 	register struct group *gr;
1059 	struct group *getgrnam();
1060 
1061 	gr = getgrnam(groupname);
1062 	if (gr != NULL)
1063 		return (gr->gr_gid);
1064 	else
1065 		return (-1);
1066 }
1067 
1068 #define permoffset(who)		((who) * 3)
1069 #define permission(who, type)	((type) >> permoffset(who))
1070 #define kbytes(bytes)		(((bytes) + 1023) / 1024)
1071 
1072 list(file, stp)
1073 	char *file;
1074 	register struct stat *stp;
1075 {
1076 	char pmode[32], uname[32], gname[32], fsize[32], ftime[32];
1077 	char *getname(), *getgroup(), *ctime();
1078 	static long special[] = { S_ISUID, 's', S_ISGID, 's', S_ISVTX, 't' };
1079 	static time_t sixmonthsago = -1;
1080 #ifdef	S_IFLNK
1081 	char flink[MAXPATHLEN + 1];
1082 #endif
1083 	register int who;
1084 	register char *cp;
1085 	time_t now;
1086 
1087 	if (file == NULL || stp == NULL)
1088 		return (-1);
1089 
1090 	time(&now);
1091 	if (sixmonthsago == -1)
1092 		sixmonthsago = now - 6L*30L*24L*60L*60L;
1093 
1094 	switch (stp->st_mode & S_IFMT) {
1095 #ifdef	S_IFDIR
1096 	case S_IFDIR:	/* directory */
1097 		pmode[0] = 'd';
1098 		break;
1099 #endif
1100 #ifdef	S_IFCHR
1101 	case S_IFCHR:	/* character special */
1102 		pmode[0] = 'c';
1103 		break;
1104 #endif
1105 #ifdef	S_IFBLK
1106 	case S_IFBLK:	/* block special */
1107 		pmode[0] = 'b';
1108 		break;
1109 #endif
1110 #ifdef	S_IFLNK
1111 	case S_IFLNK:	/* symbolic link */
1112 		pmode[0] = 'l';
1113 		break;
1114 #endif
1115 #ifdef	S_IFSOCK
1116 	case S_IFSOCK:	/* socket */
1117 		pmode[0] = 's';
1118 		break;
1119 #endif
1120 #ifdef	S_IFREG
1121 	case S_IFREG:	/* regular */
1122 #endif
1123 	default:
1124 		pmode[0] = '-';
1125 		break;
1126 	}
1127 
1128 	for (who = 0; who < 3; who++) {
1129 		if (stp->st_mode & permission(who, S_IREAD))
1130 			pmode[permoffset(who) + 1] = 'r';
1131 		else
1132 			pmode[permoffset(who) + 1] = '-';
1133 
1134 		if (stp->st_mode & permission(who, S_IWRITE))
1135 			pmode[permoffset(who) + 2] = 'w';
1136 		else
1137 			pmode[permoffset(who) + 2] = '-';
1138 
1139 		if (stp->st_mode & special[who * 2])
1140 			pmode[permoffset(who) + 3] = special[who * 2 + 1];
1141 		else if (stp->st_mode & permission(who, S_IEXEC))
1142 			pmode[permoffset(who) + 3] = 'x';
1143 		else
1144 			pmode[permoffset(who) + 3] = '-';
1145 	}
1146 	pmode[permoffset(who) + 1] = '\0';
1147 
1148 	cp = getname(stp->st_uid);
1149 	if (cp != NULL)
1150 		sprintf(uname, "%-9.9s", cp);
1151 	else
1152 		sprintf(uname, "%-9d", stp->st_uid);
1153 
1154 	cp = getgroup(stp->st_gid);
1155 	if (cp != NULL)
1156 		sprintf(gname, "%-9.9s", cp);
1157 	else
1158 		sprintf(gname, "%-9d", stp->st_gid);
1159 
1160 	if (pmode[0] == 'b' || pmode[0] == 'c')
1161 		sprintf(fsize, "%3d,%4d",
1162 			major(stp->st_rdev), minor(stp->st_rdev));
1163 	else {
1164 		sprintf(fsize, "%8ld", stp->st_size);
1165 #ifdef	S_IFLNK
1166 		if (pmode[0] == 'l') {
1167 			/*
1168 			 * Need to get the tail of the file name, since we have
1169 			 * already chdir()ed into the directory of the file
1170 			 */
1171 			cp = rindex(file, '/');
1172 			if (cp == NULL)
1173 				cp = file;
1174 			else
1175 				cp++;
1176 			who = readlink(cp, flink, sizeof flink - 1);
1177 			if (who >= 0)
1178 				flink[who] = '\0';
1179 			else
1180 				flink[0] = '\0';
1181 		}
1182 #endif
1183 	}
1184 
1185 	cp = ctime(&stp->st_mtime);
1186 	if (stp->st_mtime < sixmonthsago || stp->st_mtime > now)
1187 		sprintf(ftime, "%-7.7s %-4.4s", cp + 4, cp + 20);
1188 	else
1189 		sprintf(ftime, "%-12.12s", cp + 4);
1190 
1191 	printf("%5lu %4ld %s %2d %s%s%s %s %s%s%s\n",
1192 		stp->st_ino,				/* inode #	*/
1193 #ifdef	S_IFSOCK
1194 		(long) kbytes(dbtob(stp->st_blocks)),	/* kbytes       */
1195 #else
1196 		(long) kbytes(stp->st_size),		/* kbytes       */
1197 #endif
1198 		pmode,					/* protection	*/
1199 		stp->st_nlink,				/* # of links	*/
1200 		uname,					/* owner	*/
1201 		gname,					/* group	*/
1202 		fsize,					/* # of bytes	*/
1203 		ftime,					/* modify time	*/
1204 		file,					/* name		*/
1205 #ifdef	S_IFLNK
1206 		(pmode[0] == 'l') ? " -> " : "",
1207 		(pmode[0] == 'l') ? flink  : ""		/* symlink	*/
1208 #else
1209 		"",
1210 		""
1211 #endif
1212 	);
1213 
1214 	return (0);
1215 }
1216