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