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