Annotation of src/usr.bin/find/function.c, Revision 1.16
1.16 ! millert 1: /* $OpenBSD: function.c,v 1.15 1999/10/04 21:17:32 millert Exp $ */
1.4 deraadt 2:
1.1 deraadt 3: /*-
4: * Copyright (c) 1990, 1993
5: * The Regents of the University of California. All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Cimarron D. Taylor of the University of California, Berkeley.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the University of
21: * California, Berkeley and its contributors.
22: * 4. Neither the name of the University nor the names of its contributors
23: * may be used to endorse or promote products derived from this software
24: * without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36: * SUCH DAMAGE.
37: */
38:
39: #ifndef lint
40: /*static char sccsid[] = "from: @(#)function.c 8.1 (Berkeley) 6/6/93";*/
1.16 ! millert 41: static char rcsid[] = "$OpenBSD: function.c,v 1.15 1999/10/04 21:17:32 millert Exp $";
1.1 deraadt 42: #endif /* not lint */
43:
44: #include <sys/param.h>
45: #include <sys/ucred.h>
46: #include <sys/stat.h>
47: #include <sys/wait.h>
48: #include <sys/mount.h>
49:
1.7 tholo 50: #include <dirent.h>
1.1 deraadt 51: #include <err.h>
52: #include <errno.h>
1.15 millert 53: #include <fcntl.h>
1.1 deraadt 54: #include <fnmatch.h>
55: #include <fts.h>
56: #include <grp.h>
1.10 millert 57: #include <libgen.h>
1.1 deraadt 58: #include <pwd.h>
59: #include <stdio.h>
60: #include <stdlib.h>
61: #include <string.h>
62: #include <tzfile.h>
63: #include <unistd.h>
64:
65: #include "find.h"
66:
67: #define COMPARE(a, b) { \
68: switch (plan->flags) { \
69: case F_EQUAL: \
70: return (a == b); \
71: case F_LESSTHAN: \
72: return (a < b); \
73: case F_GREATER: \
74: return (a > b); \
75: default: \
76: abort(); \
77: } \
78: }
79:
80: static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *))));
81:
1.9 millert 82: extern int dotfd;
83: extern time_t now;
84: extern FTS *tree;
85:
1.1 deraadt 86: /*
87: * find_parsenum --
88: * Parse a string of the form [+-]# and return the value.
89: */
90: static long
91: find_parsenum(plan, option, vp, endch)
92: PLAN *plan;
93: char *option, *vp, *endch;
94: {
95: long value;
96: char *endchar, *str; /* Pointer to character ending conversion. */
97:
98: /* Determine comparison from leading + or -. */
99: str = vp;
100: switch (*str) {
101: case '+':
102: ++str;
103: plan->flags = F_GREATER;
104: break;
105: case '-':
106: ++str;
107: plan->flags = F_LESSTHAN;
108: break;
109: default:
110: plan->flags = F_EQUAL;
111: break;
112: }
113:
114: /*
115: * Convert the string with strtol(). Note, if strtol() returns zero
116: * and endchar points to the beginning of the string we know we have
117: * a syntax error.
118: */
119: value = strtol(str, &endchar, 10);
120: if (value == 0 && endchar == str)
121: errx(1, "%s: %s: illegal numeric value", option, vp);
122: if (endchar[0] && (endch == NULL || endchar[0] != *endch))
123: errx(1, "%s: %s: illegal trailing character", option, vp);
124: if (endch)
125: *endch = endchar[0];
126: return (value);
127: }
128:
129: /*
130: * The value of n for the inode times (atime, ctime, and mtime) is a range,
131: * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with
132: * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
133: * user wanted. Correct so that -1 is "less than 1".
134: */
135: #define TIME_CORRECT(p, ttype) \
136: if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \
137: ++((p)->t_data);
138:
139: /*
1.11 deraadt 140: * -amin n functions --
141: *
142: * True if the difference between the file access time and the
143: * current time is n min periods.
144: */
145: int
146: f_amin(plan, entry)
147: PLAN *plan;
148: FTSENT *entry;
149: {
150: extern time_t now;
151:
152: COMPARE((now - entry->fts_statp->st_atime +
153: 60 - 1) / 60, plan->t_data);
154: }
155:
156: PLAN *
157: c_amin(arg)
158: char *arg;
159: {
160: PLAN *new;
161:
162: ftsoptions &= ~FTS_NOSTAT;
163:
164: new = palloc(N_AMIN, f_amin);
165: new->t_data = find_parsenum(new, "-amin", arg, NULL);
166: TIME_CORRECT(new, N_AMIN);
167: return (new);
168: }
169:
170: /*
1.1 deraadt 171: * -atime n functions --
172: *
173: * True if the difference between the file access time and the
174: * current time is n 24 hour periods.
175: */
176: int
177: f_atime(plan, entry)
178: PLAN *plan;
179: FTSENT *entry;
180: {
181:
182: COMPARE((now - entry->fts_statp->st_atime +
183: SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
184: }
185:
186: PLAN *
187: c_atime(arg)
188: char *arg;
189: {
190: PLAN *new;
191:
192: ftsoptions &= ~FTS_NOSTAT;
193:
194: new = palloc(N_ATIME, f_atime);
195: new->t_data = find_parsenum(new, "-atime", arg, NULL);
196: TIME_CORRECT(new, N_ATIME);
197: return (new);
198: }
1.11 deraadt 199:
200: /*
201: * -cmin n functions --
202: *
203: * True if the difference between the last change of file
204: * status information and the current time is n min periods.
205: */
206: int
207: f_cmin(plan, entry)
208: PLAN *plan;
209: FTSENT *entry;
210: {
211: extern time_t now;
212:
213: COMPARE((now - entry->fts_statp->st_ctime +
214: 60 - 1) / 60, plan->t_data);
215: }
216:
217: PLAN *
218: c_cmin(arg)
219: char *arg;
220: {
221: PLAN *new;
222:
223: ftsoptions &= ~FTS_NOSTAT;
224:
225: new = palloc(N_CMIN, f_cmin);
226: new->t_data = find_parsenum(new, "-cmin", arg, NULL);
227: TIME_CORRECT(new, N_CMIN);
228: return (new);
229: }
230:
1.1 deraadt 231: /*
232: * -ctime n functions --
233: *
234: * True if the difference between the last change of file
235: * status information and the current time is n 24 hour periods.
236: */
237: int
238: f_ctime(plan, entry)
239: PLAN *plan;
240: FTSENT *entry;
241: {
242:
243: COMPARE((now - entry->fts_statp->st_ctime +
244: SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
245: }
246:
247: PLAN *
248: c_ctime(arg)
249: char *arg;
250: {
251: PLAN *new;
252:
253: ftsoptions &= ~FTS_NOSTAT;
254:
255: new = palloc(N_CTIME, f_ctime);
256: new->t_data = find_parsenum(new, "-ctime", arg, NULL);
257: TIME_CORRECT(new, N_CTIME);
258: return (new);
259: }
260:
261: /*
262: * -depth functions --
263: *
264: * Always true, causes descent of the directory hierarchy to be done
265: * so that all entries in a directory are acted on before the directory
266: * itself.
267: */
268: int
269: f_always_true(plan, entry)
270: PLAN *plan;
271: FTSENT *entry;
272: {
273: return (1);
274: }
275:
276: PLAN *
277: c_depth()
278: {
279: isdepth = 1;
280:
281: return (palloc(N_DEPTH, f_always_true));
282: }
283:
1.7 tholo 284: /*
285: * -empty functions --
286: *
287: * True if the file or directory is empty
288: */
289: int
290: f_empty(plan, entry)
291: PLAN *plan;
292: FTSENT *entry;
293: {
294: if (S_ISREG(entry->fts_statp->st_mode) && entry->fts_statp->st_size == 0)
295: return (1);
296: if (S_ISDIR(entry->fts_statp->st_mode)) {
297: struct dirent *dp;
298: int empty;
299: DIR *dir;
300:
301: empty = 1;
302: dir = opendir(entry->fts_accpath);
303: if (dir == NULL)
304: err(1, "%s", entry->fts_accpath);
305: for (dp = readdir(dir); dp; dp = readdir(dir))
306: if (dp->d_name[0] != '.' ||
307: (dp->d_name[1] != '\0' &&
308: (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
309: empty = 0;
310: break;
311: }
312: closedir(dir);
313: return (empty);
314: }
315: return (0);
316: }
317:
318: PLAN *
319: c_empty()
320: {
321: ftsoptions &= ~FTS_NOSTAT;
322:
323: return (palloc(N_EMPTY, f_empty));
324: }
325:
1.1 deraadt 326: /*
327: * [-exec | -ok] utility [arg ... ] ; functions --
328: *
329: * True if the executed utility returns a zero value as exit status.
330: * The end of the primary expression is delimited by a semicolon. If
331: * "{}" occurs anywhere, it gets replaced by the current pathname.
332: * The current directory for the execution of utility is the same as
333: * the current directory when the find utility was started.
334: *
335: * The primary -ok is different in that it requests affirmation of the
336: * user before executing the utility.
337: */
338: int
339: f_exec(plan, entry)
340: register PLAN *plan;
341: FTSENT *entry;
342: {
343: register int cnt;
344: pid_t pid;
345: int status;
346:
347: for (cnt = 0; plan->e_argv[cnt]; ++cnt)
348: if (plan->e_len[cnt])
349: brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
350: entry->fts_path, plan->e_len[cnt]);
351:
352: if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
353: return (0);
354:
355: /* don't mix output of command with find output */
356: fflush(stdout);
357: fflush(stderr);
358:
359: switch (pid = vfork()) {
360: case -1:
361: err(1, "fork");
362: /* NOTREACHED */
363: case 0:
364: if (fchdir(dotfd)) {
365: warn("chdir");
366: _exit(1);
367: }
368: execvp(plan->e_argv[0], plan->e_argv);
369: warn("%s", plan->e_argv[0]);
370: _exit(1);
371: }
372: pid = waitpid(pid, &status, 0);
373: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
374: }
375:
376: /*
377: * c_exec --
378: * build three parallel arrays, one with pointers to the strings passed
379: * on the command line, one with (possibly duplicated) pointers to the
380: * argv array, and one with integer values that are lengths of the
381: * strings, but also flags meaning that the string has to be massaged.
382: */
383: PLAN *
384: c_exec(argvp, isok)
385: char ***argvp;
386: int isok;
387: {
388: PLAN *new; /* node returned */
389: register int cnt;
390: register char **argv, **ap, *p;
391:
392: isoutput = 1;
393:
394: new = palloc(N_EXEC, f_exec);
395: if (isok)
396: new->flags = F_NEEDOK;
397:
398: for (ap = argv = *argvp;; ++ap) {
399: if (!*ap)
400: errx(1,
401: "%s: no terminating \";\"", isok ? "-ok" : "-exec");
1.8 millert 402: if (**ap == ';')
403: break;
404: }
405:
406: cnt = ap - *argvp + 1;
407: new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
408: new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
409: new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
410:
411: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
412: new->e_orig[cnt] = *argv;
413: for (p = *argv; *p; ++p)
414: if (p[0] == '{' && p[1] == '}') {
415: new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
416: new->e_len[cnt] = MAXPATHLEN;
417: break;
418: }
419: if (!*p) {
420: new->e_argv[cnt] = *argv;
421: new->e_len[cnt] = 0;
422: }
423: }
424: new->e_argv[cnt] = new->e_orig[cnt] = NULL;
425:
426: *argvp = argv + 1;
427: return (new);
428: }
429:
430: /*
431: * -execdir utility [arg ... ] ; functions --
432: *
433: * True if the executed utility returns a zero value as exit status.
434: * The end of the primary expression is delimited by a semicolon. If
435: * "{}" occurs anywhere, it gets replaced by the unqualified pathname.
436: * The current directory for the execution of utility is the same as
437: * the directory where the file lives.
438: */
439: int
440: f_execdir(plan, entry)
441: register PLAN *plan;
442: FTSENT *entry;
443: {
444: register int cnt;
445: pid_t pid;
1.15 millert 446: int status, fd;
1.10 millert 447: char base[MAXPATHLEN];
1.8 millert 448:
1.15 millert 449: /* fts(3) does not chdir for the root level so we do it ourselves. */
450: if (entry->fts_level == FTS_ROOTLEVEL) {
451: if ((fd = open(".", O_RDONLY)) == -1) {
452: warn("cannot open \".\"");
453: return (0);
454: }
455: if (chdir(entry->fts_accpath)) {
1.16 ! millert 456: (void) close(fd);
1.15 millert 457: return (0);
458: }
459: }
460:
1.10 millert 461: /* Substitute basename(path) for {} since cwd is it's parent dir */
462: (void)strncpy(base, basename(entry->fts_path), sizeof(base) - 1);
463: base[sizeof(base) - 1] = '\0';
1.8 millert 464: for (cnt = 0; plan->e_argv[cnt]; ++cnt)
465: if (plan->e_len[cnt])
466: brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
1.10 millert 467: base, plan->e_len[cnt]);
1.8 millert 468:
469: /* don't mix output of command with find output */
470: fflush(stdout);
471: fflush(stderr);
472:
473: switch (pid = vfork()) {
474: case -1:
475: err(1, "fork");
476: /* NOTREACHED */
477: case 0:
478: execvp(plan->e_argv[0], plan->e_argv);
479: warn("%s", plan->e_argv[0]);
480: _exit(1);
481: }
1.15 millert 482:
483: /* Undo the above... */
484: if (entry->fts_level == FTS_ROOTLEVEL) {
485: if (fchdir(fd) == -1) {
486: warn("unable to chdir back to starting directory");
1.16 ! millert 487: (void) close(fd);
1.15 millert 488: return (0);
489: }
1.16 ! millert 490: (void) close(fd);
1.15 millert 491: }
492:
1.8 millert 493: pid = waitpid(pid, &status, 0);
494: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
495: }
496:
497: /*
498: * c_execdir --
499: * build three parallel arrays, one with pointers to the strings passed
500: * on the command line, one with (possibly duplicated) pointers to the
501: * argv array, and one with integer values that are lengths of the
502: * strings, but also flags meaning that the string has to be massaged.
503: */
504: PLAN *
505: c_execdir(argvp)
506: char ***argvp;
507: {
508: PLAN *new; /* node returned */
509: register int cnt;
510: register char **argv, **ap, *p;
511:
512: ftsoptions &= ~FTS_NOSTAT;
513: isoutput = 1;
514:
515: new = palloc(N_EXECDIR, f_execdir);
516:
517: for (ap = argv = *argvp;; ++ap) {
518: if (!*ap)
519: errx(1,
520: "-execdir: no terminating \";\"");
1.1 deraadt 521: if (**ap == ';')
522: break;
523: }
524:
525: cnt = ap - *argvp + 1;
526: new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
527: new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
528: new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
529:
530: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
531: new->e_orig[cnt] = *argv;
532: for (p = *argv; *p; ++p)
533: if (p[0] == '{' && p[1] == '}') {
534: new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
535: new->e_len[cnt] = MAXPATHLEN;
536: break;
537: }
538: if (!*p) {
539: new->e_argv[cnt] = *argv;
540: new->e_len[cnt] = 0;
541: }
542: }
543: new->e_argv[cnt] = new->e_orig[cnt] = NULL;
544:
545: *argvp = argv + 1;
546: return (new);
547: }
548:
549: /*
550: * -follow functions --
551: *
552: * Always true, causes symbolic links to be followed on a global
553: * basis.
554: */
555: PLAN *
556: c_follow()
557: {
558: ftsoptions &= ~FTS_PHYSICAL;
559: ftsoptions |= FTS_LOGICAL;
560:
561: return (palloc(N_FOLLOW, f_always_true));
562: }
563:
564: /*
565: * -fstype functions --
566: *
567: * True if the file is of a certain type.
568: */
569: int
570: f_fstype(plan, entry)
571: PLAN *plan;
572: FTSENT *entry;
573: {
574: static dev_t curdev; /* need a guaranteed illegal dev value */
575: static int first = 1;
576: struct statfs sb;
577: static short val;
578: static char fstype[MFSNAMELEN];
579: char *p, save[2];
580:
581: /* Only check when we cross mount point. */
582: if (first || curdev != entry->fts_statp->st_dev) {
583: curdev = entry->fts_statp->st_dev;
584:
585: /*
586: * Statfs follows symlinks; find wants the link's file system,
587: * not where it points.
588: */
589: if (entry->fts_info == FTS_SL ||
590: entry->fts_info == FTS_SLNONE) {
1.9 millert 591: if ((p = strrchr(entry->fts_accpath, '/')))
1.1 deraadt 592: ++p;
593: else
594: p = entry->fts_accpath;
595: save[0] = p[0];
596: p[0] = '.';
597: save[1] = p[1];
598: p[1] = '\0';
599:
600: } else
601: p = NULL;
602:
603: if (statfs(entry->fts_accpath, &sb))
604: err(1, "%s", entry->fts_accpath);
605:
606: if (p) {
607: p[0] = save[0];
608: p[1] = save[1];
609: }
610:
611: first = 0;
612:
613: /*
614: * Further tests may need both of these values, so
615: * always copy both of them.
616: */
617: val = sb.f_flags;
618: strncpy(fstype, sb.f_fstypename, MFSNAMELEN);
619: }
620: switch (plan->flags) {
621: case F_MTFLAG:
622: return (val & plan->mt_data);
623: case F_MTTYPE:
624: return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
625: default:
626: abort();
627: }
628: }
629:
630: PLAN *
631: c_fstype(arg)
632: char *arg;
633: {
634: register PLAN *new;
635:
636: ftsoptions &= ~FTS_NOSTAT;
637:
638: new = palloc(N_FSTYPE, f_fstype);
639: switch (*arg) {
640: case 'l':
641: if (!strcmp(arg, "local")) {
642: new->flags = F_MTFLAG;
643: new->mt_data = MNT_LOCAL;
644: return (new);
645: }
646: break;
647: case 'r':
648: if (!strcmp(arg, "rdonly")) {
649: new->flags = F_MTFLAG;
650: new->mt_data = MNT_RDONLY;
651: return (new);
652: }
653: break;
654: }
655:
656: new->flags = F_MTTYPE;
657: new->c_data = arg;
658: return (new);
659: }
660:
661: /*
662: * -group gname functions --
663: *
664: * True if the file belongs to the group gname. If gname is numeric and
665: * an equivalent of the getgrnam() function does not return a valid group
666: * name, gname is taken as a group ID.
667: */
668: int
669: f_group(plan, entry)
670: PLAN *plan;
671: FTSENT *entry;
672: {
673: return (entry->fts_statp->st_gid == plan->g_data);
674: }
675:
676: PLAN *
677: c_group(gname)
678: char *gname;
679: {
680: PLAN *new;
681: struct group *g;
682: gid_t gid;
683:
684: ftsoptions &= ~FTS_NOSTAT;
685:
686: g = getgrnam(gname);
687: if (g == NULL) {
688: gid = atoi(gname);
689: if (gid == 0 && gname[0] != '0')
690: errx(1, "-group: %s: no such group", gname);
691: } else
692: gid = g->gr_gid;
693:
694: new = palloc(N_GROUP, f_group);
695: new->g_data = gid;
696: return (new);
697: }
698:
699: /*
700: * -inum n functions --
701: *
702: * True if the file has inode # n.
703: */
704: int
705: f_inum(plan, entry)
706: PLAN *plan;
707: FTSENT *entry;
708: {
709: COMPARE(entry->fts_statp->st_ino, plan->i_data);
710: }
711:
712: PLAN *
713: c_inum(arg)
714: char *arg;
715: {
716: PLAN *new;
717:
718: ftsoptions &= ~FTS_NOSTAT;
719:
720: new = palloc(N_INUM, f_inum);
721: new->i_data = find_parsenum(new, "-inum", arg, NULL);
722: return (new);
723: }
724:
725: /*
726: * -links n functions --
727: *
728: * True if the file has n links.
729: */
730: int
731: f_links(plan, entry)
732: PLAN *plan;
733: FTSENT *entry;
734: {
735: COMPARE(entry->fts_statp->st_nlink, plan->l_data);
736: }
737:
738: PLAN *
739: c_links(arg)
740: char *arg;
741: {
742: PLAN *new;
743:
744: ftsoptions &= ~FTS_NOSTAT;
745:
746: new = palloc(N_LINKS, f_links);
747: new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
748: return (new);
749: }
750:
751: /*
752: * -ls functions --
753: *
754: * Always true - prints the current entry to stdout in "ls" format.
755: */
756: int
757: f_ls(plan, entry)
758: PLAN *plan;
759: FTSENT *entry;
760: {
761: printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
762: return (1);
763: }
764:
765: PLAN *
766: c_ls()
767: {
768: ftsoptions &= ~FTS_NOSTAT;
769: isoutput = 1;
770:
771: return (palloc(N_LS, f_ls));
1.5 tholo 772: }
773:
774: /*
775: * - maxdepth n functions --
776: *
777: * True if the current search depth is less than or equal to the
778: * maximum depth specified
779: */
780: int
1.6 tholo 781: f_maxdepth(plan, entry)
1.5 tholo 782: PLAN *plan;
783: FTSENT *entry;
784: {
785:
1.6 tholo 786: if (entry->fts_level >= plan->max_data)
1.5 tholo 787: fts_set(tree, entry, FTS_SKIP);
1.6 tholo 788: return (entry->fts_level <= plan->max_data);
1.5 tholo 789: }
790:
791: PLAN *
1.6 tholo 792: c_maxdepth(arg)
1.5 tholo 793: char *arg;
794: {
795: PLAN *new;
796:
1.6 tholo 797: new = palloc(N_MAXDEPTH, f_maxdepth);
798: new->max_data = atoi(arg);
799: return (new);
800: }
801:
802: /*
803: * - mindepth n functions --
804: *
805: * True if the current search depth is greater than or equal to the
806: * minimum depth specified
807: */
808: int
809: f_mindepth(plan, entry)
810: PLAN *plan;
811: FTSENT *entry;
812: {
813:
814: return (entry->fts_level >= plan->min_data);
815: }
816:
817: PLAN *
818: c_mindepth(arg)
819: char *arg;
820: {
821: PLAN *new;
822:
823: new = palloc(N_MINDEPTH, f_mindepth);
824: new->min_data = atoi(arg);
1.5 tholo 825: return (new);
1.1 deraadt 826: }
827:
828: /*
829: * -mtime n functions --
830: *
831: * True if the difference between the file modification time and the
832: * current time is n 24 hour periods.
833: */
834: int
835: f_mtime(plan, entry)
836: PLAN *plan;
837: FTSENT *entry;
838: {
839:
840: COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
841: SECSPERDAY, plan->t_data);
842: }
843:
844: PLAN *
845: c_mtime(arg)
846: char *arg;
847: {
848: PLAN *new;
849:
850: ftsoptions &= ~FTS_NOSTAT;
851:
852: new = palloc(N_MTIME, f_mtime);
853: new->t_data = find_parsenum(new, "-mtime", arg, NULL);
854: TIME_CORRECT(new, N_MTIME);
1.11 deraadt 855: return (new);
856: }
857:
858: /*
859: * -mmin n functions --
860: *
861: * True if the difference between the file modification time and the
862: * current time is n min periods.
863: */
864: int
865: f_mmin(plan, entry)
866: PLAN *plan;
867: FTSENT *entry;
868: {
869: extern time_t now;
870:
871: COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /
872: 60, plan->t_data);
873: }
874:
875: PLAN *
876: c_mmin(arg)
877: char *arg;
878: {
879: PLAN *new;
880:
881: ftsoptions &= ~FTS_NOSTAT;
882:
883: new = palloc(N_MMIN, f_mmin);
884: new->t_data = find_parsenum(new, "-mmin", arg, NULL);
885: TIME_CORRECT(new, N_MMIN);
1.1 deraadt 886: return (new);
887: }
888:
889: /*
890: * -name functions --
891: *
892: * True if the basename of the filename being examined
893: * matches pattern using Pattern Matching Notation S3.14
894: */
895: int
896: f_name(plan, entry)
897: PLAN *plan;
898: FTSENT *entry;
899: {
900: return (!fnmatch(plan->c_data, entry->fts_name, 0));
901: }
902:
903: PLAN *
904: c_name(pattern)
905: char *pattern;
906: {
907: PLAN *new;
908:
909: new = palloc(N_NAME, f_name);
910: new->c_data = pattern;
911: return (new);
912: }
913:
914: /*
915: * -newer file functions --
916: *
917: * True if the current file has been modified more recently
918: * then the modification time of the file named by the pathname
919: * file.
920: */
921: int
922: f_newer(plan, entry)
923: PLAN *plan;
924: FTSENT *entry;
925: {
926: return (entry->fts_statp->st_mtime > plan->t_data);
927: }
928:
929: PLAN *
930: c_newer(filename)
931: char *filename;
932: {
933: PLAN *new;
934: struct stat sb;
935:
936: ftsoptions &= ~FTS_NOSTAT;
937:
938: if (stat(filename, &sb))
939: err(1, "%s", filename);
940: new = palloc(N_NEWER, f_newer);
941: new->t_data = sb.st_mtime;
942: return (new);
943: }
944:
945: /*
946: * -nogroup functions --
947: *
948: * True if file belongs to a user ID for which the equivalent
949: * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
950: */
951: int
952: f_nogroup(plan, entry)
953: PLAN *plan;
954: FTSENT *entry;
955: {
956: char *group_from_gid();
957:
958: return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
959: }
960:
961: PLAN *
962: c_nogroup()
963: {
964: ftsoptions &= ~FTS_NOSTAT;
965:
966: return (palloc(N_NOGROUP, f_nogroup));
967: }
968:
969: /*
970: * -nouser functions --
971: *
972: * True if file belongs to a user ID for which the equivalent
973: * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
974: */
975: int
976: f_nouser(plan, entry)
977: PLAN *plan;
978: FTSENT *entry;
979: {
980: char *user_from_uid();
981:
982: return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
983: }
984:
985: PLAN *
986: c_nouser()
987: {
988: ftsoptions &= ~FTS_NOSTAT;
989:
990: return (palloc(N_NOUSER, f_nouser));
991: }
992:
993: /*
994: * -path functions --
995: *
996: * True if the path of the filename being examined
997: * matches pattern using Pattern Matching Notation S3.14
998: */
999: int
1000: f_path(plan, entry)
1001: PLAN *plan;
1002: FTSENT *entry;
1003: {
1004: return (!fnmatch(plan->c_data, entry->fts_path, 0));
1005: }
1006:
1007: PLAN *
1008: c_path(pattern)
1009: char *pattern;
1010: {
1011: PLAN *new;
1012:
1013: new = palloc(N_NAME, f_path);
1014: new->c_data = pattern;
1015: return (new);
1016: }
1017:
1018: /*
1019: * -perm functions --
1020: *
1021: * The mode argument is used to represent file mode bits. If it starts
1022: * with a leading digit, it's treated as an octal mode, otherwise as a
1023: * symbolic mode.
1024: */
1025: int
1026: f_perm(plan, entry)
1027: PLAN *plan;
1028: FTSENT *entry;
1029: {
1030: mode_t mode;
1031:
1032: mode = entry->fts_statp->st_mode &
1033: (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1034: if (plan->flags == F_ATLEAST)
1035: return ((plan->m_data | mode) == mode);
1036: else
1037: return (mode == plan->m_data);
1038: /* NOTREACHED */
1039: }
1040:
1041: PLAN *
1042: c_perm(perm)
1043: char *perm;
1044: {
1045: PLAN *new;
1046: mode_t *set;
1047:
1048: ftsoptions &= ~FTS_NOSTAT;
1049:
1050: new = palloc(N_PERM, f_perm);
1051:
1052: if (*perm == '-') {
1053: new->flags = F_ATLEAST;
1054: ++perm;
1055: }
1056:
1057: if ((set = setmode(perm)) == NULL)
1058: err(1, "-perm: %s: illegal mode string", perm);
1059:
1060: new->m_data = getmode(set, 0);
1.12 deraadt 1061: free(set);
1.1 deraadt 1062: return (new);
1063: }
1064:
1065: /*
1066: * -print functions --
1067: *
1068: * Always true, causes the current pathame to be written to
1069: * standard output.
1070: */
1071: int
1072: f_print(plan, entry)
1073: PLAN *plan;
1074: FTSENT *entry;
1075: {
1076: (void)printf("%s\n", entry->fts_path);
1077: return(1);
1078: }
1079:
1080: /* ARGSUSED */
1.9 millert 1081: int
1.1 deraadt 1082: f_print0(plan, entry)
1083: PLAN *plan;
1084: FTSENT *entry;
1085: {
1086: (void)fputs(entry->fts_path, stdout);
1087: (void)fputc('\0', stdout);
1088: return(1);
1089: }
1090:
1091: PLAN *
1092: c_print()
1093: {
1094: isoutput = 1;
1095:
1096: return(palloc(N_PRINT, f_print));
1097: }
1098:
1099: PLAN *
1100: c_print0()
1101: {
1102: isoutput = 1;
1103:
1104: return(palloc(N_PRINT0, f_print0));
1105: }
1106:
1107: /*
1108: * -prune functions --
1109: *
1110: * Prune a portion of the hierarchy.
1111: */
1112: int
1113: f_prune(plan, entry)
1114: PLAN *plan;
1115: FTSENT *entry;
1116: {
1117:
1118: if (fts_set(tree, entry, FTS_SKIP))
1119: err(1, "%s", entry->fts_path);
1120: return (1);
1121: }
1122:
1123: PLAN *
1124: c_prune()
1125: {
1126: return (palloc(N_PRUNE, f_prune));
1127: }
1128:
1129: /*
1130: * -size n[c] functions --
1131: *
1132: * True if the file size in bytes, divided by an implementation defined
1133: * value and rounded up to the next integer, is n. If n is followed by
1134: * a c, the size is in bytes.
1135: */
1136: #define FIND_SIZE 512
1137: static int divsize = 1;
1138:
1139: int
1140: f_size(plan, entry)
1141: PLAN *plan;
1142: FTSENT *entry;
1143: {
1144: off_t size;
1145:
1146: size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1147: FIND_SIZE : entry->fts_statp->st_size;
1148: COMPARE(size, plan->o_data);
1149: }
1150:
1151: PLAN *
1152: c_size(arg)
1153: char *arg;
1154: {
1155: PLAN *new;
1156: char endch;
1157:
1158: ftsoptions &= ~FTS_NOSTAT;
1159:
1160: new = palloc(N_SIZE, f_size);
1161: endch = 'c';
1162: new->o_data = find_parsenum(new, "-size", arg, &endch);
1163: if (endch == 'c')
1164: divsize = 0;
1165: return (new);
1166: }
1167:
1168: /*
1169: * -type c functions --
1170: *
1171: * True if the type of the file is c, where c is b, c, d, p, or f for
1172: * block special file, character special file, directory, FIFO, or
1173: * regular file, respectively.
1174: */
1175: int
1176: f_type(plan, entry)
1177: PLAN *plan;
1178: FTSENT *entry;
1179: {
1180: return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1181: }
1182:
1183: PLAN *
1184: c_type(typestring)
1185: char *typestring;
1186: {
1187: PLAN *new;
1188: mode_t mask;
1189:
1190: ftsoptions &= ~FTS_NOSTAT;
1191:
1192: switch (typestring[0]) {
1.2 deraadt 1193: #ifdef S_IFWHT
1.3 deraadt 1194: case 'W':
1195: mask = S_IFWHT;
1.13 espie 1196: if ((ftsoptions & FTS_WHITEOUT) == 0)
1197: warnx("-type W without -W is a no-op");
1.3 deraadt 1198: break;
1.2 deraadt 1199: #endif
1.1 deraadt 1200: case 'b':
1201: mask = S_IFBLK;
1202: break;
1203: case 'c':
1204: mask = S_IFCHR;
1205: break;
1206: case 'd':
1207: mask = S_IFDIR;
1208: break;
1209: case 'f':
1210: mask = S_IFREG;
1211: break;
1212: case 'l':
1213: mask = S_IFLNK;
1214: break;
1215: case 'p':
1216: mask = S_IFIFO;
1217: break;
1218: case 's':
1219: mask = S_IFSOCK;
1220: break;
1221: default:
1222: errx(1, "-type: %s: unknown type", typestring);
1223: }
1224:
1225: new = palloc(N_TYPE, f_type);
1226: new->m_data = mask;
1227: return (new);
1228: }
1229:
1230: /*
1231: * -user uname functions --
1232: *
1233: * True if the file belongs to the user uname. If uname is numeric and
1234: * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1235: * return a valid user name, uname is taken as a user ID.
1236: */
1237: int
1238: f_user(plan, entry)
1239: PLAN *plan;
1240: FTSENT *entry;
1241: {
1242: return (entry->fts_statp->st_uid == plan->u_data);
1243: }
1244:
1245: PLAN *
1246: c_user(username)
1247: char *username;
1248: {
1249: PLAN *new;
1250: struct passwd *p;
1251: uid_t uid;
1252:
1253: ftsoptions &= ~FTS_NOSTAT;
1254:
1255: p = getpwnam(username);
1256: if (p == NULL) {
1257: uid = atoi(username);
1258: if (uid == 0 && username[0] != '0')
1259: errx(1, "-user: %s: no such user", username);
1260: } else
1261: uid = p->pw_uid;
1262:
1263: new = palloc(N_USER, f_user);
1264: new->u_data = uid;
1265: return (new);
1266: }
1267:
1268: /*
1269: * -xdev functions --
1270: *
1271: * Always true, causes find not to decend past directories that have a
1272: * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1273: */
1274: PLAN *
1275: c_xdev()
1276: {
1277: ftsoptions |= FTS_XDEV;
1278:
1279: return (palloc(N_XDEV, f_always_true));
1280: }
1281:
1282: /*
1283: * ( expression ) functions --
1284: *
1285: * True if expression is true.
1286: */
1287: int
1288: f_expr(plan, entry)
1289: PLAN *plan;
1290: FTSENT *entry;
1291: {
1292: register PLAN *p;
1293: register int state;
1294:
1295: for (p = plan->p_data[0];
1296: p && (state = (p->eval)(p, entry)); p = p->next);
1297: return (state);
1298: }
1299:
1300: /*
1301: * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1302: * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1303: * to a N_EXPR node containing the expression and the ')' node is discarded.
1304: */
1305: PLAN *
1306: c_openparen()
1307: {
1308: return (palloc(N_OPENPAREN, (int (*)())-1));
1309: }
1310:
1311: PLAN *
1312: c_closeparen()
1313: {
1314: return (palloc(N_CLOSEPAREN, (int (*)())-1));
1315: }
1316:
1317: /*
1318: * ! expression functions --
1319: *
1320: * Negation of a primary; the unary NOT operator.
1321: */
1322: int
1323: f_not(plan, entry)
1324: PLAN *plan;
1325: FTSENT *entry;
1326: {
1327: register PLAN *p;
1328: register int state;
1329:
1330: for (p = plan->p_data[0];
1331: p && (state = (p->eval)(p, entry)); p = p->next);
1332: return (!state);
1333: }
1334:
1335: PLAN *
1336: c_not()
1337: {
1338: return (palloc(N_NOT, f_not));
1339: }
1340:
1341: /*
1342: * expression -o expression functions --
1343: *
1344: * Alternation of primaries; the OR operator. The second expression is
1345: * not evaluated if the first expression is true.
1346: */
1347: int
1348: f_or(plan, entry)
1349: PLAN *plan;
1350: FTSENT *entry;
1351: {
1352: register PLAN *p;
1353: register int state;
1354:
1355: for (p = plan->p_data[0];
1356: p && (state = (p->eval)(p, entry)); p = p->next);
1357:
1358: if (state)
1359: return (1);
1360:
1361: for (p = plan->p_data[1];
1362: p && (state = (p->eval)(p, entry)); p = p->next);
1363: return (state);
1364: }
1365:
1366: PLAN *
1367: c_or()
1368: {
1369: return (palloc(N_OR, f_or));
1370: }
1371:
1372: static PLAN *
1373: palloc(t, f)
1374: enum ntype t;
1375: int (*f) __P((PLAN *, FTSENT *));
1376: {
1377: PLAN *new;
1378:
1.9 millert 1379: if ((new = malloc(sizeof(PLAN)))) {
1.1 deraadt 1380: new->type = t;
1381: new->eval = f;
1382: new->flags = 0;
1383: new->next = NULL;
1384: return (new);
1385: }
1386: err(1, NULL);
1387: /* NOTREACHED */
1388: }