|
|
1.1 root 1: %{
2: /*
3: * Find all files in the given
4: * directory hierarchies that
5: * satisfy the given expression
6: * primaries.
7: */
8:
9: #include <stdio.h>
10: #include <sys/stat.h>
11: #include <sys/dir.h>
12: #include <pwd.h>
13: #include <grp.h>
14: #include "findnode.h"
15:
16: #define NPRIM (sizeof(primaries)/sizeof(primaries[0]))
17: #define NARG 50
18: #define NRECUR 14 /* Maximum recursion depth before forking */
19: #define NFNAME 600 /* size of filename buffer */
20: #define FILEARG ((char *)EOF)
21: #define DAYSEC (60L*60L*24L) /* seconds in a day */
22: #define inode(f,v) lnode(FUN,f,v,NULL)
23: #define snode(f,s) lnode(FUN,f,0,s)
24: %}
25: %start command
26:
27: %union {
28: NODE *nodeptr;
29: }
30:
31: %left OR
32: %left AND
33: %left '!'
34: %token <nodeptr> NAME PERM TYPE LINKS USER GROUP SIZE INUM
35: %token <nodeptr> ATIME CTIME MTIME EXEC OK PRINT NEWER FUN NOP
36: %type <nodeptr> exp
37:
38: %%
39:
40: command:
41: exp '\n' { if (seflag)
42: code = $1; else
43: code = bnode(AND,$1,snode(xprint,NULL));
44: return;
45: }
46: | '\n' { code = snode(xprint, NULL); return; }
47: ;
48:
49: exp:
50: '(' exp ')' { $$ = $2; }
51: | '!' exp { $$ = bnode('!', $2, NULL); }
52: | exp OR exp { $$ = bnode(OR, $1, $3); }
53: | exp AND exp { $$ = bnode(AND, $1, $3); }
54: | NAME { $$ = snode(xname, next()); }
55: | PERM { $$ = onode(xperm); }
56: | TYPE { $$ = snode(xtype, next()); }
57: | LINKS { $$ = nnode(xlinks); }
58: | USER { $$ = getuser(); }
59: | GROUP { $$ = getgroup(); }
60: | SIZE { $$ = nnode(xsize); }
61: | INUM { $$ = nnode(xinum); }
62: | ATIME { $$ = nnode(xatime); }
63: | CTIME { $$ = nnode(xctime); }
64: | MTIME { $$ = nnode(xmtime); }
65: | EXEC { $$ = enode(0); }
66: | OK { $$ = enode(1); }
67: | PRINT { $$ = snode(xprint, NULL); seflag++; }
68: | NEWER { $$ = getnewer(); }
69: | NOP { $$ = snode(xnop, NULL); seflag++; }
70: ;
71:
72: %%
73: struct primary {
74: char *p_name;
75: int p_lval;
76: } primaries[] = {
77: "-name", NAME,
78: "-perm", PERM,
79: "-type", TYPE,
80: "-links", LINKS,
81: "-user", USER,
82: "-group", GROUP,
83: "-size", SIZE,
84: "-inum", INUM,
85: "-atime", ATIME,
86: "-ctime", CTIME,
87: "-mtime", MTIME,
88: "-exec", EXEC,
89: "-ok", OK,
90: "-print", PRINT,
91: "-newer", NEWER,
92: "-nop", NOP,
93: "-o", OR,
94: "-a", AND,
95: };
96:
97: char **gav;
98: int gac;
99: int depth; /* Recursive depth */
100:
101: struct stat sb;
102: char fname[NFNAME];
103: char *prompt;
104:
105: char toodeep[] = "directory structure too deep to traverse";
106: char nospace[] = "out of memory";
107:
108: time_t curtime;
109:
110: NODE *code;
111: int seflag; /* Set if a side effect (print, exec) found */
112:
113: char *next();
114: NODE *bnode();
115: NODE *enode();
116: NODE *lnode();
117: NODE *nnode();
118: NODE *onode();
119: NODE *getuser();
120: NODE *getgroup();
121: NODE *getnewer();
122: int xname();
123: int xperm();
124: int xtype();
125: int xlinks();
126: int xuser();
127: int xgroup();
128: int xsize();
129: int xinum();
130: int xatime();
131: int xctime();
132: int xmtime();
133: int xnewer();
134: int xexec();
135: int xprint();
136: int xnop();
137: char *getenv();
138:
139: main(argc, argv)
140: char *argv[];
141: {
142: register int i;
143: register char *ap;
144: register int eargc;
145:
146: for (i=1; i<argc; i++) {
147: ap = argv[i];
148: if (*ap == '-')
149: break;
150: if (ap[1]=='\0' && (*ap=='!' || *ap=='('))
151: break;
152: }
153: if ((eargc=i) < 2)
154: usage();
155: gav = argv+i;
156: gac = argc-i;
157: yyparse();
158: time(&curtime);
159: if ((prompt = getenv("PS1")) == NULL)
160: prompt = "> ";
161: for (i=1; i<eargc; i++)
162: find(argv[i]);
163: }
164:
165: /*
166: * Lexical analyser
167: */
168: yylex()
169: {
170: static int binop = 0;
171: static int ntoken = 0;
172: register char *ap;
173: struct primary *pp;
174: register int token;
175:
176: if (ntoken) {
177: token = ntoken;
178: ntoken = 0;
179: } else if ((ap = next()) == NULL)
180: token = '\n';
181: else if (ap[1] == '\0')
182: token = ap[0];
183: else if (*ap == '-') {
184: for (pp = primaries; pp < &primaries[NPRIM]; pp++)
185: if (strcmp(pp->p_name, ap) == 0) {
186: token = pp->p_lval;
187: break;
188: }
189: if (pp >= &primaries[NPRIM])
190: ferr("`%s' is an illegal primary", ap);
191: } else
192: ferr("Illegal expression %s\n", ap);
193: if (binop && token!=')' && token!='\n' && token!=OR && token!=AND) {
194: binop = 0;
195: ntoken = token;
196: return (AND);
197: }
198: if (token!=OR && token!=AND && token!='!' && token!='\n' && token!='(')
199: binop = 1; else
200: binop = 0;
201: return (token);
202: }
203:
204: yyerror()
205: {
206: fprintf(stderr, "Primary expression syntax error\n");
207: usage();
208: }
209:
210: /*
211: * Return the next argument from the arg list.
212: */
213: char *
214: next()
215: {
216: if (gac < 1)
217: return (NULL);
218: gac--;
219: return (*gav++);
220: }
221:
222: /*
223: * Produce a node consisting
224: * of an octal number.
225: */
226: NODE *
227: onode(fun)
228: int (*fun)();
229: {
230: register char *ap;
231: register int num;
232: register NODE *np;
233: register int type;
234: char *aap;
235:
236: if ((ap = next()) == NULL)
237: ferr("Missing octal permission");
238: aap = ap;
239: if (*ap == '-') {
240: ap++;
241: type = -1;
242: } else
243: type = 0;
244: num = 0;
245: while (*ap>='0' && *ap<='7')
246: num = num*8 + *ap++-'0';
247: if (*ap != '\0')
248: ferr("%s: bad octal permission", aap);
249: np = inode(fun, num);
250: np->n_un.n_val = num;
251: np->n_type = type;
252: return (np);
253: }
254:
255: /*
256: * Get a number -- it also may be
257: * prefixed by `+' or `-' to
258: * represent quantities greater or
259: * less.
260: */
261: NODE *
262: nnode(fun)
263: int (*fun);
264: {
265: register char *ap;
266: register int num = 0;
267: register int type = 0;
268: register NODE *np;
269: char *aap;
270:
271: if ((ap = next()) == NULL)
272: ferr("Missing number");
273: aap = ap;
274: if (*ap == '+') {
275: type = 1;
276: ap++;
277: } else if (*ap == '-') {
278: type = -1;
279: ap++;
280: }
281: while (*ap>='0' && *ap<='9')
282: num = num*10 + *ap++ - '0';
283: if (*ap != '\0')
284: ferr("%s: invalid number", aap);
285: np = inode(fun, num);
286: np->n_type = type;
287: return (np);
288: }
289:
290: /*
291: * Get a user name or number.
292: */
293: NODE *
294: getuser()
295: {
296: register struct passwd *pwp;
297: register char *cp;
298: register int uid;
299:
300: if ((cp = next()) == NULL)
301: ferr("Missing username");
302: if (*cp>='0' && *cp<='9')
303: uid = atoi(cp);
304: else {
305: if ((pwp = getpwnam(cp)) == NULL)
306: ferr("%s: bad user name", cp);
307: uid = pwp->pw_uid;
308: }
309: return (lnode(FUN, xuser, uid, NULL));
310: }
311:
312: /*
313: * Get group
314: */
315: NODE *
316: getgroup()
317: {
318: register struct group *grp;
319: register char *cp;
320: register int gid;
321:
322: if ((cp = next()) == NULL)
323: ferr("Missing group name");
324: if (*cp>='0' && *cp<='9')
325: gid = atoi(cp);
326: else {
327: if ((grp = getgrnam(cp)) == NULL)
328: ferr("%s: bad group name", cp);
329: gid = grp->gr_gid;
330: }
331: return (lnode(FUN, xgroup, gid, NULL));
332: }
333:
334: /*
335: * Get the time for the file used in
336: * the `-newer' primary.
337: */
338: NODE *
339: getnewer()
340: {
341: register NODE *np;
342: register char *fn;
343:
344: if ((fn = next()) == NULL)
345: ferr("Missing filename for `-newer'");
346: if (stat(fn, &sb) < 0)
347: ferr("%s: nonexistent", fn);
348: np = inode(xnewer, 0);
349: np->n_un.n_time = sb.st_mtime;
350: return (np);
351: }
352:
353: /*
354: * Build an expression tree node (non-leaf).
355: */
356: NODE *
357: bnode(op, left, right)
358: int op;
359: NODE *left, *right;
360: {
361: register NODE *np;
362:
363: if ((np = malloc(sizeof (NODE))) == NULL)
364: ferr(nospace);
365: np->n_op = op;
366: np->n_left = left;
367: np->n_right = right;
368: np->n_un.n_val = 0;
369: return (np);
370: }
371:
372: /*
373: * Build a leaf node in expression tree.
374: */
375: NODE *
376: lnode(op, fn, val, str)
377: int (*fn)();
378: char *str;
379: {
380: register NODE *np;
381:
382: if ((np = malloc(sizeof (NODE))) == NULL)
383: ferr(nospace);
384: np->n_left = np->n_right = NULL;
385: np->n_op = op;
386: np->n_fun = fn;
387: if (str != NULL)
388: np->n_un.n_str = str; else
389: np->n_un.n_val = val;
390: return (np);
391: }
392:
393: /*
394: * Build an execution node
395: * for -ok or -exec.
396: */
397: NODE *
398: enode(type)
399: {
400: register NODE *np;
401: register char **app;
402: register char *ap;
403:
404: seflag++;
405: np = snode(xexec, NULL);
406: np->n_type = type;
407: if ((np->n_un.n_strp = (char**)malloc(sizeof(char*[NARG])))==NULL)
408: ferr(nospace);
409: app = np->n_un.n_strp;
410: for (;;) {
411: if ((ap = next()) == NULL)
412: ferr("Non-terminated -exec or -ok command list");
413: if (strcmp(ap, "{}") == 0)
414: ap = FILEARG;
415: else if (strcmp(ap, ";") == 0)
416: break;
417: if (app-np->n_un.n_strp >= NARG-1)
418: ferr("Too many -exec or -ok command arguments");
419: *app++ = ap;
420: }
421: *app = NULL;
422: return (np);
423: }
424:
425: /*
426: * Execute find on a single
427: * pathname hierarchy.
428: */
429: find(dir)
430: char *dir;
431: {
432: register char *ep, *cp;
433:
434: cp = dir;
435: ep = fname;
436: while (*cp)
437: *ep++ = *cp++;
438: *ep = '\0';
439: if (stat(dir, &sb) < 0)
440: ferr("Cannot find directory `%s'", dir);
441: if ((sb.st_mode&S_IFMT) != S_IFDIR)
442: ferr("%s: not a directory", dir);
443: fentry(ep, &sb);
444: }
445:
446: /*
447: * The pointer is the end pointer
448: * into the fname buffer.
449: * And the stat buffer is passed to this
450: * which traverses the directory hierarchy.
451: */
452: fentry(ep, sbp)
453: char *ep;
454: struct stat *sbp;
455: {
456: char *buildname();
457: register char *np;
458: register struct direct *dp;
459: register int nb;
460: int fd;
461: int dirflag;
462: char *iobuf;
463:
464: if (sbp != NULL) {
465: dirflag = (sbp->st_mode&S_IFMT)==S_IFDIR;
466: execute(code);
467: } else
468: dirflag = 1;
469: if (dirflag) {
470: if (++depth >= NRECUR) {
471: depth = 0;
472: ffork(ep, sbp);
473: return;
474: }
475: if ((fd = open(fname, 0)) < 0) {
476: fmsg("%s: cannot open directory", fname);
477: return;
478: }
479: if ((iobuf = malloc(BUFSIZ)) == NULL)
480: ferr(nospace);
481: while ((nb = read(fd, iobuf, BUFSIZ)) > 0)
482: for (dp = iobuf; dp < &iobuf[nb]; dp++) {
483: if (dp->d_ino == 0)
484: continue;
485: np = dp->d_name;
486: if (*np++=='.'
487: && (*np=='\0' || (*np++=='.' && *np=='\0')))
488: continue;
489: np = buildname(dp, ep);
490: if (stat(fname, &sb) < 0) {
491: fmsg("%s: cannot stat", fname);
492: continue;
493: }
494: fentry(np, &sb);
495: }
496: if (nb < 0)
497: fmsg("%s: directory read error", fname);
498: free(iobuf);
499: *ep = '\0';
500: close(fd);
501: depth--;
502: }
503: }
504:
505: /*
506: * Fork to do a find on recursive directory
507: * structure that is too deep to fit into
508: * user's open files.
509: */
510: ffork(ep, sbp)
511: char *ep;
512: struct stat *sbp;
513: {
514: register int i;
515: register int pid;
516: int status;
517:
518: fflush(stdout);
519: if ((pid = fork()) < 0) {
520: fmsg(toodeep);
521: return;
522: }
523: if (pid) {
524: while (wait(&status) >= 0)
525: ;
526: if (status)
527: fmsg("panic: child failed: %o", status);
528: return;
529: }
530: for (i=3; i<_NFILE; i++)
531: close(i);
532: fentry(ep, (struct stat *)NULL);
533: fflush(stdout);
534: exit(0);
535: }
536:
537: /*
538: * Build up the next entry
539: * in the name.
540: */
541: char *
542: buildname(dp, ep)
543: struct direct *dp;
544: register char *ep;
545: {
546: register char *cp = dp->d_name;
547: register unsigned n = DIRSIZ;
548:
549: if (ep+DIRSIZ+2 >= &fname[NFNAME]) {
550: fmsg(toodeep);
551: return (NULL);
552: }
553: if (ep>fname && ep[-1]!='/')
554: *ep++ = '/';
555: do {
556: if (*cp == '\0')
557: break;
558: *ep++ = *cp++;
559: } while (--n);
560: *ep = '\0';
561: return (ep);
562: }
563:
564: /*
565: * Execute compiled code.
566: */
567: execute(np)
568: register NODE *np;
569: {
570: switch (np->n_op) {
571: case AND:
572: if (execute(np->n_left) && execute(np->n_right))
573: return (1);
574: return (0);
575:
576: case OR:
577: if (execute(np->n_left) || execute(np->n_right))
578: return (1);
579: return (0);
580:
581: case '!':
582: return (!execute(np->n_left));
583:
584: case FUN:
585: return ((*np->n_fun)(np));
586:
587: default:
588: ferr("Panic: bad expression tree (op %d)", np->n_op);
589: }
590: /* NOTREACHED */
591: }
592:
593: /*
594: * Check for a match on the filename
595: */
596: xname(np)
597: NODE *np;
598: {
599: register char *ep;
600:
601: ep = fname;
602: while (*ep != '\0')
603: ep++;
604: while (ep>fname && *--ep!='/')
605: ;
606: if (*ep == '/')
607: ep++;
608: return (pnmatch(ep, np->n_un.n_str, 0));
609: }
610:
611: /*
612: * Compare the mode for a match again
613: * octal number `np->n_un.n_val'.
614: */
615: xperm(np)
616: NODE *np;
617: {
618: register int onum;
619: register int mode;
620:
621: mode = np->n_type<0 ? sb.st_mode&017777 : sb.st_mode&0777;
622: onum = np->n_un.n_val;
623: if (np->n_type < 0)
624: return ((mode&onum) == onum);
625: return (mode == onum);
626: }
627:
628: /*
629: * Compare again filetypes
630: */
631: xtype(np)
632: NODE *np;
633: {
634: register char *type;
635: register int ftype;
636:
637: type = np->n_un.n_str;
638: ftype = sb.st_mode&S_IFMT;
639: if (type[1] == '\0')
640: switch (type[0]) {
641: case 'b':
642: return (ftype == S_IFBLK);
643:
644: case 'c':
645: return (ftype == S_IFCHR);
646:
647: case 'd':
648: return (ftype == S_IFDIR);
649:
650: case 'f':
651: return (ftype == S_IFREG);
652:
653: case 'm':
654: return (ftype==S_IFMPB || ftype==S_IFMPC);
655:
656: case 'p':
657: return (ftype == S_IFPIP);
658: }
659: ferr("Bad file type `%s'", type);
660: }
661:
662: /*
663: * Compare link counts.
664: */
665: xlinks(np)
666: NODE *np;
667: {
668: return (ncomp(np, sb.st_nlink));
669: }
670:
671: /*
672: * Compare uid.
673: */
674: xuser(np)
675: NODE *np;
676: {
677: return (np->n_un.n_val == sb.st_uid);
678: }
679:
680: /*
681: * Compare group id of file
682: * with given one.
683: */
684: xgroup(np)
685: NODE *np;
686: {
687: return (np->n_un.n_val == sb.st_gid);
688: }
689:
690: /*
691: * Compare size of file in blocks
692: * with given.
693: */
694: xsize(np)
695: NODE *np;
696: {
697: register int fsize;
698:
699: fsize = (sb.st_size+BUFSIZ-1)/BUFSIZ;
700: return (ncomp(np, fsize));
701: }
702:
703: /*
704: * Compare the i-number of the file
705: * with that given.
706: */
707: xinum(np)
708: NODE *np;
709: {
710: return (ncomp(np, sb.st_ino));
711: }
712:
713: /*
714: * Return true if file has been accessed
715: * in `n' days.
716: */
717: xatime(np)
718: NODE *np;
719: {
720: return (ndays(np, sb.st_atime));
721: }
722:
723: /*
724: * Return non-zero if file has been created
725: * in `n' days.
726: */
727: xctime(np)
728: NODE *np;
729: {
730: return (ndays(np, sb.st_ctime));
731: }
732:
733: /*
734: * Return true if file has been modified
735: * in `n' days.
736: */
737: xmtime(np)
738: NODE *np;
739: {
740: return (ndays(np, sb.st_mtime));
741: }
742:
743: /*
744: * Execute a command based on the filename
745: */
746: xexec(np)
747: NODE *np;
748: {
749: static char command[200];
750: register char *ap, **app;
751: register int c;
752: int ok;
753:
754: command[0] = '\0';
755: app = np->n_un.n_strp;
756: while (*app != NULL) {
757: if ((ap = *app++) == FILEARG)
758: ap = fname;
759: strcat(command, ap);
760: if (*app != NULL)
761: strcat(command, " ");
762: }
763: if (np->n_type) {
764: printf("%s%s? ", prompt, command);
765: ok = (c = getchar()) == 'y';
766: while (c!='\n' && c!=EOF)
767: c = getchar();
768: if (!ok)
769: return (0);
770: }
771: return (!system(command));
772: }
773:
774: /*
775: * Print the filename.
776: */
777: /* ARGSUSED */
778: xprint(np)
779: NODE *np;
780: {
781: printf("%s\n", fname);
782: return (1);
783: }
784:
785: xnop(np)
786: NODE *np;
787: {
788: return (1);
789: }
790:
791: /*
792: * Return true if the file is newer than
793: * the given one.
794: */
795: xnewer(np)
796: register NODE *np;
797: {
798: return (sb.st_mtime > np->n_un.n_time);
799: }
800:
801: /*
802: * Do a numerical comparison on dates.
803: */
804: ndays(np, t)
805: register NODE *np;
806: time_t t;
807: {
808: register int days;
809:
810: days = (curtime-t+DAYSEC/2)/DAYSEC;
811: return (ncomp(np, days));
812: }
813:
814: /*
815: * Numerical compare.
816: */
817: ncomp(np, val)
818: register NODE *np;
819: register unsigned val;
820: {
821: if (np->n_type == 0)
822: return (np->n_un.n_val == val);
823: if (np->n_type > 0)
824: return (val > np->n_un.n_val);
825: return (val < np->n_un.n_val);
826: }
827:
828: /*
829: * Errors
830: */
831: /* VARARGS */
832: ferr(x)
833: {
834: fprintf(stderr, "find: %r\n", &x);
835: exit (1);
836: }
837:
838: /* VARARGS */
839: fmsg(x)
840: {
841: fprintf(stderr, "find: %r\n", &x);
842: }
843:
844: usage()
845: {
846: fprintf(stderr, "Usage: find directory ... [ expression ]\n");
847: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.