Annotation of src/usr.bin/find/function.c, Revision 1.36
1.36 ! millert 1: /* $OpenBSD: function.c,v 1.35 2009/12/09 13:59:43 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.1 deraadt 49: #include <pwd.h>
50: #include <stdio.h>
51: #include <stdlib.h>
52: #include <string.h>
53: #include <tzfile.h>
54: #include <unistd.h>
55:
56: #include "find.h"
1.28 deraadt 57: #include "extern.h"
1.1 deraadt 58:
59: #define COMPARE(a, b) { \
60: switch (plan->flags) { \
61: case F_EQUAL: \
62: return (a == b); \
63: case F_LESSTHAN: \
64: return (a < b); \
65: case F_GREATER: \
66: return (a > b); \
67: default: \
68: abort(); \
69: } \
70: }
71:
1.23 millert 72: static PLAN *palloc(enum ntype, int (*)(PLAN *, FTSENT *));
1.28 deraadt 73: static long find_parsenum(PLAN *plan, char *option, char *vp, char *endch);
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: */
122: static long
1.30 deraadt 123: find_parsenum(PLAN *plan, char *option, char *vp, char *endch)
1.1 deraadt 124: {
125: long value;
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: /*
145: * Convert the string with strtol(). Note, if strtol() returns zero
146: * and endchar points to the beginning of the string we know we have
147: * a syntax error.
148: */
149: value = strtol(str, &endchar, 10);
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 --
342: *
343: * True if the executed utility returns a zero value as exit status.
344: * The end of the primary expression is delimited by a semicolon. If
345: * "{}" occurs anywhere, it gets replaced by the current pathname.
346: * The current directory for the execution of utility is the same as
347: * the current directory when the find utility was started.
348: *
349: * The primary -ok is different in that it requests affirmation of the
350: * user before executing the utility.
351: */
352: int
1.30 deraadt 353: f_exec(PLAN *plan, FTSENT *entry)
1.1 deraadt 354: {
1.21 mpech 355: int cnt;
1.1 deraadt 356: pid_t pid;
357: int status;
358:
359: for (cnt = 0; plan->e_argv[cnt]; ++cnt)
360: if (plan->e_len[cnt])
361: brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
362: entry->fts_path, plan->e_len[cnt]);
363:
364: if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
365: return (0);
366:
367: /* don't mix output of command with find output */
368: fflush(stdout);
369: fflush(stderr);
370:
371: switch (pid = vfork()) {
372: case -1:
373: err(1, "fork");
374: /* NOTREACHED */
375: case 0:
376: if (fchdir(dotfd)) {
377: warn("chdir");
378: _exit(1);
379: }
380: execvp(plan->e_argv[0], plan->e_argv);
381: warn("%s", plan->e_argv[0]);
382: _exit(1);
383: }
384: pid = waitpid(pid, &status, 0);
385: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
386: }
387:
388: /*
389: * c_exec --
390: * build three parallel arrays, one with pointers to the strings passed
391: * on the command line, one with (possibly duplicated) pointers to the
392: * argv array, and one with integer values that are lengths of the
393: * strings, but also flags meaning that the string has to be massaged.
394: */
395: PLAN *
1.28 deraadt 396: c_exec(char *unused, char ***argvp, int isok)
1.1 deraadt 397: {
398: PLAN *new; /* node returned */
1.21 mpech 399: int cnt;
400: char **argv, **ap, *p;
1.35 millert 401:
402: /* make sure the current directory is readable */
403: if (dotfd == -1)
404: errx(1, "%s: cannot open \".\"", isok ? "-ok" : "-exec");
1.1 deraadt 405:
406: isoutput = 1;
407:
408: new = palloc(N_EXEC, f_exec);
409: if (isok)
410: new->flags = F_NEEDOK;
411:
412: for (ap = argv = *argvp;; ++ap) {
413: if (!*ap)
414: errx(1,
415: "%s: no terminating \";\"", isok ? "-ok" : "-exec");
1.8 millert 416: if (**ap == ';')
417: break;
418: }
419:
420: cnt = ap - *argvp + 1;
421: new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
422: new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
423: new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
424:
425: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
426: new->e_orig[cnt] = *argv;
427: for (p = *argv; *p; ++p)
428: if (p[0] == '{' && p[1] == '}') {
429: new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
430: new->e_len[cnt] = MAXPATHLEN;
431: break;
432: }
433: if (!*p) {
434: new->e_argv[cnt] = *argv;
435: new->e_len[cnt] = 0;
436: }
437: }
438: new->e_argv[cnt] = new->e_orig[cnt] = NULL;
439:
440: *argvp = argv + 1;
441: return (new);
442: }
443:
444: /*
445: * -execdir utility [arg ... ] ; functions --
446: *
447: * True if the executed utility returns a zero value as exit status.
448: * The end of the primary expression is delimited by a semicolon. If
449: * "{}" occurs anywhere, it gets replaced by the unqualified pathname.
450: * The current directory for the execution of utility is the same as
451: * the directory where the file lives.
452: */
453: int
1.30 deraadt 454: f_execdir(PLAN *plan, FTSENT *entry)
1.8 millert 455: {
1.21 mpech 456: int cnt;
1.8 millert 457: pid_t pid;
1.15 millert 458: int status, fd;
1.10 millert 459: char base[MAXPATHLEN];
1.8 millert 460:
1.15 millert 461: /* fts(3) does not chdir for the root level so we do it ourselves. */
462: if (entry->fts_level == FTS_ROOTLEVEL) {
463: if ((fd = open(".", O_RDONLY)) == -1) {
464: warn("cannot open \".\"");
465: return (0);
466: }
467: if (chdir(entry->fts_accpath)) {
1.16 millert 468: (void) close(fd);
1.15 millert 469: return (0);
470: }
471: }
472:
1.10 millert 473: /* Substitute basename(path) for {} since cwd is it's parent dir */
474: (void)strncpy(base, basename(entry->fts_path), sizeof(base) - 1);
475: base[sizeof(base) - 1] = '\0';
1.8 millert 476: for (cnt = 0; plan->e_argv[cnt]; ++cnt)
477: if (plan->e_len[cnt])
478: brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
1.10 millert 479: base, plan->e_len[cnt]);
1.8 millert 480:
481: /* don't mix output of command with find output */
482: fflush(stdout);
483: fflush(stderr);
484:
485: switch (pid = vfork()) {
486: case -1:
487: err(1, "fork");
488: /* NOTREACHED */
489: case 0:
490: execvp(plan->e_argv[0], plan->e_argv);
491: warn("%s", plan->e_argv[0]);
492: _exit(1);
493: }
1.15 millert 494:
495: /* Undo the above... */
496: if (entry->fts_level == FTS_ROOTLEVEL) {
497: if (fchdir(fd) == -1) {
498: warn("unable to chdir back to starting directory");
1.16 millert 499: (void) close(fd);
1.15 millert 500: return (0);
501: }
1.16 millert 502: (void) close(fd);
1.15 millert 503: }
504:
1.8 millert 505: pid = waitpid(pid, &status, 0);
506: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
507: }
508:
509: /*
510: * c_execdir --
511: * build three parallel arrays, one with pointers to the strings passed
512: * on the command line, one with (possibly duplicated) pointers to the
513: * argv array, and one with integer values that are lengths of the
514: * strings, but also flags meaning that the string has to be massaged.
515: */
516: PLAN *
1.28 deraadt 517: c_execdir(char *ignored, char ***argvp, int unused)
1.8 millert 518: {
519: PLAN *new; /* node returned */
1.21 mpech 520: int cnt;
521: char **argv, **ap, *p;
1.8 millert 522:
523: ftsoptions &= ~FTS_NOSTAT;
524: isoutput = 1;
525:
526: new = palloc(N_EXECDIR, f_execdir);
527:
528: for (ap = argv = *argvp;; ++ap) {
529: if (!*ap)
530: errx(1,
531: "-execdir: no terminating \";\"");
1.1 deraadt 532: if (**ap == ';')
533: break;
534: }
535:
536: cnt = ap - *argvp + 1;
537: new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
538: new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
539: new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
540:
541: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
542: new->e_orig[cnt] = *argv;
543: for (p = *argv; *p; ++p)
544: if (p[0] == '{' && p[1] == '}') {
545: new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
546: new->e_len[cnt] = MAXPATHLEN;
547: break;
548: }
549: if (!*p) {
550: new->e_argv[cnt] = *argv;
551: new->e_len[cnt] = 0;
552: }
553: }
554: new->e_argv[cnt] = new->e_orig[cnt] = NULL;
555:
556: *argvp = argv + 1;
557: return (new);
558: }
1.19 millert 559:
560: /*
561: * -flags functions --
562: *
563: * The flags argument is used to represent file flags bits.
564: */
565: int
1.30 deraadt 566: f_flags(PLAN *plan, FTSENT *entry)
1.19 millert 567: {
568: u_int flags;
569:
570: flags = entry->fts_statp->st_flags &
571: (UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE |
572: SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND);
573: if (plan->flags == F_ATLEAST)
574: /* note that plan->fl_flags always is a subset of
575: plan->fl_mask */
576: return ((flags & plan->fl_mask) == plan->fl_flags);
577: else
578: return (flags == plan->fl_flags);
579: /* NOTREACHED */
580: }
581:
582: PLAN *
1.28 deraadt 583: c_flags(char *flags_str, char ***ignored, int unused)
1.19 millert 584: {
585: PLAN *new;
1.20 mickey 586: u_int32_t flags, notflags;
1.19 millert 587:
588: ftsoptions &= ~FTS_NOSTAT;
589:
590: new = palloc(N_FLAGS, f_flags);
591:
592: if (*flags_str == '-') {
593: new->flags = F_ATLEAST;
594: ++flags_str;
595: }
596:
1.20 mickey 597: if (strtofflags(&flags_str, &flags, ¬flags) == 1)
1.19 millert 598: errx(1, "-flags: %s: illegal flags string", flags_str);
599:
600: new->fl_flags = flags;
601: new->fl_mask = flags | notflags;
602: return (new);
603: }
1.1 deraadt 604:
605: /*
606: * -follow functions --
607: *
608: * Always true, causes symbolic links to be followed on a global
609: * basis.
610: */
611: PLAN *
1.28 deraadt 612: c_follow(char *ignore, char ***ignored, int unused)
1.1 deraadt 613: {
614: ftsoptions &= ~FTS_PHYSICAL;
615: ftsoptions |= FTS_LOGICAL;
616:
617: return (palloc(N_FOLLOW, f_always_true));
618: }
619:
620: /*
621: * -fstype functions --
622: *
623: * True if the file is of a certain type.
624: */
625: int
1.30 deraadt 626: f_fstype(PLAN *plan, FTSENT *entry)
1.1 deraadt 627: {
628: static dev_t curdev; /* need a guaranteed illegal dev value */
629: static int first = 1;
630: struct statfs sb;
631: static short val;
632: static char fstype[MFSNAMELEN];
633: char *p, save[2];
634:
635: /* Only check when we cross mount point. */
636: if (first || curdev != entry->fts_statp->st_dev) {
637: curdev = entry->fts_statp->st_dev;
638:
639: /*
640: * Statfs follows symlinks; find wants the link's file system,
641: * not where it points.
642: */
643: if (entry->fts_info == FTS_SL ||
644: entry->fts_info == FTS_SLNONE) {
1.9 millert 645: if ((p = strrchr(entry->fts_accpath, '/')))
1.1 deraadt 646: ++p;
647: else
648: p = entry->fts_accpath;
649: save[0] = p[0];
650: p[0] = '.';
651: save[1] = p[1];
652: p[1] = '\0';
653:
654: } else
655: p = NULL;
656:
657: if (statfs(entry->fts_accpath, &sb))
658: err(1, "%s", entry->fts_accpath);
659:
660: if (p) {
661: p[0] = save[0];
662: p[1] = save[1];
663: }
664:
665: first = 0;
666:
667: /*
668: * Further tests may need both of these values, so
669: * always copy both of them.
670: */
671: val = sb.f_flags;
672: strncpy(fstype, sb.f_fstypename, MFSNAMELEN);
673: }
674: switch (plan->flags) {
675: case F_MTFLAG:
676: return (val & plan->mt_data);
677: case F_MTTYPE:
678: return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
679: default:
680: abort();
681: }
682: }
683:
684: PLAN *
1.28 deraadt 685: c_fstype(char *arg, char ***ignored, int unused)
1.1 deraadt 686: {
1.21 mpech 687: PLAN *new;
1.1 deraadt 688:
689: ftsoptions &= ~FTS_NOSTAT;
690:
691: new = palloc(N_FSTYPE, f_fstype);
692: switch (*arg) {
693: case 'l':
694: if (!strcmp(arg, "local")) {
695: new->flags = F_MTFLAG;
696: new->mt_data = MNT_LOCAL;
697: return (new);
698: }
699: break;
700: case 'r':
701: if (!strcmp(arg, "rdonly")) {
702: new->flags = F_MTFLAG;
703: new->mt_data = MNT_RDONLY;
704: return (new);
705: }
706: break;
707: }
708:
709: new->flags = F_MTTYPE;
710: new->c_data = arg;
711: return (new);
712: }
713:
714: /*
715: * -group gname functions --
716: *
717: * True if the file belongs to the group gname. If gname is numeric and
718: * an equivalent of the getgrnam() function does not return a valid group
719: * name, gname is taken as a group ID.
720: */
721: int
1.30 deraadt 722: f_group(PLAN *plan, FTSENT *entry)
1.1 deraadt 723: {
724: return (entry->fts_statp->st_gid == plan->g_data);
725: }
726:
727: PLAN *
1.28 deraadt 728: c_group(char *gname, char ***ignored, int unused)
1.1 deraadt 729: {
730: PLAN *new;
731: struct group *g;
732: gid_t gid;
733:
734: ftsoptions &= ~FTS_NOSTAT;
735:
736: g = getgrnam(gname);
737: if (g == NULL) {
738: gid = atoi(gname);
739: if (gid == 0 && gname[0] != '0')
740: errx(1, "-group: %s: no such group", gname);
741: } else
742: gid = g->gr_gid;
743:
744: new = palloc(N_GROUP, f_group);
745: new->g_data = gid;
746: return (new);
747: }
748:
749: /*
750: * -inum n functions --
751: *
752: * True if the file has inode # n.
753: */
754: int
1.30 deraadt 755: f_inum(PLAN *plan, FTSENT *entry)
1.1 deraadt 756: {
757: COMPARE(entry->fts_statp->st_ino, plan->i_data);
758: }
759:
760: PLAN *
1.28 deraadt 761: c_inum(char *arg, char ***ignored, int unused)
1.1 deraadt 762: {
763: PLAN *new;
764:
765: ftsoptions &= ~FTS_NOSTAT;
766:
767: new = palloc(N_INUM, f_inum);
768: new->i_data = find_parsenum(new, "-inum", arg, NULL);
769: return (new);
770: }
771:
772: /*
773: * -links n functions --
774: *
775: * True if the file has n links.
776: */
777: int
1.30 deraadt 778: f_links(PLAN *plan, FTSENT *entry)
1.1 deraadt 779: {
780: COMPARE(entry->fts_statp->st_nlink, plan->l_data);
781: }
782:
783: PLAN *
1.28 deraadt 784: c_links(char *arg, char ***ignored, int unused)
1.1 deraadt 785: {
786: PLAN *new;
787:
788: ftsoptions &= ~FTS_NOSTAT;
789:
790: new = palloc(N_LINKS, f_links);
791: new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
792: return (new);
793: }
794:
795: /*
796: * -ls functions --
797: *
798: * Always true - prints the current entry to stdout in "ls" format.
799: */
800: int
1.30 deraadt 801: f_ls(PLAN *plan, FTSENT *entry)
1.1 deraadt 802: {
803: printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
804: return (1);
805: }
806:
807: PLAN *
1.28 deraadt 808: c_ls(char *ignore, char ***ignored, int unused)
1.1 deraadt 809: {
810: ftsoptions &= ~FTS_NOSTAT;
811: isoutput = 1;
812:
813: return (palloc(N_LS, f_ls));
1.5 tholo 814: }
815:
816: /*
817: * - maxdepth n functions --
818: *
819: * True if the current search depth is less than or equal to the
820: * maximum depth specified
821: */
822: int
1.30 deraadt 823: f_maxdepth(PLAN *plan, FTSENT *entry)
1.5 tholo 824: {
825:
1.6 tholo 826: if (entry->fts_level >= plan->max_data)
1.5 tholo 827: fts_set(tree, entry, FTS_SKIP);
1.6 tholo 828: return (entry->fts_level <= plan->max_data);
1.5 tholo 829: }
830:
831: PLAN *
1.28 deraadt 832: c_maxdepth(char *arg, char ***ignored, int unused)
1.5 tholo 833: {
834: PLAN *new;
1.32 millert 835: const char *errstr = NULL;
1.5 tholo 836:
1.6 tholo 837: new = palloc(N_MAXDEPTH, f_maxdepth);
1.33 millert 838: new->max_data = strtonum(arg, 0, FTS_MAXLEVEL, &errstr);
1.32 millert 839: if (errstr)
840: errx(1, "%s: maxdepth value %s", arg, errstr);
1.6 tholo 841: return (new);
842: }
843:
844: /*
845: * - mindepth n functions --
846: *
847: * True if the current search depth is greater than or equal to the
848: * minimum depth specified
849: */
850: int
1.30 deraadt 851: f_mindepth(PLAN *plan, FTSENT *entry)
1.6 tholo 852: {
853:
854: return (entry->fts_level >= plan->min_data);
855: }
856:
857: PLAN *
1.28 deraadt 858: c_mindepth(char *arg, char ***ignored, int unused)
1.6 tholo 859: {
860: PLAN *new;
861:
862: new = palloc(N_MINDEPTH, f_mindepth);
863: new->min_data = atoi(arg);
1.5 tholo 864: return (new);
1.1 deraadt 865: }
866:
867: /*
868: * -mtime n functions --
869: *
870: * True if the difference between the file modification time and the
871: * current time is n 24 hour periods.
872: */
873: int
1.30 deraadt 874: f_mtime(PLAN *plan, FTSENT *entry)
1.1 deraadt 875: {
876:
877: COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
1.17 millert 878: SECSPERDAY, plan->sec_data);
1.1 deraadt 879: }
880:
881: PLAN *
1.28 deraadt 882: c_mtime(char *arg, char ***ignored, int unused)
1.1 deraadt 883: {
884: PLAN *new;
885:
886: ftsoptions &= ~FTS_NOSTAT;
887:
888: new = palloc(N_MTIME, f_mtime);
1.17 millert 889: new->sec_data = find_parsenum(new, "-mtime", arg, NULL);
1.1 deraadt 890: TIME_CORRECT(new, N_MTIME);
1.11 deraadt 891: return (new);
892: }
893:
894: /*
895: * -mmin n functions --
896: *
897: * True if the difference between the file modification time and the
898: * current time is n min periods.
899: */
900: int
1.30 deraadt 901: f_mmin(PLAN *plan, FTSENT *entry)
1.11 deraadt 902: {
903: extern time_t now;
904:
905: COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /
1.17 millert 906: 60, plan->sec_data);
1.11 deraadt 907: }
908:
909: PLAN *
1.28 deraadt 910: c_mmin(char *arg, char ***ignored, int unused)
1.11 deraadt 911: {
912: PLAN *new;
913:
914: ftsoptions &= ~FTS_NOSTAT;
915:
916: new = palloc(N_MMIN, f_mmin);
1.17 millert 917: new->sec_data = find_parsenum(new, "-mmin", arg, NULL);
1.11 deraadt 918: TIME_CORRECT(new, N_MMIN);
1.1 deraadt 919: return (new);
920: }
921:
922: /*
923: * -name functions --
924: *
925: * True if the basename of the filename being examined
926: * matches pattern using Pattern Matching Notation S3.14
927: */
928: int
1.30 deraadt 929: f_name(PLAN *plan, FTSENT *entry)
1.1 deraadt 930: {
931: return (!fnmatch(plan->c_data, entry->fts_name, 0));
932: }
933:
934: PLAN *
1.28 deraadt 935: c_name(char *pattern, char ***ignored, int unused)
1.1 deraadt 936: {
937: PLAN *new;
938:
939: new = palloc(N_NAME, f_name);
1.18 deraadt 940: new->c_data = pattern;
941: return (new);
942: }
943:
944: /*
945: * -iname functions --
946: *
947: * Similar to -name, but does case insensitive matching
948: *
949: */
950: int
1.30 deraadt 951: f_iname(PLAN *plan, FTSENT *entry)
1.18 deraadt 952: {
953: return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD));
954: }
955:
956: PLAN *
1.28 deraadt 957: c_iname(char *pattern, char ***ignored, int unused)
1.18 deraadt 958: {
959: PLAN *new;
960:
961: new = palloc(N_INAME, f_iname);
1.1 deraadt 962: new->c_data = pattern;
963: return (new);
964: }
965:
966: /*
967: * -newer file functions --
968: *
969: * True if the current file has been modified more recently
970: * then the modification time of the file named by the pathname
971: * file.
972: */
973: int
1.30 deraadt 974: f_newer(PLAN *plan, FTSENT *entry)
1.1 deraadt 975: {
1.17 millert 976:
977: return (entry->fts_statp->st_mtimespec.tv_sec > plan->t_data.tv_sec ||
978: (entry->fts_statp->st_mtimespec.tv_sec == plan->t_data.tv_sec &&
979: entry->fts_statp->st_mtimespec.tv_nsec > plan->t_data.tv_nsec));
1.1 deraadt 980: }
981:
982: PLAN *
1.28 deraadt 983: c_newer(char *filename, char ***ignored, int unused)
1.1 deraadt 984: {
985: PLAN *new;
986: struct stat sb;
987:
988: ftsoptions &= ~FTS_NOSTAT;
989:
990: if (stat(filename, &sb))
991: err(1, "%s", filename);
992: new = palloc(N_NEWER, f_newer);
1.17 millert 993: memcpy(&new->t_data, &sb.st_mtimespec, sizeof(struct timespec));
994: return (new);
995: }
996:
997: /*
998: * -anewer file functions --
999: *
1000: * True if the current file has been accessed more recently
1001: * then the access time of the file named by the pathname
1002: * file.
1003: */
1004: int
1.30 deraadt 1005: f_anewer(PLAN *plan, FTSENT *entry)
1.17 millert 1006: {
1007:
1008: return (entry->fts_statp->st_atimespec.tv_sec > plan->t_data.tv_sec ||
1009: (entry->fts_statp->st_atimespec.tv_sec == plan->t_data.tv_sec &&
1010: entry->fts_statp->st_atimespec.tv_nsec > plan->t_data.tv_nsec));
1011: }
1012:
1013: PLAN *
1.28 deraadt 1014: c_anewer(char *filename, char ***ignored, int unused)
1.17 millert 1015: {
1016: PLAN *new;
1017: struct stat sb;
1018:
1019: ftsoptions &= ~FTS_NOSTAT;
1020:
1021: if (stat(filename, &sb))
1022: err(1, "%s", filename);
1.24 millert 1023: new = palloc(N_NEWER, f_anewer);
1.17 millert 1024: memcpy(&new->t_data, &sb.st_atimespec, sizeof(struct timespec));
1025: return (new);
1026: }
1027:
1028: /*
1029: * -cnewer file functions --
1030: *
1031: * True if the current file has been changed more recently
1032: * then the inode change time of the file named by the pathname
1033: * file.
1034: */
1035: int
1.30 deraadt 1036: f_cnewer(PLAN *plan, FTSENT *entry)
1.17 millert 1037: {
1038:
1039: return (entry->fts_statp->st_ctimespec.tv_sec > plan->t_data.tv_sec ||
1040: (entry->fts_statp->st_ctimespec.tv_sec == plan->t_data.tv_sec &&
1041: entry->fts_statp->st_ctimespec.tv_nsec > plan->t_data.tv_nsec));
1042: }
1043:
1044: PLAN *
1.28 deraadt 1045: c_cnewer(char *filename, char ***ignored, int unused)
1.17 millert 1046: {
1047: PLAN *new;
1048: struct stat sb;
1049:
1050: ftsoptions &= ~FTS_NOSTAT;
1051:
1052: if (stat(filename, &sb))
1053: err(1, "%s", filename);
1.24 millert 1054: new = palloc(N_NEWER, f_cnewer);
1.17 millert 1055: memcpy(&new->t_data, &sb.st_ctimespec, sizeof(struct timespec));
1.1 deraadt 1056: return (new);
1057: }
1058:
1059: /*
1060: * -nogroup functions --
1061: *
1062: * True if file belongs to a user ID for which the equivalent
1063: * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
1064: */
1065: int
1.30 deraadt 1066: f_nogroup(PLAN *plan, FTSENT *entry)
1.1 deraadt 1067: {
1068: return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
1069: }
1070:
1071: PLAN *
1.28 deraadt 1072: c_nogroup(char *ignore, char ***ignored, int unused)
1.1 deraadt 1073: {
1074: ftsoptions &= ~FTS_NOSTAT;
1075:
1076: return (palloc(N_NOGROUP, f_nogroup));
1077: }
1078:
1079: /*
1080: * -nouser functions --
1081: *
1082: * True if file belongs to a user ID for which the equivalent
1083: * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
1084: */
1085: int
1.30 deraadt 1086: f_nouser(PLAN *plan, FTSENT *entry)
1.1 deraadt 1087: {
1088: return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
1089: }
1090:
1091: PLAN *
1.28 deraadt 1092: c_nouser(char *ignore, char ***ignored, int unused)
1.1 deraadt 1093: {
1094: ftsoptions &= ~FTS_NOSTAT;
1095:
1096: return (palloc(N_NOUSER, f_nouser));
1097: }
1098:
1099: /*
1100: * -path functions --
1101: *
1102: * True if the path of the filename being examined
1103: * matches pattern using Pattern Matching Notation S3.14
1104: */
1105: int
1.30 deraadt 1106: f_path(PLAN *plan, FTSENT *entry)
1.1 deraadt 1107: {
1108: return (!fnmatch(plan->c_data, entry->fts_path, 0));
1109: }
1110:
1111: PLAN *
1.28 deraadt 1112: c_path(char *pattern, char ***ignored, int unused)
1.1 deraadt 1113: {
1114: PLAN *new;
1115:
1116: new = palloc(N_NAME, f_path);
1117: new->c_data = pattern;
1118: return (new);
1119: }
1120:
1121: /*
1122: * -perm functions --
1123: *
1124: * The mode argument is used to represent file mode bits. If it starts
1125: * with a leading digit, it's treated as an octal mode, otherwise as a
1126: * symbolic mode.
1127: */
1128: int
1.30 deraadt 1129: f_perm(PLAN *plan, FTSENT *entry)
1.1 deraadt 1130: {
1131: mode_t mode;
1132:
1133: mode = entry->fts_statp->st_mode &
1134: (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1135: if (plan->flags == F_ATLEAST)
1136: return ((plan->m_data | mode) == mode);
1137: else
1138: return (mode == plan->m_data);
1139: /* NOTREACHED */
1140: }
1141:
1142: PLAN *
1.28 deraadt 1143: c_perm(char *perm, char ***ignored, int unused)
1.1 deraadt 1144: {
1145: PLAN *new;
1.29 otto 1146: void *set;
1.1 deraadt 1147:
1148: ftsoptions &= ~FTS_NOSTAT;
1149:
1150: new = palloc(N_PERM, f_perm);
1151:
1152: if (*perm == '-') {
1153: new->flags = F_ATLEAST;
1154: ++perm;
1155: }
1156:
1157: if ((set = setmode(perm)) == NULL)
1.19 millert 1158: errx(1, "-perm: %s: illegal mode string", perm);
1.1 deraadt 1159:
1160: new->m_data = getmode(set, 0);
1.12 deraadt 1161: free(set);
1.1 deraadt 1162: return (new);
1163: }
1164:
1165: /*
1166: * -print functions --
1167: *
1168: * Always true, causes the current pathame to be written to
1169: * standard output.
1170: */
1171: int
1.30 deraadt 1172: f_print(PLAN *plan, FTSENT *entry)
1.1 deraadt 1173: {
1174: (void)printf("%s\n", entry->fts_path);
1175: return(1);
1176: }
1177:
1178: /* ARGSUSED */
1.9 millert 1179: int
1.30 deraadt 1180: f_print0(PLAN *plan, FTSENT *entry)
1.1 deraadt 1181: {
1182: (void)fputs(entry->fts_path, stdout);
1183: (void)fputc('\0', stdout);
1184: return(1);
1185: }
1186:
1187: PLAN *
1.28 deraadt 1188: c_print(char *ignore, char ***ignored, int unused)
1.1 deraadt 1189: {
1190: isoutput = 1;
1191:
1192: return(palloc(N_PRINT, f_print));
1193: }
1194:
1195: PLAN *
1.28 deraadt 1196: c_print0(char *ignore, char ***ignored, int unused)
1.1 deraadt 1197: {
1198: isoutput = 1;
1199:
1200: return(palloc(N_PRINT0, f_print0));
1201: }
1202:
1203: /*
1204: * -prune functions --
1205: *
1206: * Prune a portion of the hierarchy.
1207: */
1208: int
1.30 deraadt 1209: f_prune(PLAN *plan, FTSENT *entry)
1.1 deraadt 1210: {
1211:
1212: if (fts_set(tree, entry, FTS_SKIP))
1213: err(1, "%s", entry->fts_path);
1214: return (1);
1215: }
1216:
1217: PLAN *
1.28 deraadt 1218: c_prune(char *ignore, char ***ignored, int unused)
1.1 deraadt 1219: {
1220: return (palloc(N_PRUNE, f_prune));
1221: }
1222:
1223: /*
1224: * -size n[c] functions --
1225: *
1226: * True if the file size in bytes, divided by an implementation defined
1227: * value and rounded up to the next integer, is n. If n is followed by
1228: * a c, the size is in bytes.
1229: */
1230: #define FIND_SIZE 512
1231: static int divsize = 1;
1232:
1233: int
1.30 deraadt 1234: f_size(PLAN *plan, FTSENT *entry)
1.1 deraadt 1235: {
1236: off_t size;
1237:
1238: size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1239: FIND_SIZE : entry->fts_statp->st_size;
1240: COMPARE(size, plan->o_data);
1241: }
1242:
1243: PLAN *
1.28 deraadt 1244: c_size(char *arg, char ***ignored, int unused)
1.1 deraadt 1245: {
1246: PLAN *new;
1247: char endch;
1248:
1249: ftsoptions &= ~FTS_NOSTAT;
1250:
1251: new = palloc(N_SIZE, f_size);
1252: endch = 'c';
1253: new->o_data = find_parsenum(new, "-size", arg, &endch);
1254: if (endch == 'c')
1255: divsize = 0;
1256: return (new);
1257: }
1258:
1259: /*
1260: * -type c functions --
1261: *
1262: * True if the type of the file is c, where c is b, c, d, p, or f for
1263: * block special file, character special file, directory, FIFO, or
1264: * regular file, respectively.
1265: */
1266: int
1.30 deraadt 1267: f_type(PLAN *plan, FTSENT *entry)
1.1 deraadt 1268: {
1269: return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1270: }
1271:
1272: PLAN *
1.28 deraadt 1273: c_type(char *typestring, char ***ignored, int unused)
1.1 deraadt 1274: {
1275: PLAN *new;
1276: mode_t mask;
1277:
1278: ftsoptions &= ~FTS_NOSTAT;
1279:
1280: switch (typestring[0]) {
1281: case 'b':
1282: mask = S_IFBLK;
1283: break;
1284: case 'c':
1285: mask = S_IFCHR;
1286: break;
1287: case 'd':
1288: mask = S_IFDIR;
1289: break;
1290: case 'f':
1291: mask = S_IFREG;
1292: break;
1293: case 'l':
1294: mask = S_IFLNK;
1295: break;
1296: case 'p':
1297: mask = S_IFIFO;
1298: break;
1299: case 's':
1300: mask = S_IFSOCK;
1301: break;
1302: default:
1303: errx(1, "-type: %s: unknown type", typestring);
1304: }
1305:
1306: new = palloc(N_TYPE, f_type);
1307: new->m_data = mask;
1308: return (new);
1309: }
1310:
1311: /*
1312: * -user uname functions --
1313: *
1314: * True if the file belongs to the user uname. If uname is numeric and
1315: * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1316: * return a valid user name, uname is taken as a user ID.
1317: */
1318: int
1.30 deraadt 1319: f_user(PLAN *plan, FTSENT *entry)
1.1 deraadt 1320: {
1321: return (entry->fts_statp->st_uid == plan->u_data);
1322: }
1323:
1324: PLAN *
1.28 deraadt 1325: c_user(char *username, char ***ignored, int unused)
1.1 deraadt 1326: {
1327: PLAN *new;
1328: struct passwd *p;
1329: uid_t uid;
1330:
1331: ftsoptions &= ~FTS_NOSTAT;
1332:
1333: p = getpwnam(username);
1334: if (p == NULL) {
1335: uid = atoi(username);
1336: if (uid == 0 && username[0] != '0')
1337: errx(1, "-user: %s: no such user", username);
1338: } else
1339: uid = p->pw_uid;
1340:
1341: new = palloc(N_USER, f_user);
1342: new->u_data = uid;
1343: return (new);
1344: }
1345:
1346: /*
1347: * -xdev functions --
1348: *
1349: * Always true, causes find not to decend past directories that have a
1350: * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1351: */
1352: PLAN *
1.28 deraadt 1353: c_xdev(char *ignore, char ***ignored, int unused)
1.1 deraadt 1354: {
1355: ftsoptions |= FTS_XDEV;
1356:
1357: return (palloc(N_XDEV, f_always_true));
1358: }
1359:
1360: /*
1361: * ( expression ) functions --
1362: *
1363: * True if expression is true.
1364: */
1365: int
1.30 deraadt 1366: f_expr(PLAN *plan, FTSENT *entry)
1.1 deraadt 1367: {
1.21 mpech 1368: PLAN *p;
1369: int state;
1.1 deraadt 1370:
1371: for (p = plan->p_data[0];
1372: p && (state = (p->eval)(p, entry)); p = p->next);
1373: return (state);
1374: }
1375:
1376: /*
1377: * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1378: * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1379: * to a N_EXPR node containing the expression and the ')' node is discarded.
1380: */
1381: PLAN *
1.28 deraadt 1382: c_openparen(char *ignore, char ***ignored, int unused)
1.1 deraadt 1383: {
1.28 deraadt 1384: return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1));
1.1 deraadt 1385: }
1386:
1387: PLAN *
1.28 deraadt 1388: c_closeparen(char *ignore, char ***ignored, int unused)
1.1 deraadt 1389: {
1.28 deraadt 1390: return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1));
1.1 deraadt 1391: }
1392:
1393: /*
1394: * ! expression functions --
1395: *
1396: * Negation of a primary; the unary NOT operator.
1397: */
1398: int
1.30 deraadt 1399: f_not(PLAN *plan, FTSENT *entry)
1.1 deraadt 1400: {
1.21 mpech 1401: PLAN *p;
1402: int state;
1.1 deraadt 1403:
1404: for (p = plan->p_data[0];
1405: p && (state = (p->eval)(p, entry)); p = p->next);
1406: return (!state);
1407: }
1408:
1409: PLAN *
1.28 deraadt 1410: c_not(char *ignore, char ***ignored, int unused)
1.1 deraadt 1411: {
1412: return (palloc(N_NOT, f_not));
1413: }
1414:
1415: /*
1416: * expression -o expression functions --
1417: *
1418: * Alternation of primaries; the OR operator. The second expression is
1419: * not evaluated if the first expression is true.
1420: */
1421: int
1.30 deraadt 1422: f_or(PLAN *plan, FTSENT *entry)
1.1 deraadt 1423: {
1.21 mpech 1424: PLAN *p;
1425: int state;
1.1 deraadt 1426:
1427: for (p = plan->p_data[0];
1428: p && (state = (p->eval)(p, entry)); p = p->next);
1429:
1430: if (state)
1431: return (1);
1432:
1433: for (p = plan->p_data[1];
1434: p && (state = (p->eval)(p, entry)); p = p->next);
1435: return (state);
1436: }
1437:
1438: PLAN *
1.28 deraadt 1439: c_or(char *ignore, char ***ignored, int unused)
1.1 deraadt 1440: {
1441: return (palloc(N_OR, f_or));
1442: }
1443:
1444: static PLAN *
1.28 deraadt 1445: palloc(enum ntype t, int (*f)(PLAN *, FTSENT *))
1.1 deraadt 1446: {
1447: PLAN *new;
1448:
1.17 millert 1449: if ((new = calloc(1, sizeof(PLAN)))) {
1.1 deraadt 1450: new->type = t;
1451: new->eval = f;
1452: return (new);
1453: }
1454: err(1, NULL);
1455: /* NOTREACHED */
1456: }