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