Annotation of src/usr.bin/find/function.c, Revision 1.48
1.48 ! jca 1: /* $OpenBSD: function.c,v 1.47 2019/06/28 13:35:01 deraadt 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));
1.45 tedu 297: }
298:
299: /*
300: * -delete functions
301: */
302: int
303: f_delete(PLAN *plan, FTSENT *entry)
304: {
305:
306: /* can't delete these */
307: if (strcmp(entry->fts_accpath, ".") == 0 ||
308: strcmp(entry->fts_accpath, "..") == 0)
309: return 1;
310:
311: /* sanity check */
312: if (isdepth == 0 || /* depth off */
313: (ftsoptions & FTS_NOSTAT)) /* not stat()ing */
314: errx(1, "-delete: insecure options got turned on");
315: if (!(ftsoptions & FTS_PHYSICAL) || /* physical off */
316: (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */
317: errx(1, "-delete: forbidden when symlinks are followed");
318:
319: /* Potentially unsafe - do not accept relative paths whatsoever */
320: if (entry->fts_level > FTS_ROOTLEVEL &&
321: strchr(entry->fts_accpath, '/') != NULL)
322: errx(1, "-delete: %s: relative path potentially not safe",
323: entry->fts_accpath);
324: #if 0
325: /* Turn off user immutable bits if running as root */
326: if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
327: !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
328: geteuid() == 0)
329: lchflags(entry->fts_accpath,
330: entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
331: #endif
332: /* rmdir directories, unlink everything else */
333: if (S_ISDIR(entry->fts_statp->st_mode)) {
1.47 deraadt 334: if (rmdir(entry->fts_accpath) == -1 && errno != ENOTEMPTY)
1.45 tedu 335: warn("-delete: rmdir(%s)", entry->fts_path);
336: } else {
1.47 deraadt 337: if (unlink(entry->fts_accpath) == -1)
1.45 tedu 338: warn("-delete: unlink(%s)", entry->fts_path);
339:
340: }
341:
342: return 1;
343: }
344:
345: PLAN *
346: c_delete(char *ignore, char ***ignored, int unused)
347: {
348: ftsoptions &= ~FTS_NOSTAT;
349: isoutput = 1;
350: isdelete = 1;
351: isdepth = 1;
352:
353: return (palloc(N_DELETE, f_delete));
1.1 deraadt 354: }
355:
1.7 tholo 356: /*
357: * -empty functions --
358: *
359: * True if the file or directory is empty
360: */
361: int
1.30 deraadt 362: f_empty(PLAN *plan, FTSENT *entry)
1.7 tholo 363: {
364: if (S_ISREG(entry->fts_statp->st_mode) && entry->fts_statp->st_size == 0)
365: return (1);
366: if (S_ISDIR(entry->fts_statp->st_mode)) {
367: struct dirent *dp;
368: int empty;
369: DIR *dir;
370:
371: empty = 1;
372: dir = opendir(entry->fts_accpath);
373: if (dir == NULL)
1.36 millert 374: return (0);
1.7 tholo 375: for (dp = readdir(dir); dp; dp = readdir(dir))
376: if (dp->d_name[0] != '.' ||
377: (dp->d_name[1] != '\0' &&
378: (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
379: empty = 0;
380: break;
381: }
382: closedir(dir);
383: return (empty);
384: }
385: return (0);
386: }
387:
388: PLAN *
1.28 deraadt 389: c_empty(char *ignore, char ***ignored, int unused)
1.7 tholo 390: {
391: ftsoptions &= ~FTS_NOSTAT;
392:
393: return (palloc(N_EMPTY, f_empty));
394: }
395:
1.1 deraadt 396: /*
397: * [-exec | -ok] utility [arg ... ] ; functions --
1.37 pascal 398: * [-exec | -ok] utility [arg ... ] {} + functions --
1.1 deraadt 399: *
1.37 pascal 400: * If the end of the primary expression is delimited by a
401: * semicolon: true if the executed utility returns a zero value
402: * as exit status. If "{}" occurs anywhere, it gets replaced by
403: * the current pathname.
404: *
405: * If the end of the primary expression is delimited by a plus
406: * sign: always true. Pathnames for which the primary is
407: * evaluated shall be aggregated into sets. The utility will be
408: * executed once per set, with "{}" replaced by the entire set of
409: * pathnames (as if xargs). "{}" must appear last.
410: *
411: * The current directory for the execution of utility is the same
412: * as the current directory when the find utility was started.
1.1 deraadt 413: *
1.37 pascal 414: * The primary -ok is different in that it requests affirmation
415: * of the user before executing the utility.
1.1 deraadt 416: */
417: int
1.30 deraadt 418: f_exec(PLAN *plan, FTSENT *entry)
1.1 deraadt 419: {
1.37 pascal 420: int cnt, l;
1.1 deraadt 421: pid_t pid;
422: int status;
423:
1.37 pascal 424: if (plan->flags & F_PLUSSET) {
425: /*
426: * Confirm sufficient buffer space, then copy the path
427: * to the buffer.
428: */
429: l = strlen(entry->fts_path);
430: if (plan->ep_p + l < plan->ep_ebp) {
431: plan->ep_bxp[plan->ep_narg++] = plan->ep_p;
432: strlcpy(plan->ep_p, entry->fts_path, l + 1);
433: plan->ep_p += l + 1;
434:
435: if (plan->ep_narg == plan->ep_maxargs)
436: run_f_exec(plan);
437: } else {
438: /*
439: * Without sufficient space to copy in the next
440: * argument, run the command to empty out the
441: * buffer before re-attepting the copy.
442: */
443: run_f_exec(plan);
444: if (plan->ep_p + l < plan->ep_ebp) {
445: plan->ep_bxp[plan->ep_narg++] = plan->ep_p;
446: strlcpy(plan->ep_p, entry->fts_path, l + 1);
447: plan->ep_p += l + 1;
448: } else
449: errx(1, "insufficient space for argument");
450: }
451: return (1);
452: } else {
453: for (cnt = 0; plan->e_argv[cnt]; ++cnt)
454: if (plan->e_len[cnt])
455: brace_subst(plan->e_orig[cnt],
456: &plan->e_argv[cnt],
457: entry->fts_path,
458: plan->e_len[cnt]);
459: if (plan->flags & F_NEEDOK && !queryuser(plan->e_argv))
460: return (0);
461:
462: /* don't mix output of command with find output */
463: fflush(stdout);
464: fflush(stderr);
465:
466: switch (pid = vfork()) {
467: case -1:
468: err(1, "fork");
469: /* NOTREACHED */
470: case 0:
471: if (fchdir(dotfd)) {
472: warn("chdir");
473: _exit(1);
474: }
475: execvp(plan->e_argv[0], plan->e_argv);
476: warn("%s", plan->e_argv[0]);
477: _exit(1);
478: }
479: pid = waitpid(pid, &status, 0);
480: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
481: }
482: }
483:
484: static void
485: run_f_exec(PLAN *plan)
486: {
487: pid_t pid;
488: int rval, status;
1.1 deraadt 489:
1.37 pascal 490: /* Ensure arg list is null terminated. */
491: plan->ep_bxp[plan->ep_narg] = NULL;
1.1 deraadt 492:
1.37 pascal 493: /* Don't mix output of command with find output. */
494: fflush(stdout);
495: fflush(stderr);
1.1 deraadt 496:
497: switch (pid = vfork()) {
498: case -1:
1.37 pascal 499: err(1, "vfork");
1.1 deraadt 500: /* NOTREACHED */
501: case 0:
502: if (fchdir(dotfd)) {
503: warn("chdir");
504: _exit(1);
505: }
506: execvp(plan->e_argv[0], plan->e_argv);
507: warn("%s", plan->e_argv[0]);
508: _exit(1);
509: }
1.37 pascal 510:
511: /* Clear out the argument list. */
512: plan->ep_narg = 0;
513: plan->ep_bxp[plan->ep_narg] = NULL;
514: /* As well as the argument buffer. */
515: plan->ep_p = plan->ep_bbp;
516: *plan->ep_p = '\0';
517:
1.1 deraadt 518: pid = waitpid(pid, &status, 0);
1.37 pascal 519: if (WIFEXITED(status))
520: rval = WEXITSTATUS(status);
521: else
522: rval = -1;
523:
524: /*
525: * If we have a non-zero exit status, preserve it so find(1) can
526: * later exit with it.
527: */
528: if (rval)
529: plan->ep_rval = rval;
1.1 deraadt 530: }
531:
532: /*
533: * c_exec --
534: * build three parallel arrays, one with pointers to the strings passed
535: * on the command line, one with (possibly duplicated) pointers to the
536: * argv array, and one with integer values that are lengths of the
537: * strings, but also flags meaning that the string has to be massaged.
1.37 pascal 538: *
539: * If -exec ... {} +, use only the first array, but make it large
540: * enough to hold 5000 args (cf. src/usr.bin/xargs/xargs.c for a
1.48 ! jca 541: * discussion), and then allocate space for args.
1.1 deraadt 542: */
543: PLAN *
1.28 deraadt 544: c_exec(char *unused, char ***argvp, int isok)
1.1 deraadt 545: {
546: PLAN *new; /* node returned */
1.37 pascal 547: int cnt, brace, lastbrace;
1.21 mpech 548: char **argv, **ap, *p;
1.35 millert 549:
550: /* make sure the current directory is readable */
551: if (dotfd == -1)
552: errx(1, "%s: cannot open \".\"", isok ? "-ok" : "-exec");
1.1 deraadt 553:
554: isoutput = 1;
555:
556: new = palloc(N_EXEC, f_exec);
557: if (isok)
1.37 pascal 558: new->flags |= F_NEEDOK;
1.1 deraadt 559:
1.37 pascal 560: /*
1.38 sthen 561: * Terminate if we encounter an arg exactly equal to ";", or an
562: * arg exactly equal to "+" following an arg exactly equal to
1.37 pascal 563: * "{}".
564: */
565: for (ap = argv = *argvp, brace = 0;; ++ap) {
1.1 deraadt 566: if (!*ap)
1.37 pascal 567: errx(1, "%s: no terminating \";\" or \"+\"",
568: isok ? "-ok" : "-exec");
569: lastbrace = brace;
570: brace = 0;
571: if (strcmp(*ap, "{}") == 0)
572: brace = 1;
573: if (strcmp(*ap, ";") == 0)
574: break;
575: if (strcmp(*ap, "+") == 0 && lastbrace) {
576: new->flags |= F_PLUSSET;
1.8 millert 577: break;
1.37 pascal 578: }
1.8 millert 579: }
580:
581:
1.37 pascal 582: /*
583: * POSIX says -ok ... {} + "need not be supported," and it does
584: * not make much sense anyway.
585: */
586: if (new->flags & F_NEEDOK && new->flags & F_PLUSSET)
587: errx(1, "-ok: terminating \"+\" not permitted.");
588:
589: if (new->flags & F_PLUSSET) {
1.48 ! jca 590: long arg_max;
1.37 pascal 591: u_int c, bufsize;
592:
593: cnt = ap - *argvp - 1; /* units are words */
594: new->ep_maxargs = 5000;
1.41 espie 595: new->e_argv = ereallocarray(NULL,
596: (size_t)(cnt + new->ep_maxargs), sizeof(char **));
1.37 pascal 597:
598: /* We start stuffing arguments after the user's last one. */
599: new->ep_bxp = &new->e_argv[cnt];
600: new->ep_narg = 0;
601:
602: /*
1.48 ! jca 603: * Compute the maximum space we can use for arguments
! 604: * passed to execve(2).
! 605: */
! 606: arg_max = sysconf(_SC_ARG_MAX);
! 607: if (arg_max == -1)
! 608: err(1, "sysconf(_SC_ARG_MAX) failed");
! 609:
! 610: /*
1.37 pascal 611: * Count up the space of the user's arguments, and
612: * subtract that from what we allocate.
613: */
614: for (argv = *argvp, c = 0, cnt = 0;
615: argv < ap;
616: ++argv, ++cnt) {
617: c += strlen(*argv) + 1;
618: new->e_argv[cnt] = *argv;
619: }
1.48 ! jca 620: bufsize = arg_max - 4 * 1024 - c;
1.37 pascal 621:
622: /*
623: * Allocate, and then initialize current, base, and
624: * end pointers.
625: */
626: new->ep_p = new->ep_bbp = malloc(bufsize + 1);
627: new->ep_ebp = new->ep_bbp + bufsize - 1;
628: new->ep_rval = 0;
629: } else { /* !F_PLUSSET */
630: cnt = ap - *argvp + 1;
1.41 espie 631: new->e_argv = ereallocarray(NULL, cnt, sizeof(char *));
632: new->e_orig = ereallocarray(NULL, cnt, sizeof(char *));
633: new->e_len = ereallocarray(NULL, cnt, sizeof(int));
1.37 pascal 634:
635: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
636: new->e_orig[cnt] = *argv;
637: for (p = *argv; *p; ++p)
638: if (p[0] == '{' && p[1] == '}') {
639: new->e_argv[cnt] =
1.42 deraadt 640: emalloc((u_int)PATH_MAX);
641: new->e_len[cnt] = PATH_MAX;
1.37 pascal 642: break;
643: }
644: if (!*p) {
645: new->e_argv[cnt] = *argv;
646: new->e_len[cnt] = 0;
1.8 millert 647: }
648: }
1.37 pascal 649: new->e_orig[cnt] = NULL;
650: }
1.8 millert 651:
1.37 pascal 652: new->e_argv[cnt] = NULL;
1.8 millert 653: *argvp = argv + 1;
654: return (new);
655: }
656:
657: /*
658: * -execdir utility [arg ... ] ; functions --
659: *
660: * True if the executed utility returns a zero value as exit status.
661: * The end of the primary expression is delimited by a semicolon. If
662: * "{}" occurs anywhere, it gets replaced by the unqualified pathname.
663: * The current directory for the execution of utility is the same as
664: * the directory where the file lives.
665: */
666: int
1.30 deraadt 667: f_execdir(PLAN *plan, FTSENT *entry)
1.8 millert 668: {
1.21 mpech 669: int cnt;
1.8 millert 670: pid_t pid;
1.15 millert 671: int status, fd;
1.42 deraadt 672: char base[PATH_MAX];
1.8 millert 673:
1.15 millert 674: /* fts(3) does not chdir for the root level so we do it ourselves. */
675: if (entry->fts_level == FTS_ROOTLEVEL) {
676: if ((fd = open(".", O_RDONLY)) == -1) {
677: warn("cannot open \".\"");
678: return (0);
679: }
680: if (chdir(entry->fts_accpath)) {
1.16 millert 681: (void) close(fd);
1.15 millert 682: return (0);
683: }
684: }
685:
1.10 millert 686: /* Substitute basename(path) for {} since cwd is it's parent dir */
687: (void)strncpy(base, basename(entry->fts_path), sizeof(base) - 1);
688: base[sizeof(base) - 1] = '\0';
1.8 millert 689: for (cnt = 0; plan->e_argv[cnt]; ++cnt)
690: if (plan->e_len[cnt])
691: brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
1.10 millert 692: base, plan->e_len[cnt]);
1.8 millert 693:
694: /* don't mix output of command with find output */
695: fflush(stdout);
696: fflush(stderr);
697:
698: switch (pid = vfork()) {
699: case -1:
700: err(1, "fork");
701: /* NOTREACHED */
702: case 0:
703: execvp(plan->e_argv[0], plan->e_argv);
704: warn("%s", plan->e_argv[0]);
705: _exit(1);
706: }
1.15 millert 707:
708: /* Undo the above... */
709: if (entry->fts_level == FTS_ROOTLEVEL) {
710: if (fchdir(fd) == -1) {
711: warn("unable to chdir back to starting directory");
1.16 millert 712: (void) close(fd);
1.15 millert 713: return (0);
714: }
1.16 millert 715: (void) close(fd);
1.15 millert 716: }
717:
1.8 millert 718: pid = waitpid(pid, &status, 0);
719: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
720: }
721:
722: /*
723: * c_execdir --
724: * build three parallel arrays, one with pointers to the strings passed
725: * on the command line, one with (possibly duplicated) pointers to the
726: * argv array, and one with integer values that are lengths of the
727: * strings, but also flags meaning that the string has to be massaged.
728: */
729: PLAN *
1.28 deraadt 730: c_execdir(char *ignored, char ***argvp, int unused)
1.8 millert 731: {
732: PLAN *new; /* node returned */
1.21 mpech 733: int cnt;
734: char **argv, **ap, *p;
1.8 millert 735:
736: ftsoptions &= ~FTS_NOSTAT;
737: isoutput = 1;
738:
739: new = palloc(N_EXECDIR, f_execdir);
740:
741: for (ap = argv = *argvp;; ++ap) {
742: if (!*ap)
743: errx(1,
744: "-execdir: no terminating \";\"");
1.1 deraadt 745: if (**ap == ';')
746: break;
747: }
748:
749: cnt = ap - *argvp + 1;
1.41 espie 750: new->e_argv = ereallocarray(NULL, cnt, sizeof(char *));
751: new->e_orig = ereallocarray(NULL, cnt, sizeof(char *));
752: new->e_len = ereallocarray(NULL, cnt, sizeof(int));
1.1 deraadt 753:
754: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
755: new->e_orig[cnt] = *argv;
756: for (p = *argv; *p; ++p)
757: if (p[0] == '{' && p[1] == '}') {
1.42 deraadt 758: new->e_argv[cnt] = emalloc((u_int)PATH_MAX);
759: new->e_len[cnt] = PATH_MAX;
1.1 deraadt 760: break;
761: }
762: if (!*p) {
763: new->e_argv[cnt] = *argv;
764: new->e_len[cnt] = 0;
765: }
766: }
767: new->e_argv[cnt] = new->e_orig[cnt] = NULL;
768:
769: *argvp = argv + 1;
770: return (new);
771: }
1.19 millert 772:
773: /*
774: * -flags functions --
775: *
776: * The flags argument is used to represent file flags bits.
777: */
778: int
1.30 deraadt 779: f_flags(PLAN *plan, FTSENT *entry)
1.19 millert 780: {
781: u_int flags;
782:
783: flags = entry->fts_statp->st_flags &
784: (UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE |
785: SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND);
786: if (plan->flags == F_ATLEAST)
787: /* note that plan->fl_flags always is a subset of
788: plan->fl_mask */
789: return ((flags & plan->fl_mask) == plan->fl_flags);
790: else
791: return (flags == plan->fl_flags);
792: /* NOTREACHED */
793: }
794:
795: PLAN *
1.28 deraadt 796: c_flags(char *flags_str, char ***ignored, int unused)
1.19 millert 797: {
798: PLAN *new;
1.20 mickey 799: u_int32_t flags, notflags;
1.19 millert 800:
801: ftsoptions &= ~FTS_NOSTAT;
802:
803: new = palloc(N_FLAGS, f_flags);
804:
805: if (*flags_str == '-') {
806: new->flags = F_ATLEAST;
807: ++flags_str;
808: }
809:
1.20 mickey 810: if (strtofflags(&flags_str, &flags, ¬flags) == 1)
1.19 millert 811: errx(1, "-flags: %s: illegal flags string", flags_str);
812:
813: new->fl_flags = flags;
814: new->fl_mask = flags | notflags;
815: return (new);
816: }
1.1 deraadt 817:
818: /*
819: * -follow functions --
820: *
821: * Always true, causes symbolic links to be followed on a global
822: * basis.
823: */
824: PLAN *
1.28 deraadt 825: c_follow(char *ignore, char ***ignored, int unused)
1.1 deraadt 826: {
827: ftsoptions &= ~FTS_PHYSICAL;
828: ftsoptions |= FTS_LOGICAL;
829:
830: return (palloc(N_FOLLOW, f_always_true));
831: }
832:
833: /*
834: * -fstype functions --
835: *
836: * True if the file is of a certain type.
837: */
838: int
1.30 deraadt 839: f_fstype(PLAN *plan, FTSENT *entry)
1.1 deraadt 840: {
841: static dev_t curdev; /* need a guaranteed illegal dev value */
842: static int first = 1;
843: struct statfs sb;
844: static short val;
845: static char fstype[MFSNAMELEN];
846: char *p, save[2];
847:
848: /* Only check when we cross mount point. */
849: if (first || curdev != entry->fts_statp->st_dev) {
850: curdev = entry->fts_statp->st_dev;
851:
852: /*
853: * Statfs follows symlinks; find wants the link's file system,
854: * not where it points.
855: */
856: if (entry->fts_info == FTS_SL ||
857: entry->fts_info == FTS_SLNONE) {
1.9 millert 858: if ((p = strrchr(entry->fts_accpath, '/')))
1.1 deraadt 859: ++p;
860: else
861: p = entry->fts_accpath;
862: save[0] = p[0];
863: p[0] = '.';
864: save[1] = p[1];
865: p[1] = '\0';
866:
867: } else
868: p = NULL;
869:
870: if (statfs(entry->fts_accpath, &sb))
871: err(1, "%s", entry->fts_accpath);
872:
873: if (p) {
874: p[0] = save[0];
875: p[1] = save[1];
876: }
877:
878: first = 0;
879:
880: /*
881: * Further tests may need both of these values, so
882: * always copy both of them.
883: */
884: val = sb.f_flags;
885: strncpy(fstype, sb.f_fstypename, MFSNAMELEN);
886: }
887: switch (plan->flags) {
888: case F_MTFLAG:
889: return (val & plan->mt_data);
890: case F_MTTYPE:
891: return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
892: default:
893: abort();
894: }
895: }
896:
897: PLAN *
1.28 deraadt 898: c_fstype(char *arg, char ***ignored, int unused)
1.1 deraadt 899: {
1.21 mpech 900: PLAN *new;
1.1 deraadt 901:
902: ftsoptions &= ~FTS_NOSTAT;
903:
904: new = palloc(N_FSTYPE, f_fstype);
905: switch (*arg) {
906: case 'l':
907: if (!strcmp(arg, "local")) {
908: new->flags = F_MTFLAG;
909: new->mt_data = MNT_LOCAL;
910: return (new);
911: }
912: break;
913: case 'r':
914: if (!strcmp(arg, "rdonly")) {
915: new->flags = F_MTFLAG;
916: new->mt_data = MNT_RDONLY;
917: return (new);
918: }
919: break;
920: }
921:
922: new->flags = F_MTTYPE;
923: new->c_data = arg;
924: return (new);
925: }
926:
927: /*
928: * -group gname functions --
929: *
930: * True if the file belongs to the group gname. If gname is numeric and
931: * an equivalent of the getgrnam() function does not return a valid group
932: * name, gname is taken as a group ID.
933: */
934: int
1.30 deraadt 935: f_group(PLAN *plan, FTSENT *entry)
1.1 deraadt 936: {
937: return (entry->fts_statp->st_gid == plan->g_data);
938: }
939:
940: PLAN *
1.28 deraadt 941: c_group(char *gname, char ***ignored, int unused)
1.1 deraadt 942: {
943: PLAN *new;
944: gid_t gid;
945:
946: ftsoptions &= ~FTS_NOSTAT;
947:
1.46 millert 948: if (gid_from_group(gname, &gid) == -1) {
1.44 deraadt 949: const char *errstr;
950:
951: gid = strtonum(gname, 0, GID_MAX, &errstr);
952: if (errstr)
1.1 deraadt 953: errx(1, "-group: %s: no such group", gname);
1.46 millert 954: }
1.1 deraadt 955:
956: new = palloc(N_GROUP, f_group);
957: new->g_data = gid;
958: return (new);
959: }
960:
961: /*
962: * -inum n functions --
963: *
964: * True if the file has inode # n.
965: */
966: int
1.30 deraadt 967: f_inum(PLAN *plan, FTSENT *entry)
1.1 deraadt 968: {
969: COMPARE(entry->fts_statp->st_ino, plan->i_data);
970: }
971:
972: PLAN *
1.28 deraadt 973: c_inum(char *arg, char ***ignored, int unused)
1.1 deraadt 974: {
1.39 deraadt 975: long long inum;
1.1 deraadt 976: PLAN *new;
977:
978: ftsoptions &= ~FTS_NOSTAT;
979:
980: new = palloc(N_INUM, f_inum);
1.39 deraadt 981: inum = find_parsenum(new, "-inum", arg, NULL);
982: if (inum != (ino_t)inum)
983: errx(1, "-inum: %s: number too great", arg);
984: new->i_data = inum;
1.1 deraadt 985: return (new);
986: }
987:
988: /*
989: * -links n functions --
990: *
991: * True if the file has n links.
992: */
993: int
1.30 deraadt 994: f_links(PLAN *plan, FTSENT *entry)
1.1 deraadt 995: {
996: COMPARE(entry->fts_statp->st_nlink, plan->l_data);
997: }
998:
999: PLAN *
1.28 deraadt 1000: c_links(char *arg, char ***ignored, int unused)
1.1 deraadt 1001: {
1002: PLAN *new;
1.39 deraadt 1003: long long nlink;
1.1 deraadt 1004:
1005: ftsoptions &= ~FTS_NOSTAT;
1006:
1007: new = palloc(N_LINKS, f_links);
1.39 deraadt 1008: nlink = find_parsenum(new, "-links", arg, NULL);
1009: if (nlink != (nlink_t)nlink)
1010: errx(1, "-links: %s: number too great", arg);
1011: new->l_data = nlink;
1.1 deraadt 1012: return (new);
1013: }
1014:
1015: /*
1016: * -ls functions --
1017: *
1018: * Always true - prints the current entry to stdout in "ls" format.
1019: */
1020: int
1.30 deraadt 1021: f_ls(PLAN *plan, FTSENT *entry)
1.1 deraadt 1022: {
1023: printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
1024: return (1);
1025: }
1026:
1027: PLAN *
1.28 deraadt 1028: c_ls(char *ignore, char ***ignored, int unused)
1.1 deraadt 1029: {
1030: ftsoptions &= ~FTS_NOSTAT;
1031: isoutput = 1;
1032:
1033: return (palloc(N_LS, f_ls));
1.5 tholo 1034: }
1035:
1036: /*
1037: * - maxdepth n functions --
1038: *
1039: * True if the current search depth is less than or equal to the
1040: * maximum depth specified
1041: */
1042: int
1.30 deraadt 1043: f_maxdepth(PLAN *plan, FTSENT *entry)
1.5 tholo 1044: {
1045:
1.6 tholo 1046: if (entry->fts_level >= plan->max_data)
1.5 tholo 1047: fts_set(tree, entry, FTS_SKIP);
1.6 tholo 1048: return (entry->fts_level <= plan->max_data);
1.5 tholo 1049: }
1050:
1051: PLAN *
1.28 deraadt 1052: c_maxdepth(char *arg, char ***ignored, int unused)
1.5 tholo 1053: {
1054: PLAN *new;
1.32 millert 1055: const char *errstr = NULL;
1.5 tholo 1056:
1.6 tholo 1057: new = palloc(N_MAXDEPTH, f_maxdepth);
1.33 millert 1058: new->max_data = strtonum(arg, 0, FTS_MAXLEVEL, &errstr);
1.32 millert 1059: if (errstr)
1060: errx(1, "%s: maxdepth value %s", arg, errstr);
1.6 tholo 1061: return (new);
1062: }
1063:
1064: /*
1065: * - mindepth n functions --
1066: *
1067: * True if the current search depth is greater than or equal to the
1068: * minimum depth specified
1069: */
1070: int
1.30 deraadt 1071: f_mindepth(PLAN *plan, FTSENT *entry)
1.6 tholo 1072: {
1073:
1074: return (entry->fts_level >= plan->min_data);
1075: }
1076:
1077: PLAN *
1.28 deraadt 1078: c_mindepth(char *arg, char ***ignored, int unused)
1.6 tholo 1079: {
1080: PLAN *new;
1.44 deraadt 1081: const char *errstr = NULL;
1.6 tholo 1082:
1083: new = palloc(N_MINDEPTH, f_mindepth);
1.44 deraadt 1084: new->min_data = strtonum(arg, 0, INT_MAX, &errstr);
1085: if (errstr)
1086: errx(1, "-mindepth: %s: value %s", arg, errstr);
1.5 tholo 1087: return (new);
1.1 deraadt 1088: }
1089:
1090: /*
1091: * -mtime n functions --
1092: *
1093: * True if the difference between the file modification time and the
1094: * current time is n 24 hour periods.
1095: */
1096: int
1.30 deraadt 1097: f_mtime(PLAN *plan, FTSENT *entry)
1.1 deraadt 1098: {
1099:
1100: COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
1.17 millert 1101: SECSPERDAY, plan->sec_data);
1.1 deraadt 1102: }
1103:
1104: PLAN *
1.28 deraadt 1105: c_mtime(char *arg, char ***ignored, int unused)
1.1 deraadt 1106: {
1107: PLAN *new;
1108:
1109: ftsoptions &= ~FTS_NOSTAT;
1110:
1111: new = palloc(N_MTIME, f_mtime);
1.17 millert 1112: new->sec_data = find_parsenum(new, "-mtime", arg, NULL);
1.1 deraadt 1113: TIME_CORRECT(new, N_MTIME);
1.11 deraadt 1114: return (new);
1115: }
1116:
1117: /*
1118: * -mmin n functions --
1119: *
1120: * True if the difference between the file modification time and the
1121: * current time is n min periods.
1122: */
1123: int
1.30 deraadt 1124: f_mmin(PLAN *plan, FTSENT *entry)
1.11 deraadt 1125: {
1126: extern time_t now;
1127:
1128: COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /
1.17 millert 1129: 60, plan->sec_data);
1.11 deraadt 1130: }
1131:
1132: PLAN *
1.28 deraadt 1133: c_mmin(char *arg, char ***ignored, int unused)
1.11 deraadt 1134: {
1135: PLAN *new;
1136:
1137: ftsoptions &= ~FTS_NOSTAT;
1138:
1139: new = palloc(N_MMIN, f_mmin);
1.17 millert 1140: new->sec_data = find_parsenum(new, "-mmin", arg, NULL);
1.11 deraadt 1141: TIME_CORRECT(new, N_MMIN);
1.1 deraadt 1142: return (new);
1143: }
1144:
1145: /*
1146: * -name functions --
1147: *
1148: * True if the basename of the filename being examined
1149: * matches pattern using Pattern Matching Notation S3.14
1150: */
1151: int
1.30 deraadt 1152: f_name(PLAN *plan, FTSENT *entry)
1.1 deraadt 1153: {
1154: return (!fnmatch(plan->c_data, entry->fts_name, 0));
1155: }
1156:
1157: PLAN *
1.28 deraadt 1158: c_name(char *pattern, char ***ignored, int unused)
1.1 deraadt 1159: {
1160: PLAN *new;
1161:
1162: new = palloc(N_NAME, f_name);
1.18 deraadt 1163: new->c_data = pattern;
1164: return (new);
1165: }
1166:
1167: /*
1168: * -iname functions --
1169: *
1170: * Similar to -name, but does case insensitive matching
1171: *
1172: */
1173: int
1.30 deraadt 1174: f_iname(PLAN *plan, FTSENT *entry)
1.18 deraadt 1175: {
1176: return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD));
1177: }
1178:
1179: PLAN *
1.28 deraadt 1180: c_iname(char *pattern, char ***ignored, int unused)
1.18 deraadt 1181: {
1182: PLAN *new;
1183:
1184: new = palloc(N_INAME, f_iname);
1.1 deraadt 1185: new->c_data = pattern;
1186: return (new);
1187: }
1188:
1189: /*
1190: * -newer file functions --
1191: *
1192: * True if the current file has been modified more recently
1193: * then the modification time of the file named by the pathname
1194: * file.
1195: */
1196: int
1.30 deraadt 1197: f_newer(PLAN *plan, FTSENT *entry)
1.1 deraadt 1198: {
1.17 millert 1199:
1200: return (entry->fts_statp->st_mtimespec.tv_sec > plan->t_data.tv_sec ||
1201: (entry->fts_statp->st_mtimespec.tv_sec == plan->t_data.tv_sec &&
1202: entry->fts_statp->st_mtimespec.tv_nsec > plan->t_data.tv_nsec));
1.1 deraadt 1203: }
1204:
1205: PLAN *
1.28 deraadt 1206: c_newer(char *filename, char ***ignored, int unused)
1.1 deraadt 1207: {
1208: PLAN *new;
1209: struct stat sb;
1210:
1211: ftsoptions &= ~FTS_NOSTAT;
1212:
1213: if (stat(filename, &sb))
1214: err(1, "%s", filename);
1215: new = palloc(N_NEWER, f_newer);
1.17 millert 1216: memcpy(&new->t_data, &sb.st_mtimespec, sizeof(struct timespec));
1217: return (new);
1218: }
1219:
1220: /*
1221: * -anewer file functions --
1222: *
1223: * True if the current file has been accessed more recently
1224: * then the access time of the file named by the pathname
1225: * file.
1226: */
1227: int
1.30 deraadt 1228: f_anewer(PLAN *plan, FTSENT *entry)
1.17 millert 1229: {
1230:
1231: return (entry->fts_statp->st_atimespec.tv_sec > plan->t_data.tv_sec ||
1232: (entry->fts_statp->st_atimespec.tv_sec == plan->t_data.tv_sec &&
1233: entry->fts_statp->st_atimespec.tv_nsec > plan->t_data.tv_nsec));
1234: }
1235:
1236: PLAN *
1.28 deraadt 1237: c_anewer(char *filename, char ***ignored, int unused)
1.17 millert 1238: {
1239: PLAN *new;
1240: struct stat sb;
1241:
1242: ftsoptions &= ~FTS_NOSTAT;
1243:
1244: if (stat(filename, &sb))
1245: err(1, "%s", filename);
1.24 millert 1246: new = palloc(N_NEWER, f_anewer);
1.17 millert 1247: memcpy(&new->t_data, &sb.st_atimespec, sizeof(struct timespec));
1248: return (new);
1249: }
1250:
1251: /*
1252: * -cnewer file functions --
1253: *
1254: * True if the current file has been changed more recently
1255: * then the inode change time of the file named by the pathname
1256: * file.
1257: */
1258: int
1.30 deraadt 1259: f_cnewer(PLAN *plan, FTSENT *entry)
1.17 millert 1260: {
1261:
1262: return (entry->fts_statp->st_ctimespec.tv_sec > plan->t_data.tv_sec ||
1263: (entry->fts_statp->st_ctimespec.tv_sec == plan->t_data.tv_sec &&
1264: entry->fts_statp->st_ctimespec.tv_nsec > plan->t_data.tv_nsec));
1265: }
1266:
1267: PLAN *
1.28 deraadt 1268: c_cnewer(char *filename, char ***ignored, int unused)
1.17 millert 1269: {
1270: PLAN *new;
1271: struct stat sb;
1272:
1273: ftsoptions &= ~FTS_NOSTAT;
1274:
1275: if (stat(filename, &sb))
1276: err(1, "%s", filename);
1.24 millert 1277: new = palloc(N_NEWER, f_cnewer);
1.17 millert 1278: memcpy(&new->t_data, &sb.st_ctimespec, sizeof(struct timespec));
1.1 deraadt 1279: return (new);
1280: }
1281:
1282: /*
1283: * -nogroup functions --
1284: *
1285: * True if file belongs to a user ID for which the equivalent
1286: * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
1287: */
1288: int
1.30 deraadt 1289: f_nogroup(PLAN *plan, FTSENT *entry)
1.1 deraadt 1290: {
1291: return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
1292: }
1293:
1294: PLAN *
1.28 deraadt 1295: c_nogroup(char *ignore, char ***ignored, int unused)
1.1 deraadt 1296: {
1297: ftsoptions &= ~FTS_NOSTAT;
1298:
1299: return (palloc(N_NOGROUP, f_nogroup));
1300: }
1301:
1302: /*
1303: * -nouser functions --
1304: *
1305: * True if file belongs to a user ID for which the equivalent
1306: * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
1307: */
1308: int
1.30 deraadt 1309: f_nouser(PLAN *plan, FTSENT *entry)
1.1 deraadt 1310: {
1311: return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
1312: }
1313:
1314: PLAN *
1.28 deraadt 1315: c_nouser(char *ignore, char ***ignored, int unused)
1.1 deraadt 1316: {
1317: ftsoptions &= ~FTS_NOSTAT;
1318:
1319: return (palloc(N_NOUSER, f_nouser));
1320: }
1321:
1322: /*
1323: * -path functions --
1324: *
1325: * True if the path of the filename being examined
1326: * matches pattern using Pattern Matching Notation S3.14
1327: */
1328: int
1.30 deraadt 1329: f_path(PLAN *plan, FTSENT *entry)
1.1 deraadt 1330: {
1331: return (!fnmatch(plan->c_data, entry->fts_path, 0));
1332: }
1333:
1334: PLAN *
1.28 deraadt 1335: c_path(char *pattern, char ***ignored, int unused)
1.1 deraadt 1336: {
1337: PLAN *new;
1338:
1339: new = palloc(N_NAME, f_path);
1340: new->c_data = pattern;
1341: return (new);
1342: }
1343:
1344: /*
1345: * -perm functions --
1346: *
1347: * The mode argument is used to represent file mode bits. If it starts
1348: * with a leading digit, it's treated as an octal mode, otherwise as a
1349: * symbolic mode.
1350: */
1351: int
1.30 deraadt 1352: f_perm(PLAN *plan, FTSENT *entry)
1.1 deraadt 1353: {
1354: mode_t mode;
1355:
1356: mode = entry->fts_statp->st_mode &
1357: (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1358: if (plan->flags == F_ATLEAST)
1359: return ((plan->m_data | mode) == mode);
1360: else
1361: return (mode == plan->m_data);
1362: /* NOTREACHED */
1363: }
1364:
1365: PLAN *
1.28 deraadt 1366: c_perm(char *perm, char ***ignored, int unused)
1.1 deraadt 1367: {
1368: PLAN *new;
1.29 otto 1369: void *set;
1.1 deraadt 1370:
1371: ftsoptions &= ~FTS_NOSTAT;
1372:
1373: new = palloc(N_PERM, f_perm);
1374:
1375: if (*perm == '-') {
1376: new->flags = F_ATLEAST;
1377: ++perm;
1378: }
1379:
1380: if ((set = setmode(perm)) == NULL)
1.19 millert 1381: errx(1, "-perm: %s: illegal mode string", perm);
1.1 deraadt 1382:
1383: new->m_data = getmode(set, 0);
1.12 deraadt 1384: free(set);
1.1 deraadt 1385: return (new);
1386: }
1387:
1388: /*
1389: * -print functions --
1390: *
1391: * Always true, causes the current pathame to be written to
1392: * standard output.
1393: */
1394: int
1.30 deraadt 1395: f_print(PLAN *plan, FTSENT *entry)
1.1 deraadt 1396: {
1397: (void)printf("%s\n", entry->fts_path);
1398: return(1);
1399: }
1400:
1401: /* ARGSUSED */
1.9 millert 1402: int
1.30 deraadt 1403: f_print0(PLAN *plan, FTSENT *entry)
1.1 deraadt 1404: {
1405: (void)fputs(entry->fts_path, stdout);
1406: (void)fputc('\0', stdout);
1407: return(1);
1408: }
1409:
1410: PLAN *
1.28 deraadt 1411: c_print(char *ignore, char ***ignored, int unused)
1.1 deraadt 1412: {
1413: isoutput = 1;
1414:
1415: return(palloc(N_PRINT, f_print));
1416: }
1417:
1418: PLAN *
1.28 deraadt 1419: c_print0(char *ignore, char ***ignored, int unused)
1.1 deraadt 1420: {
1421: isoutput = 1;
1422:
1423: return(palloc(N_PRINT0, f_print0));
1424: }
1425:
1426: /*
1427: * -prune functions --
1428: *
1429: * Prune a portion of the hierarchy.
1430: */
1431: int
1.30 deraadt 1432: f_prune(PLAN *plan, FTSENT *entry)
1.1 deraadt 1433: {
1434:
1435: if (fts_set(tree, entry, FTS_SKIP))
1436: err(1, "%s", entry->fts_path);
1437: return (1);
1438: }
1439:
1440: PLAN *
1.28 deraadt 1441: c_prune(char *ignore, char ***ignored, int unused)
1.1 deraadt 1442: {
1443: return (palloc(N_PRUNE, f_prune));
1444: }
1445:
1446: /*
1447: * -size n[c] functions --
1448: *
1449: * True if the file size in bytes, divided by an implementation defined
1450: * value and rounded up to the next integer, is n. If n is followed by
1451: * a c, the size is in bytes.
1452: */
1453: #define FIND_SIZE 512
1454: static int divsize = 1;
1455:
1456: int
1.30 deraadt 1457: f_size(PLAN *plan, FTSENT *entry)
1.1 deraadt 1458: {
1459: off_t size;
1460:
1461: size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1462: FIND_SIZE : entry->fts_statp->st_size;
1463: COMPARE(size, plan->o_data);
1464: }
1465:
1466: PLAN *
1.28 deraadt 1467: c_size(char *arg, char ***ignored, int unused)
1.1 deraadt 1468: {
1469: PLAN *new;
1470: char endch;
1471:
1472: ftsoptions &= ~FTS_NOSTAT;
1473:
1474: new = palloc(N_SIZE, f_size);
1475: endch = 'c';
1476: new->o_data = find_parsenum(new, "-size", arg, &endch);
1477: if (endch == 'c')
1478: divsize = 0;
1479: return (new);
1480: }
1481:
1482: /*
1483: * -type c functions --
1484: *
1485: * True if the type of the file is c, where c is b, c, d, p, or f for
1486: * block special file, character special file, directory, FIFO, or
1487: * regular file, respectively.
1488: */
1489: int
1.30 deraadt 1490: f_type(PLAN *plan, FTSENT *entry)
1.1 deraadt 1491: {
1492: return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1493: }
1494:
1495: PLAN *
1.28 deraadt 1496: c_type(char *typestring, char ***ignored, int unused)
1.1 deraadt 1497: {
1498: PLAN *new;
1499: mode_t mask;
1500:
1501: ftsoptions &= ~FTS_NOSTAT;
1502:
1503: switch (typestring[0]) {
1504: case 'b':
1505: mask = S_IFBLK;
1506: break;
1507: case 'c':
1508: mask = S_IFCHR;
1509: break;
1510: case 'd':
1511: mask = S_IFDIR;
1512: break;
1513: case 'f':
1514: mask = S_IFREG;
1515: break;
1516: case 'l':
1517: mask = S_IFLNK;
1518: break;
1519: case 'p':
1520: mask = S_IFIFO;
1521: break;
1522: case 's':
1523: mask = S_IFSOCK;
1524: break;
1525: default:
1526: errx(1, "-type: %s: unknown type", typestring);
1527: }
1528:
1529: new = palloc(N_TYPE, f_type);
1530: new->m_data = mask;
1531: return (new);
1532: }
1533:
1534: /*
1535: * -user uname functions --
1536: *
1537: * True if the file belongs to the user uname. If uname is numeric and
1538: * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1539: * return a valid user name, uname is taken as a user ID.
1540: */
1541: int
1.30 deraadt 1542: f_user(PLAN *plan, FTSENT *entry)
1.1 deraadt 1543: {
1544: return (entry->fts_statp->st_uid == plan->u_data);
1545: }
1546:
1547: PLAN *
1.28 deraadt 1548: c_user(char *username, char ***ignored, int unused)
1.1 deraadt 1549: {
1550: PLAN *new;
1551: uid_t uid;
1552:
1553: ftsoptions &= ~FTS_NOSTAT;
1554:
1.46 millert 1555: if (uid_from_user(username, &uid) == -1) {
1.44 deraadt 1556: const char *errstr;
1557:
1558: uid = strtonum(username, 0, UID_MAX, &errstr);
1559: if (errstr)
1.1 deraadt 1560: errx(1, "-user: %s: no such user", username);
1.46 millert 1561: }
1.1 deraadt 1562:
1563: new = palloc(N_USER, f_user);
1564: new->u_data = uid;
1565: return (new);
1566: }
1567:
1568: /*
1569: * -xdev functions --
1570: *
1571: * Always true, causes find not to decend past directories that have a
1572: * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1573: */
1574: PLAN *
1.28 deraadt 1575: c_xdev(char *ignore, char ***ignored, int unused)
1.1 deraadt 1576: {
1577: ftsoptions |= FTS_XDEV;
1578:
1579: return (palloc(N_XDEV, f_always_true));
1580: }
1581:
1582: /*
1583: * ( expression ) functions --
1584: *
1585: * True if expression is true.
1586: */
1587: int
1.30 deraadt 1588: f_expr(PLAN *plan, FTSENT *entry)
1.1 deraadt 1589: {
1.21 mpech 1590: PLAN *p;
1591: int state;
1.1 deraadt 1592:
1593: for (p = plan->p_data[0];
1594: p && (state = (p->eval)(p, entry)); p = p->next);
1595: return (state);
1596: }
1597:
1598: /*
1599: * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1600: * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1601: * to a N_EXPR node containing the expression and the ')' node is discarded.
1602: */
1603: PLAN *
1.28 deraadt 1604: c_openparen(char *ignore, char ***ignored, int unused)
1.1 deraadt 1605: {
1.28 deraadt 1606: return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1));
1.1 deraadt 1607: }
1608:
1609: PLAN *
1.28 deraadt 1610: c_closeparen(char *ignore, char ***ignored, int unused)
1.1 deraadt 1611: {
1.28 deraadt 1612: return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1));
1.1 deraadt 1613: }
1614:
1615: /*
1616: * ! expression functions --
1617: *
1618: * Negation of a primary; the unary NOT operator.
1619: */
1620: int
1.30 deraadt 1621: f_not(PLAN *plan, FTSENT *entry)
1.1 deraadt 1622: {
1.21 mpech 1623: PLAN *p;
1624: int state;
1.1 deraadt 1625:
1626: for (p = plan->p_data[0];
1627: p && (state = (p->eval)(p, entry)); p = p->next);
1628: return (!state);
1629: }
1630:
1631: PLAN *
1.28 deraadt 1632: c_not(char *ignore, char ***ignored, int unused)
1.1 deraadt 1633: {
1634: return (palloc(N_NOT, f_not));
1635: }
1636:
1637: /*
1638: * expression -o expression functions --
1639: *
1640: * Alternation of primaries; the OR operator. The second expression is
1641: * not evaluated if the first expression is true.
1642: */
1643: int
1.30 deraadt 1644: f_or(PLAN *plan, FTSENT *entry)
1.1 deraadt 1645: {
1.21 mpech 1646: PLAN *p;
1647: int state;
1.1 deraadt 1648:
1649: for (p = plan->p_data[0];
1650: p && (state = (p->eval)(p, entry)); p = p->next);
1651:
1652: if (state)
1653: return (1);
1654:
1655: for (p = plan->p_data[1];
1656: p && (state = (p->eval)(p, entry)); p = p->next);
1657: return (state);
1658: }
1659:
1660: PLAN *
1.28 deraadt 1661: c_or(char *ignore, char ***ignored, int unused)
1.1 deraadt 1662: {
1663: return (palloc(N_OR, f_or));
1664: }
1.37 pascal 1665:
1666:
1667: /*
1668: * plan_cleanup --
1669: * Check and see if the specified plan has any residual state,
1670: * and if so, clean it up as appropriate.
1671: *
1672: * At the moment, only N_EXEC has state. Two kinds: 1)
1673: * lists of files to feed to subprocesses 2) State on exit
1674: * statusses of past subprocesses.
1675: */
1676: /* ARGSUSED1 */
1677: int
1678: plan_cleanup(PLAN *plan, void *arg)
1679: {
1680: if (plan->type==N_EXEC && plan->ep_narg)
1681: run_f_exec(plan);
1682:
1683: return plan->ep_rval; /* Passed save exit-status up chain */
1684: }
1685:
1.1 deraadt 1686:
1687: static PLAN *
1.28 deraadt 1688: palloc(enum ntype t, int (*f)(PLAN *, FTSENT *))
1.1 deraadt 1689: {
1690: PLAN *new;
1691:
1.17 millert 1692: if ((new = calloc(1, sizeof(PLAN)))) {
1.1 deraadt 1693: new->type = t;
1694: new->eval = f;
1695: return (new);
1696: }
1697: err(1, NULL);
1698: /* NOTREACHED */
1699: }