first cut at real Jewish calendar
[unix-history] / usr / src / usr.bin / find / find.c
CommitLineData
b98da692 1#ifndef lint
3fae70c0 2static char *sccsid = "@(#)find.c 4.23 (Berkeley) %G%";
b98da692 3#endif
46b257b1 4
365a571b 5#include <sys/param.h>
c08d5d47
BJ
6#include <sys/dir.h>
7#include <sys/stat.h>
d0e5d403
KB
8#include <stdio.h>
9#include "pathnames.h"
b98da692 10
c08d5d47
BJ
11#define A_DAY 86400L /* a day full of seconds */
12#define EQ(x, y) (strcmp(x, y)==0)
13
14int Randlast;
e8e4232e 15char Pathname[MAXPATHLEN+1];
c08d5d47 16
b98da692
S
17#define MAXNODES 100
18
c08d5d47
BJ
19struct anode {
20 int (*F)();
21 struct anode *L, *R;
b98da692 22} Node[MAXNODES];
c08d5d47
BJ
23int Nn; /* number of nodes */
24char *Fname;
25long Now;
26int Argc,
27 Ai,
28 Pi;
29char **Argv;
30/* cpio stuff */
31int Cpio;
32short *Buf, *Dbuf, *Wp;
33int Bufsize = 5120;
34int Wct = 2560;
35
36long Newer;
37
b98da692 38int Xdev = 1; /* true if SHOULD cross devices (file systems) */
f731b76e 39int follow; /* true if SHOULD descend symlinked directories */
b98da692
S
40struct stat Devstat; /* stats of each argument path's file system */
41
c08d5d47
BJ
42struct stat Statb;
43
44struct anode *exp(),
45 *e1(),
46 *e2(),
47 *e3(),
48 *mk();
49char *nxtarg();
b98da692 50char Home[MAXPATHLEN + 1];
c08d5d47
BJ
51long Blocks;
52char *rindex();
53char *sbrk();
46b257b1
KM
54
55/*
25d63f56
JK
56 * SEE ALSO: code.c, updatedb, bigram.c
57 * Usenix ;login:, Vol 8, No 1, February/March, 1983, p. 8.
46b257b1
KM
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
9f796964 64 * compressed by updatedb (v.i.z.) The effect of
46b257b1
KM
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
76main(argc, argv)
77 int argc;
78 char *argv[];
c08d5d47
BJ
79{
80 struct anode *exlist;
81 int paths;
82 register char *cp, *sp = 0;
b98da692 83#ifdef SUID_PWD
c08d5d47 84 FILE *pwd, *popen();
b98da692 85#endif
c08d5d47 86
46b257b1
KM
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
c08d5d47 98 time(&Now);
d0e5d403
KB
99 setpassent(1);
100 setgroupent(1);
b98da692 101#ifdef SUID_PWD
c08d5d47 102 pwd = popen("pwd", "r");
b98da692 103 fgets(Home, sizeof Home, pwd);
c08d5d47
BJ
104 pclose(pwd);
105 Home[strlen(Home) - 1] = '\0';
b98da692
S
106#else
107 if (getwd(Home) == NULL) {
108 fprintf(stderr, "find: Can't get current working directory\n");
109 exit(1);
110 }
111#endif
c08d5d47
BJ
112 Argc = argc; Argv = argv;
113 if(argc<3) {
114usage: 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;
b98da692
S
144 if (!Xdev)
145 stat(Pathname, &Devstat);
c08d5d47
BJ
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
159struct 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}
171struct 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")) {
179And:
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}
188struct 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}
201struct anode *e3() { /* parse parens and predicates */
202 int exeq(), ok(), glob(), mtime(), atime(), user(),
203 group(), size(), perm(), links(), print(),
f731b76e 204 type(), ino(), cpio(), newer(),
b98da692 205 nouser(), nogroup(), ls(), dummy();
c08d5d47
BJ
206 struct anode *p1;
207 int i;
6543b56c
JL
208 register char *a, *b;
209 register int s;
c08d5d47
BJ
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 }
b98da692
S
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 }
f731b76e
MT
235 else if (EQ(a, "-follow")) {
236 follow=1;
237 return mk(dummy, (struct anode *)0, (struct anode *)0);
238 }
c08d5d47
BJ
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")) {
b98da692 249 if((i=getuid(b)) == -1) {
68791ee2 250 if(gmatch(b, "[0-9]*"))
c08d5d47
BJ
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")) {
b98da692 260 if((i=getgid(b)) == -1) {
85e30eca 261 if(gmatch(b, "[0-9]*"))
c08d5d47
BJ
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 :
773a4280
KM
283 s=='f' ? S_IFREG :
284 s=='l' ? S_IFLNK :
e8e4232e 285 s=='s' ? S_IFSOCK :
c08d5d47
BJ
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 }
316err: fprintf(stderr, "find: bad option < %s >\n", a);
317 exit(1);
318}
319struct anode *mk(f, l, r)
320int (*f)();
321struct anode *l, *r;
322{
b98da692
S
323 if (Nn >= MAXNODES) {
324 fprintf(stderr, "find: Too many options\n");
325 exit(1);
326 }
327
c08d5d47
BJ
328 Node[Nn].F = f;
329 Node[Nn].L = l;
330 Node[Nn].R = r;
331 return(&(Node[Nn++]));
332}
333
334char *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 */
350and(p)
351register struct anode *p;
352{
353 return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0);
354}
355or(p)
356register struct anode *p;
357{
358 return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0);
359}
360not(p)
361register struct anode *p;
362{
363 return( !((*p->L->F)(p->L)));
364}
365glob(p)
366register struct { int f; char *pat; } *p;
367{
368 return(gmatch(Fname, p->pat));
369}
b98da692
S
370print(p)
371struct anode *p;
c08d5d47
BJ
372{
373 puts(Pathname);
374 return(1);
375}
376mtime(p)
377register struct { int f, t, s; } *p;
378{
379 return(scomp((int)((Now - Statb.st_mtime) / A_DAY), p->t, p->s));
380}
381atime(p)
382register struct { int f, t, s; } *p;
383{
384 return(scomp((int)((Now - Statb.st_atime) / A_DAY), p->t, p->s));
385}
386user(p)
387register struct { int f, u, s; } *p;
388{
389 return(scomp(Statb.st_uid, p->u, p->s));
390}
b98da692
S
391nouser(p)
392struct anode *p;
393{
394 char *getname();
395
396 return (getname(Statb.st_uid) == NULL);
397}
c08d5d47
BJ
398ino(p)
399register struct { int f, u, s; } *p;
400{
401 return(scomp((int)Statb.st_ino, p->u, p->s));
402}
403group(p)
404register struct { int f, u; } *p;
405{
406 return(p->u == Statb.st_gid);
407}
b98da692
S
408nogroup(p)
409struct anode *p;
410{
411 char *getgroup();
412
413 return (getgroup(Statb.st_gid) == NULL);
414}
c08d5d47
BJ
415links(p)
416register struct { int f, link, s; } *p;
417{
418 return(scomp(Statb.st_nlink, p->link, p->s));
419}
420size(p)
421register struct { int f, sz, s; } *p;
422{
423 return(scomp((int)((Statb.st_size+511)>>9), p->sz, p->s));
424}
425perm(p)
426register 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}
432type(p)
433register struct { int f, per, s; } *p;
434{
435 return((Statb.st_mode&S_IFMT)==p->per);
436}
437exeq(p)
438register struct { int f, com; } *p;
439{
440 fflush(stdout); /* to flush possible `-print' */
441 return(doex(p->com));
442}
443ok(p)
444struct { 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];}
458union { long l; short s[2]; char c[4]; } U;
459long mklong(v)
460short 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}
b98da692
S
469cpio(p)
470struct anode *p;
c08d5d47
BJ
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) {
513cerror:
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}
b98da692
S
527newer(p)
528struct anode *p;
c08d5d47
BJ
529{
530 return Statb.st_mtime > Newer;
531}
b98da692
S
532ls(p)
533struct anode *p;
534{
535 list(Pathname, &Statb);
536 return (1);
537}
538dummy(p)
539struct anode *p;
540{
541 /* dummy */
542 return (1);
543}
c08d5d47
BJ
544
545/* support functions */
546scomp(a, b, s) /* funny signed compare */
547register a, b;
548register char s;
549{
550 if(s == '+')
551 return(a > b);
552 if(s == '-')
553 return(a < (b * -1));
554 return(a == b);
555}
556
557doex(com)
558{
559 register np;
560 register char *na;
561 static char *nargv[50];
562 static ccode;
ea68db4b 563 register int w, pid, omask;
c08d5d47
BJ
564
565 ccode = np = 0;
566 while (na=Argv[com++]) {
b98da692 567 if(np >= sizeof nargv / sizeof *nargv - 1) break;
c08d5d47
BJ
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);
b98da692
S
574 switch (pid = vfork()) {
575 case -1:
576 perror("find: Can't fork");
577 exit(1);
578 break;
579
580 case 0:
c08d5d47
BJ
581 chdir(Home);
582 execvp(nargv[0], nargv, np);
ea68db4b
KM
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);
b98da692
S
590 break;
591
592 default:
ea68db4b 593 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT));
b98da692
S
594 while ((w = wait(&ccode)) != pid && w != -1)
595 ;
ea68db4b
KM
596 (void) sigsetmask(omask);
597 if ((ccode & 0177) == SIGUSR1)
b98da692 598 exit(1);
b98da692 599 return (ccode != 0 ? 0 : 1);
c08d5d47 600 }
c08d5d47
BJ
601}
602
603getunum(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
635descend(name, fname, exlist)
365a571b
KM
636 struct anode *exlist;
637 char *name, *fname;
c08d5d47 638{
365a571b 639 DIR *dir = NULL;
c08d5d47 640 register struct direct *dp;
365a571b 641 register char *c1;
c08d5d47
BJ
642 int rv = 0;
643 char *endofname;
644
f731b76e 645 if ((follow?stat(fname, &Statb):lstat(fname, &Statb))<0) {
c08d5d47
BJ
646 fprintf(stderr, "find: bad status < %s >\n", name);
647 return(0);
648 }
649 (*exlist->F)(exlist);
b98da692
S
650 if((Statb.st_mode&S_IFMT)!=S_IFDIR ||
651 !Xdev && Devstat.st_dev != Statb.st_dev)
c08d5d47
BJ
652 return(1);
653
44ac974b
KB
654 if (Statb.st_nlink == 2 && exlist->F == and &&
655 exlist->L->F == type && ((int) (exlist->L->L)) == S_IFDIR)
656 return(1);
657
365a571b
KM
658 for (c1 = name; *c1; ++c1);
659 if (*(c1-1) == '/')
c08d5d47
BJ
660 --c1;
661 endofname = c1;
c08d5d47 662
365a571b 663 if (chdir(fname) == -1)
c08d5d47 664 return(0);
365a571b
KM
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);
c08d5d47
BJ
684 }
685 }
686 }
687 rv = 1;
688ret:
689 if(dir)
365a571b 690 closedir(dir);
c08d5d47
BJ
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
699gmatch(s, p) /* string match as in glob */
700register char *s, *p;
701{
702 if (*s=='.' && *p!='.') return(0);
703 return amatch(s, p);
704}
705
706amatch(s, p)
707register 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 '-':
6c57dc2b
KM
729 cc = p[1];
730 k |= lc <= scc && scc <= cc;
c08d5d47
BJ
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
749umatch(s, p)
750register 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
758bwrite(rp, c)
759register short *rp;
760register c;
761{
762 register short *wp = Wp;
763
764 c = (c+1) >> 1;
765 while(c--) {
766 if(!Wct) {
767again:
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}
781chgreel(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);
794again:
795 fprintf(stderr, "If you want to go on, type device/file name %s\n",
796 "when ready");
d0e5d403 797 devtty = fopen(_PATH_TTY, "r");
c08d5d47
BJ
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}
46b257b1
KM
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
25d63f56
JK
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)
46b257b1
KM
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
25d63f56
JK
836#include "find.h"
837
46b257b1
KM
838#define YES 1
839#define NO 0
46b257b1
KM
840
841fastfind ( pathpart )
842 char pathpart[];
843{
844 register char *p, *s;
845 register int c;
846 char *q, *index(), *patprep();
25d63f56 847 int count = 0, found = NO, globflag;
46b257b1
KM
848 FILE *fp, *fopen();
849 char *patend, *cutoff;
25d63f56
JK
850 char path[MAXPATHLEN];
851 char bigram1[NBG], bigram2[NBG];
46b257b1 852
d0e5d403 853 if ( (fp = fopen ( _PATH_FCODES, "r" )) == NULL ) {
25d63f56 854 perror( _PATH_FCODES );
46b257b1
KM
855 exit ( 1 );
856 }
25d63f56
JK
857 for ( c = 0, p = bigram1, s = bigram2; c < NBG; c++ )
858 p[c] = getc ( fp ), s[c] = getc ( fp );
41a1140a 859
25d63f56
JK
860 p = pathpart;
861 globflag = index ( p, '*' ) || index ( p, '?' ) || index ( p, '[' );
862 patend = patprep ( p );
46b257b1 863
3fae70c0 864 for ( c = getc ( fp ); c != EOF; ) {
46b257b1 865
25d63f56 866 count += ( (c == SWITCH) ? getw ( fp ) : c ) - OFFSET;
46b257b1 867
25d63f56
JK
868 for ( p = path + count; (c = getc ( fp )) > SWITCH; ) /* overlay old path */
869 if ( c < PARITY )
46b257b1 870 *p++ = c;
25d63f56
JK
871 else { /* bigrams are parity-marked */
872 c &= PARITY-1;
873 *p++ = bigram1[c], *p++ = bigram2[c];
874 }
46b257b1 875 *p-- = NULL;
25d63f56 876 cutoff = ( found ? path : path + count );
46b257b1
KM
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/*
084103a7
KM
894 extract last glob-free subpattern in name for fast pre-match;
895 prepend '\0' for backwards match; return end of new pattern
46b257b1
KM
896*/
897static char globfree[100];
898
899char *
084103a7
KM
900patprep ( name )
901 char *name;
46b257b1 902{
084103a7 903 register char *p, *endmark;
46b257b1
KM
904 register char *subp = globfree;
905
084103a7
KM
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 )); )
46b257b1 935 *subp++ = *p++;
084103a7
KM
936 }
937 *subp = '\0';
46b257b1
KM
938 return ( --subp );
939}
940#endif
b98da692
S
941
942/* rest should be done with nameserver or database */
943
944#include <pwd.h>
945#include <grp.h>
946#include <utmp.h>
947
948struct 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
955struct ncache {
956 int uid;
957 char name[NMAX+1];
958} nc[NUID];
959char outrangename[NMAX+1];
960int outrangeuid = -1;
961char groups[NGID][NMAX+1];
962char outrangegroup[NMAX+1];
963int 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 */
970char *
971getname(uid)
972{
973 register struct passwd *pw;
974 struct passwd *getpwent();
975 register int cp;
b98da692 976
2d479d40 977#if (((NUID) & ((NUID) - 1)) != 0)
b98da692
S
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
992char *
993getgroup(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);
1003rescan:
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
1040int
1041getuid(username)
1042 char *username;
1043{
1044 register struct passwd *pw;
1045 struct passwd *getpwnam();
b98da692
S
1046
1047 pw = getpwnam(username);
1048 if (pw != NULL)
1049 return (pw->pw_uid);
1050 else
1051 return (-1);
1052}
1053
1054int
1055getgid(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
1072list(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') {
b1e9ea21
JB
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);
b98da692
S
1177 if (who >= 0)
1178 flink[who] = '\0';
b1e9ea21
JB
1179 else
1180 flink[0] = '\0';
b98da692
S
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}