Annotation of src/usr.bin/find/function.c, Revision 1.20
1.20 ! mickey 1: /* $OpenBSD: function.c,v 1.19 2000/07/08 16:09:34 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.20 ! mickey 41: static char rcsid[] = "$OpenBSD: function.c,v 1.19 2000/07/08 16:09:34 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) \
1.17 millert 137: ++((p)->sec_data);
1.1 deraadt 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 +
1.17 millert 153: 60 - 1) / 60, plan->sec_data);
1.11 deraadt 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);
1.17 millert 165: new->sec_data = find_parsenum(new, "-amin", arg, NULL);
1.11 deraadt 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 +
1.17 millert 183: SECSPERDAY - 1) / SECSPERDAY, plan->sec_data);
1.1 deraadt 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);
1.17 millert 195: new->sec_data = find_parsenum(new, "-atime", arg, NULL);
1.1 deraadt 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 +
1.17 millert 214: 60 - 1) / 60, plan->sec_data);
1.11 deraadt 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);
1.17 millert 226: new->sec_data = find_parsenum(new, "-cmin", arg, NULL);
1.11 deraadt 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 +
1.17 millert 244: SECSPERDAY - 1) / SECSPERDAY, plan->sec_data);
1.1 deraadt 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);
1.17 millert 256: new->sec_data = find_parsenum(new, "-ctime", arg, NULL);
1.1 deraadt 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: }
1.19 millert 548:
549: /*
550: * -flags functions --
551: *
552: * The flags argument is used to represent file flags bits.
553: */
554: int
555: f_flags(plan, entry)
556: PLAN *plan;
557: FTSENT *entry;
558: {
559: u_int flags;
560:
561: flags = entry->fts_statp->st_flags &
562: (UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE |
563: SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND);
564: if (plan->flags == F_ATLEAST)
565: /* note that plan->fl_flags always is a subset of
566: plan->fl_mask */
567: return ((flags & plan->fl_mask) == plan->fl_flags);
568: else
569: return (flags == plan->fl_flags);
570: /* NOTREACHED */
571: }
572:
573: PLAN *
574: c_flags(flags_str)
575: char *flags_str;
576: {
577: PLAN *new;
1.20 ! mickey 578: u_int32_t flags, notflags;
1.19 millert 579:
580: ftsoptions &= ~FTS_NOSTAT;
581:
582: new = palloc(N_FLAGS, f_flags);
583:
584: if (*flags_str == '-') {
585: new->flags = F_ATLEAST;
586: ++flags_str;
587: }
588:
1.20 ! mickey 589: if (strtofflags(&flags_str, &flags, ¬flags) == 1)
1.19 millert 590: errx(1, "-flags: %s: illegal flags string", flags_str);
591:
592: new->fl_flags = flags;
593: new->fl_mask = flags | notflags;
594: return (new);
595: }
1.1 deraadt 596:
597: /*
598: * -follow functions --
599: *
600: * Always true, causes symbolic links to be followed on a global
601: * basis.
602: */
603: PLAN *
604: c_follow()
605: {
606: ftsoptions &= ~FTS_PHYSICAL;
607: ftsoptions |= FTS_LOGICAL;
608:
609: return (palloc(N_FOLLOW, f_always_true));
610: }
611:
612: /*
613: * -fstype functions --
614: *
615: * True if the file is of a certain type.
616: */
617: int
618: f_fstype(plan, entry)
619: PLAN *plan;
620: FTSENT *entry;
621: {
622: static dev_t curdev; /* need a guaranteed illegal dev value */
623: static int first = 1;
624: struct statfs sb;
625: static short val;
626: static char fstype[MFSNAMELEN];
627: char *p, save[2];
628:
629: /* Only check when we cross mount point. */
630: if (first || curdev != entry->fts_statp->st_dev) {
631: curdev = entry->fts_statp->st_dev;
632:
633: /*
634: * Statfs follows symlinks; find wants the link's file system,
635: * not where it points.
636: */
637: if (entry->fts_info == FTS_SL ||
638: entry->fts_info == FTS_SLNONE) {
1.9 millert 639: if ((p = strrchr(entry->fts_accpath, '/')))
1.1 deraadt 640: ++p;
641: else
642: p = entry->fts_accpath;
643: save[0] = p[0];
644: p[0] = '.';
645: save[1] = p[1];
646: p[1] = '\0';
647:
648: } else
649: p = NULL;
650:
651: if (statfs(entry->fts_accpath, &sb))
652: err(1, "%s", entry->fts_accpath);
653:
654: if (p) {
655: p[0] = save[0];
656: p[1] = save[1];
657: }
658:
659: first = 0;
660:
661: /*
662: * Further tests may need both of these values, so
663: * always copy both of them.
664: */
665: val = sb.f_flags;
666: strncpy(fstype, sb.f_fstypename, MFSNAMELEN);
667: }
668: switch (plan->flags) {
669: case F_MTFLAG:
670: return (val & plan->mt_data);
671: case F_MTTYPE:
672: return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
673: default:
674: abort();
675: }
676: }
677:
678: PLAN *
679: c_fstype(arg)
680: char *arg;
681: {
682: register PLAN *new;
683:
684: ftsoptions &= ~FTS_NOSTAT;
685:
686: new = palloc(N_FSTYPE, f_fstype);
687: switch (*arg) {
688: case 'l':
689: if (!strcmp(arg, "local")) {
690: new->flags = F_MTFLAG;
691: new->mt_data = MNT_LOCAL;
692: return (new);
693: }
694: break;
695: case 'r':
696: if (!strcmp(arg, "rdonly")) {
697: new->flags = F_MTFLAG;
698: new->mt_data = MNT_RDONLY;
699: return (new);
700: }
701: break;
702: }
703:
704: new->flags = F_MTTYPE;
705: new->c_data = arg;
706: return (new);
707: }
708:
709: /*
710: * -group gname functions --
711: *
712: * True if the file belongs to the group gname. If gname is numeric and
713: * an equivalent of the getgrnam() function does not return a valid group
714: * name, gname is taken as a group ID.
715: */
716: int
717: f_group(plan, entry)
718: PLAN *plan;
719: FTSENT *entry;
720: {
721: return (entry->fts_statp->st_gid == plan->g_data);
722: }
723:
724: PLAN *
725: c_group(gname)
726: char *gname;
727: {
728: PLAN *new;
729: struct group *g;
730: gid_t gid;
731:
732: ftsoptions &= ~FTS_NOSTAT;
733:
734: g = getgrnam(gname);
735: if (g == NULL) {
736: gid = atoi(gname);
737: if (gid == 0 && gname[0] != '0')
738: errx(1, "-group: %s: no such group", gname);
739: } else
740: gid = g->gr_gid;
741:
742: new = palloc(N_GROUP, f_group);
743: new->g_data = gid;
744: return (new);
745: }
746:
747: /*
748: * -inum n functions --
749: *
750: * True if the file has inode # n.
751: */
752: int
753: f_inum(plan, entry)
754: PLAN *plan;
755: FTSENT *entry;
756: {
757: COMPARE(entry->fts_statp->st_ino, plan->i_data);
758: }
759:
760: PLAN *
761: c_inum(arg)
762: char *arg;
763: {
764: PLAN *new;
765:
766: ftsoptions &= ~FTS_NOSTAT;
767:
768: new = palloc(N_INUM, f_inum);
769: new->i_data = find_parsenum(new, "-inum", arg, NULL);
770: return (new);
771: }
772:
773: /*
774: * -links n functions --
775: *
776: * True if the file has n links.
777: */
778: int
779: f_links(plan, entry)
780: PLAN *plan;
781: FTSENT *entry;
782: {
783: COMPARE(entry->fts_statp->st_nlink, plan->l_data);
784: }
785:
786: PLAN *
787: c_links(arg)
788: char *arg;
789: {
790: PLAN *new;
791:
792: ftsoptions &= ~FTS_NOSTAT;
793:
794: new = palloc(N_LINKS, f_links);
795: new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
796: return (new);
797: }
798:
799: /*
800: * -ls functions --
801: *
802: * Always true - prints the current entry to stdout in "ls" format.
803: */
804: int
805: f_ls(plan, entry)
806: PLAN *plan;
807: FTSENT *entry;
808: {
809: printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
810: return (1);
811: }
812:
813: PLAN *
814: c_ls()
815: {
816: ftsoptions &= ~FTS_NOSTAT;
817: isoutput = 1;
818:
819: return (palloc(N_LS, f_ls));
1.5 tholo 820: }
821:
822: /*
823: * - maxdepth n functions --
824: *
825: * True if the current search depth is less than or equal to the
826: * maximum depth specified
827: */
828: int
1.6 tholo 829: f_maxdepth(plan, entry)
1.5 tholo 830: PLAN *plan;
831: FTSENT *entry;
832: {
833:
1.6 tholo 834: if (entry->fts_level >= plan->max_data)
1.5 tholo 835: fts_set(tree, entry, FTS_SKIP);
1.6 tholo 836: return (entry->fts_level <= plan->max_data);
1.5 tholo 837: }
838:
839: PLAN *
1.6 tholo 840: c_maxdepth(arg)
1.5 tholo 841: char *arg;
842: {
843: PLAN *new;
844:
1.6 tholo 845: new = palloc(N_MAXDEPTH, f_maxdepth);
846: new->max_data = atoi(arg);
847: return (new);
848: }
849:
850: /*
851: * - mindepth n functions --
852: *
853: * True if the current search depth is greater than or equal to the
854: * minimum depth specified
855: */
856: int
857: f_mindepth(plan, entry)
858: PLAN *plan;
859: FTSENT *entry;
860: {
861:
862: return (entry->fts_level >= plan->min_data);
863: }
864:
865: PLAN *
866: c_mindepth(arg)
867: char *arg;
868: {
869: PLAN *new;
870:
871: new = palloc(N_MINDEPTH, f_mindepth);
872: new->min_data = atoi(arg);
1.5 tholo 873: return (new);
1.1 deraadt 874: }
875:
876: /*
877: * -mtime n functions --
878: *
879: * True if the difference between the file modification time and the
880: * current time is n 24 hour periods.
881: */
882: int
883: f_mtime(plan, entry)
884: PLAN *plan;
885: FTSENT *entry;
886: {
887:
888: COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
1.17 millert 889: SECSPERDAY, plan->sec_data);
1.1 deraadt 890: }
891:
892: PLAN *
893: c_mtime(arg)
894: char *arg;
895: {
896: PLAN *new;
897:
898: ftsoptions &= ~FTS_NOSTAT;
899:
900: new = palloc(N_MTIME, f_mtime);
1.17 millert 901: new->sec_data = find_parsenum(new, "-mtime", arg, NULL);
1.1 deraadt 902: TIME_CORRECT(new, N_MTIME);
1.11 deraadt 903: return (new);
904: }
905:
906: /*
907: * -mmin n functions --
908: *
909: * True if the difference between the file modification time and the
910: * current time is n min periods.
911: */
912: int
913: f_mmin(plan, entry)
914: PLAN *plan;
915: FTSENT *entry;
916: {
917: extern time_t now;
918:
919: COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /
1.17 millert 920: 60, plan->sec_data);
1.11 deraadt 921: }
922:
923: PLAN *
924: c_mmin(arg)
925: char *arg;
926: {
927: PLAN *new;
928:
929: ftsoptions &= ~FTS_NOSTAT;
930:
931: new = palloc(N_MMIN, f_mmin);
1.17 millert 932: new->sec_data = find_parsenum(new, "-mmin", arg, NULL);
1.11 deraadt 933: TIME_CORRECT(new, N_MMIN);
1.1 deraadt 934: return (new);
935: }
936:
937: /*
938: * -name functions --
939: *
940: * True if the basename of the filename being examined
941: * matches pattern using Pattern Matching Notation S3.14
942: */
943: int
944: f_name(plan, entry)
945: PLAN *plan;
946: FTSENT *entry;
947: {
948: return (!fnmatch(plan->c_data, entry->fts_name, 0));
949: }
950:
951: PLAN *
952: c_name(pattern)
953: char *pattern;
954: {
955: PLAN *new;
956:
957: new = palloc(N_NAME, f_name);
1.18 deraadt 958: new->c_data = pattern;
959: return (new);
960: }
961:
962: /*
963: * -iname functions --
964: *
965: * Similar to -name, but does case insensitive matching
966: *
967: */
968: int
969: f_iname(plan, entry)
970: PLAN *plan;
971: FTSENT *entry;
972: {
973: return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD));
974: }
975:
976: PLAN *
977: c_iname(pattern)
978: char *pattern;
979: {
980: PLAN *new;
981:
982: new = palloc(N_INAME, f_iname);
1.1 deraadt 983: new->c_data = pattern;
984: return (new);
985: }
986:
987: /*
988: * -newer file functions --
989: *
990: * True if the current file has been modified more recently
991: * then the modification time of the file named by the pathname
992: * file.
993: */
994: int
995: f_newer(plan, entry)
996: PLAN *plan;
997: FTSENT *entry;
998: {
1.17 millert 999:
1000: return (entry->fts_statp->st_mtimespec.tv_sec > plan->t_data.tv_sec ||
1001: (entry->fts_statp->st_mtimespec.tv_sec == plan->t_data.tv_sec &&
1002: entry->fts_statp->st_mtimespec.tv_nsec > plan->t_data.tv_nsec));
1.1 deraadt 1003: }
1004:
1005: PLAN *
1006: c_newer(filename)
1007: char *filename;
1008: {
1009: PLAN *new;
1010: struct stat sb;
1011:
1012: ftsoptions &= ~FTS_NOSTAT;
1013:
1014: if (stat(filename, &sb))
1015: err(1, "%s", filename);
1016: new = palloc(N_NEWER, f_newer);
1.17 millert 1017: memcpy(&new->t_data, &sb.st_mtimespec, sizeof(struct timespec));
1018: return (new);
1019: }
1020:
1021: /*
1022: * -anewer file functions --
1023: *
1024: * True if the current file has been accessed more recently
1025: * then the access time of the file named by the pathname
1026: * file.
1027: */
1028: int
1029: f_anewer(plan, entry)
1030: PLAN *plan;
1031: FTSENT *entry;
1032: {
1033:
1034: return (entry->fts_statp->st_atimespec.tv_sec > plan->t_data.tv_sec ||
1035: (entry->fts_statp->st_atimespec.tv_sec == plan->t_data.tv_sec &&
1036: entry->fts_statp->st_atimespec.tv_nsec > plan->t_data.tv_nsec));
1037: }
1038:
1039: PLAN *
1040: c_anewer(filename)
1041: char *filename;
1042: {
1043: PLAN *new;
1044: struct stat sb;
1045:
1046: ftsoptions &= ~FTS_NOSTAT;
1047:
1048: if (stat(filename, &sb))
1049: err(1, "%s", filename);
1050: new = palloc(N_NEWER, f_newer);
1051: memcpy(&new->t_data, &sb.st_atimespec, sizeof(struct timespec));
1052: return (new);
1053: }
1054:
1055: /*
1056: * -cnewer file functions --
1057: *
1058: * True if the current file has been changed more recently
1059: * then the inode change time of the file named by the pathname
1060: * file.
1061: */
1062: int
1063: f_cnewer(plan, entry)
1064: PLAN *plan;
1065: FTSENT *entry;
1066: {
1067:
1068: return (entry->fts_statp->st_ctimespec.tv_sec > plan->t_data.tv_sec ||
1069: (entry->fts_statp->st_ctimespec.tv_sec == plan->t_data.tv_sec &&
1070: entry->fts_statp->st_ctimespec.tv_nsec > plan->t_data.tv_nsec));
1071: }
1072:
1073: PLAN *
1074: c_cnewer(filename)
1075: char *filename;
1076: {
1077: PLAN *new;
1078: struct stat sb;
1079:
1080: ftsoptions &= ~FTS_NOSTAT;
1081:
1082: if (stat(filename, &sb))
1083: err(1, "%s", filename);
1084: new = palloc(N_NEWER, f_newer);
1085: memcpy(&new->t_data, &sb.st_ctimespec, sizeof(struct timespec));
1.1 deraadt 1086: return (new);
1087: }
1088:
1089: /*
1090: * -nogroup functions --
1091: *
1092: * True if file belongs to a user ID for which the equivalent
1093: * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
1094: */
1095: int
1096: f_nogroup(plan, entry)
1097: PLAN *plan;
1098: FTSENT *entry;
1099: {
1100: char *group_from_gid();
1101:
1102: return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
1103: }
1104:
1105: PLAN *
1106: c_nogroup()
1107: {
1108: ftsoptions &= ~FTS_NOSTAT;
1109:
1110: return (palloc(N_NOGROUP, f_nogroup));
1111: }
1112:
1113: /*
1114: * -nouser functions --
1115: *
1116: * True if file belongs to a user ID for which the equivalent
1117: * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
1118: */
1119: int
1120: f_nouser(plan, entry)
1121: PLAN *plan;
1122: FTSENT *entry;
1123: {
1124: char *user_from_uid();
1125:
1126: return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
1127: }
1128:
1129: PLAN *
1130: c_nouser()
1131: {
1132: ftsoptions &= ~FTS_NOSTAT;
1133:
1134: return (palloc(N_NOUSER, f_nouser));
1135: }
1136:
1137: /*
1138: * -path functions --
1139: *
1140: * True if the path of the filename being examined
1141: * matches pattern using Pattern Matching Notation S3.14
1142: */
1143: int
1144: f_path(plan, entry)
1145: PLAN *plan;
1146: FTSENT *entry;
1147: {
1148: return (!fnmatch(plan->c_data, entry->fts_path, 0));
1149: }
1150:
1151: PLAN *
1152: c_path(pattern)
1153: char *pattern;
1154: {
1155: PLAN *new;
1156:
1157: new = palloc(N_NAME, f_path);
1158: new->c_data = pattern;
1159: return (new);
1160: }
1161:
1162: /*
1163: * -perm functions --
1164: *
1165: * The mode argument is used to represent file mode bits. If it starts
1166: * with a leading digit, it's treated as an octal mode, otherwise as a
1167: * symbolic mode.
1168: */
1169: int
1170: f_perm(plan, entry)
1171: PLAN *plan;
1172: FTSENT *entry;
1173: {
1174: mode_t mode;
1175:
1176: mode = entry->fts_statp->st_mode &
1177: (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1178: if (plan->flags == F_ATLEAST)
1179: return ((plan->m_data | mode) == mode);
1180: else
1181: return (mode == plan->m_data);
1182: /* NOTREACHED */
1183: }
1184:
1185: PLAN *
1186: c_perm(perm)
1187: char *perm;
1188: {
1189: PLAN *new;
1190: mode_t *set;
1191:
1192: ftsoptions &= ~FTS_NOSTAT;
1193:
1194: new = palloc(N_PERM, f_perm);
1195:
1196: if (*perm == '-') {
1197: new->flags = F_ATLEAST;
1198: ++perm;
1199: }
1200:
1201: if ((set = setmode(perm)) == NULL)
1.19 millert 1202: errx(1, "-perm: %s: illegal mode string", perm);
1.1 deraadt 1203:
1204: new->m_data = getmode(set, 0);
1.12 deraadt 1205: free(set);
1.1 deraadt 1206: return (new);
1207: }
1208:
1209: /*
1210: * -print functions --
1211: *
1212: * Always true, causes the current pathame to be written to
1213: * standard output.
1214: */
1215: int
1216: f_print(plan, entry)
1217: PLAN *plan;
1218: FTSENT *entry;
1219: {
1220: (void)printf("%s\n", entry->fts_path);
1221: return(1);
1222: }
1223:
1224: /* ARGSUSED */
1.9 millert 1225: int
1.1 deraadt 1226: f_print0(plan, entry)
1227: PLAN *plan;
1228: FTSENT *entry;
1229: {
1230: (void)fputs(entry->fts_path, stdout);
1231: (void)fputc('\0', stdout);
1232: return(1);
1233: }
1234:
1235: PLAN *
1236: c_print()
1237: {
1238: isoutput = 1;
1239:
1240: return(palloc(N_PRINT, f_print));
1241: }
1242:
1243: PLAN *
1244: c_print0()
1245: {
1246: isoutput = 1;
1247:
1248: return(palloc(N_PRINT0, f_print0));
1249: }
1250:
1251: /*
1252: * -prune functions --
1253: *
1254: * Prune a portion of the hierarchy.
1255: */
1256: int
1257: f_prune(plan, entry)
1258: PLAN *plan;
1259: FTSENT *entry;
1260: {
1261:
1262: if (fts_set(tree, entry, FTS_SKIP))
1263: err(1, "%s", entry->fts_path);
1264: return (1);
1265: }
1266:
1267: PLAN *
1268: c_prune()
1269: {
1270: return (palloc(N_PRUNE, f_prune));
1271: }
1272:
1273: /*
1274: * -size n[c] functions --
1275: *
1276: * True if the file size in bytes, divided by an implementation defined
1277: * value and rounded up to the next integer, is n. If n is followed by
1278: * a c, the size is in bytes.
1279: */
1280: #define FIND_SIZE 512
1281: static int divsize = 1;
1282:
1283: int
1284: f_size(plan, entry)
1285: PLAN *plan;
1286: FTSENT *entry;
1287: {
1288: off_t size;
1289:
1290: size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1291: FIND_SIZE : entry->fts_statp->st_size;
1292: COMPARE(size, plan->o_data);
1293: }
1294:
1295: PLAN *
1296: c_size(arg)
1297: char *arg;
1298: {
1299: PLAN *new;
1300: char endch;
1301:
1302: ftsoptions &= ~FTS_NOSTAT;
1303:
1304: new = palloc(N_SIZE, f_size);
1305: endch = 'c';
1306: new->o_data = find_parsenum(new, "-size", arg, &endch);
1307: if (endch == 'c')
1308: divsize = 0;
1309: return (new);
1310: }
1311:
1312: /*
1313: * -type c functions --
1314: *
1315: * True if the type of the file is c, where c is b, c, d, p, or f for
1316: * block special file, character special file, directory, FIFO, or
1317: * regular file, respectively.
1318: */
1319: int
1320: f_type(plan, entry)
1321: PLAN *plan;
1322: FTSENT *entry;
1323: {
1324: return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1325: }
1326:
1327: PLAN *
1328: c_type(typestring)
1329: char *typestring;
1330: {
1331: PLAN *new;
1332: mode_t mask;
1333:
1334: ftsoptions &= ~FTS_NOSTAT;
1335:
1336: switch (typestring[0]) {
1.2 deraadt 1337: #ifdef S_IFWHT
1.3 deraadt 1338: case 'W':
1339: mask = S_IFWHT;
1.13 espie 1340: if ((ftsoptions & FTS_WHITEOUT) == 0)
1341: warnx("-type W without -W is a no-op");
1.3 deraadt 1342: break;
1.2 deraadt 1343: #endif
1.1 deraadt 1344: case 'b':
1345: mask = S_IFBLK;
1346: break;
1347: case 'c':
1348: mask = S_IFCHR;
1349: break;
1350: case 'd':
1351: mask = S_IFDIR;
1352: break;
1353: case 'f':
1354: mask = S_IFREG;
1355: break;
1356: case 'l':
1357: mask = S_IFLNK;
1358: break;
1359: case 'p':
1360: mask = S_IFIFO;
1361: break;
1362: case 's':
1363: mask = S_IFSOCK;
1364: break;
1365: default:
1366: errx(1, "-type: %s: unknown type", typestring);
1367: }
1368:
1369: new = palloc(N_TYPE, f_type);
1370: new->m_data = mask;
1371: return (new);
1372: }
1373:
1374: /*
1375: * -user uname functions --
1376: *
1377: * True if the file belongs to the user uname. If uname is numeric and
1378: * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1379: * return a valid user name, uname is taken as a user ID.
1380: */
1381: int
1382: f_user(plan, entry)
1383: PLAN *plan;
1384: FTSENT *entry;
1385: {
1386: return (entry->fts_statp->st_uid == plan->u_data);
1387: }
1388:
1389: PLAN *
1390: c_user(username)
1391: char *username;
1392: {
1393: PLAN *new;
1394: struct passwd *p;
1395: uid_t uid;
1396:
1397: ftsoptions &= ~FTS_NOSTAT;
1398:
1399: p = getpwnam(username);
1400: if (p == NULL) {
1401: uid = atoi(username);
1402: if (uid == 0 && username[0] != '0')
1403: errx(1, "-user: %s: no such user", username);
1404: } else
1405: uid = p->pw_uid;
1406:
1407: new = palloc(N_USER, f_user);
1408: new->u_data = uid;
1409: return (new);
1410: }
1411:
1412: /*
1413: * -xdev functions --
1414: *
1415: * Always true, causes find not to decend past directories that have a
1416: * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1417: */
1418: PLAN *
1419: c_xdev()
1420: {
1421: ftsoptions |= FTS_XDEV;
1422:
1423: return (palloc(N_XDEV, f_always_true));
1424: }
1425:
1426: /*
1427: * ( expression ) functions --
1428: *
1429: * True if expression is true.
1430: */
1431: int
1432: f_expr(plan, entry)
1433: PLAN *plan;
1434: FTSENT *entry;
1435: {
1436: register PLAN *p;
1437: register int state;
1438:
1439: for (p = plan->p_data[0];
1440: p && (state = (p->eval)(p, entry)); p = p->next);
1441: return (state);
1442: }
1443:
1444: /*
1445: * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1446: * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1447: * to a N_EXPR node containing the expression and the ')' node is discarded.
1448: */
1449: PLAN *
1450: c_openparen()
1451: {
1452: return (palloc(N_OPENPAREN, (int (*)())-1));
1453: }
1454:
1455: PLAN *
1456: c_closeparen()
1457: {
1458: return (palloc(N_CLOSEPAREN, (int (*)())-1));
1459: }
1460:
1461: /*
1462: * ! expression functions --
1463: *
1464: * Negation of a primary; the unary NOT operator.
1465: */
1466: int
1467: f_not(plan, entry)
1468: PLAN *plan;
1469: FTSENT *entry;
1470: {
1471: register PLAN *p;
1472: register int state;
1473:
1474: for (p = plan->p_data[0];
1475: p && (state = (p->eval)(p, entry)); p = p->next);
1476: return (!state);
1477: }
1478:
1479: PLAN *
1480: c_not()
1481: {
1482: return (palloc(N_NOT, f_not));
1483: }
1484:
1485: /*
1486: * expression -o expression functions --
1487: *
1488: * Alternation of primaries; the OR operator. The second expression is
1489: * not evaluated if the first expression is true.
1490: */
1491: int
1492: f_or(plan, entry)
1493: PLAN *plan;
1494: FTSENT *entry;
1495: {
1496: register PLAN *p;
1497: register int state;
1498:
1499: for (p = plan->p_data[0];
1500: p && (state = (p->eval)(p, entry)); p = p->next);
1501:
1502: if (state)
1503: return (1);
1504:
1505: for (p = plan->p_data[1];
1506: p && (state = (p->eval)(p, entry)); p = p->next);
1507: return (state);
1508: }
1509:
1510: PLAN *
1511: c_or()
1512: {
1513: return (palloc(N_OR, f_or));
1514: }
1515:
1516: static PLAN *
1517: palloc(t, f)
1518: enum ntype t;
1519: int (*f) __P((PLAN *, FTSENT *));
1520: {
1521: PLAN *new;
1522:
1.17 millert 1523: if ((new = calloc(1, sizeof(PLAN)))) {
1.1 deraadt 1524: new->type = t;
1525: new->eval = f;
1526: return (new);
1527: }
1528: err(1, NULL);
1529: /* NOTREACHED */
1530: }