function.c revision 1.54 1 /* $NetBSD: function.c,v 1.54 2006/05/10 21:53:20 mrg Exp $ */
2
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.
18 * 3. Neither the name of the University nor the names of its contributors
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/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "from: @(#)function.c 8.10 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: function.c,v 1.54 2006/05/10 21:53:20 mrg Exp $");
41 #endif
42 #endif /* not lint */
43
44 #include <sys/param.h>
45 #include <sys/stat.h>
46 #include <sys/wait.h>
47 #include <sys/mount.h>
48
49 #include <dirent.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <fnmatch.h>
53 #include <fts.h>
54 #include <grp.h>
55 #include <inttypes.h>
56 #include <pwd.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <tzfile.h>
61 #include <unistd.h>
62
63 #include "find.h"
64 #include "stat_flags.h"
65
66 #define COMPARE(a, b) { \
67 switch (plan->flags) { \
68 case F_EQUAL: \
69 return (a == b); \
70 case F_LESSTHAN: \
71 return (a < b); \
72 case F_GREATER: \
73 return (a > b); \
74 default: \
75 abort(); \
76 } \
77 }
78
79 static int64_t find_parsenum __P((PLAN *, char *, char *, char *));
80 int f_always_true __P((PLAN *, FTSENT *));
81 int f_amin __P((PLAN *, FTSENT *));
82 int f_anewer __P((PLAN *, FTSENT *));
83 int f_atime __P((PLAN *, FTSENT *));
84 int f_cmin __P((PLAN *, FTSENT *));
85 int f_cnewer __P((PLAN *, FTSENT *));
86 int f_ctime __P((PLAN *, FTSENT *));
87 int f_empty __P((PLAN *, FTSENT *));
88 int f_exec __P((PLAN *, FTSENT *));
89 int f_execdir __P((PLAN *, FTSENT *));
90 int f_false __P((PLAN *, FTSENT *));
91 int f_flags __P((PLAN *, FTSENT *));
92 int f_fprint __P((PLAN *, FTSENT *));
93 int f_fstype __P((PLAN *, FTSENT *));
94 int f_group __P((PLAN *, FTSENT *));
95 int f_iname __P((PLAN *, FTSENT *));
96 int f_inum __P((PLAN *, FTSENT *));
97 int f_links __P((PLAN *, FTSENT *));
98 int f_ls __P((PLAN *, FTSENT *));
99 int f_mindepth __P((PLAN *, FTSENT *));
100 int f_maxdepth __P((PLAN *, FTSENT *));
101 int f_mmin __P((PLAN *, FTSENT *));
102 int f_mtime __P((PLAN *, FTSENT *));
103 int f_name __P((PLAN *, FTSENT *));
104 int f_newer __P((PLAN *, FTSENT *));
105 int f_nogroup __P((PLAN *, FTSENT *));
106 int f_nouser __P((PLAN *, FTSENT *));
107 int f_path __P((PLAN *, FTSENT *));
108 int f_perm __P((PLAN *, FTSENT *));
109 int f_print __P((PLAN *, FTSENT *));
110 int f_print0 __P((PLAN *, FTSENT *));
111 int f_printx __P((PLAN *, FTSENT *));
112 int f_prune __P((PLAN *, FTSENT *));
113 int f_regex __P((PLAN *, FTSENT *));
114 int f_size __P((PLAN *, FTSENT *));
115 int f_type __P((PLAN *, FTSENT *));
116 int f_user __P((PLAN *, FTSENT *));
117 int f_not __P((PLAN *, FTSENT *));
118 int f_or __P((PLAN *, FTSENT *));
119 static PLAN *c_regex_common __P((char ***, int, enum ntype, int));
120 static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *))));
121
122 extern int dotfd;
123 extern FTS *tree;
124 extern time_t now;
125
126 /*
127 * find_parsenum --
128 * Parse a string of the form [+-]# and return the value.
129 */
130 static int64_t
131 find_parsenum(plan, option, vp, endch)
132 PLAN *plan;
133 char *option, *vp, *endch;
134 {
135 int64_t value;
136 char *endchar, *str; /* Pointer to character ending conversion. */
137
138 /* Determine comparison from leading + or -. */
139 str = vp;
140 switch (*str) {
141 case '+':
142 ++str;
143 plan->flags = F_GREATER;
144 break;
145 case '-':
146 ++str;
147 plan->flags = F_LESSTHAN;
148 break;
149 default:
150 plan->flags = F_EQUAL;
151 break;
152 }
153
154 /*
155 * Convert the string with strtol(). Note, if strtol() returns zero
156 * and endchar points to the beginning of the string we know we have
157 * a syntax error.
158 */
159 value = strtoq(str, &endchar, 10);
160 if (value == 0 && endchar == str)
161 errx(1, "%s: %s: illegal numeric value", option, vp);
162 if (endchar[0] && (endch == NULL || endchar[0] != *endch))
163 errx(1, "%s: %s: illegal trailing character", option, vp);
164 if (endch)
165 *endch = endchar[0];
166 return (value);
167 }
168
169 /*
170 * The value of n for the inode times (atime, ctime, and mtime) is a range,
171 * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with
172 * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
173 * user wanted. Correct so that -1 is "less than 1".
174 */
175 #define TIME_CORRECT(p, ttype) \
176 if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \
177 ++((p)->t_data);
178
179 /*
180 * -amin n functions --
181 *
182 * True if the difference between the file access time and the
183 * current time is n 1 minute periods.
184 */
185 int
186 f_amin(plan, entry)
187 PLAN *plan;
188 FTSENT *entry;
189 {
190 COMPARE((now - entry->fts_statp->st_atime +
191 SECSPERMIN - 1) / SECSPERMIN, plan->t_data);
192 }
193
194 PLAN *
195 c_amin(argvp, isok)
196 char ***argvp;
197 int isok;
198 {
199 char *arg = **argvp;
200 PLAN *new;
201
202 (*argvp)++;
203 ftsoptions &= ~FTS_NOSTAT;
204
205 new = palloc(N_AMIN, f_amin);
206 new->t_data = find_parsenum(new, "-amin", arg, NULL);
207 TIME_CORRECT(new, N_AMIN);
208 return (new);
209 }
210
211 /*
212 * -anewer file functions --
213 *
214 * True if the current file has been accessed more recently
215 * than the access time of the file named by the pathname
216 * file.
217 */
218 int
219 f_anewer(plan, entry)
220 PLAN *plan;
221 FTSENT *entry;
222 {
223
224 return (entry->fts_statp->st_atime > plan->t_data);
225 }
226
227 PLAN *
228 c_anewer(argvp, isok)
229 char ***argvp;
230 int isok;
231 {
232 char *filename = **argvp;
233 PLAN *new;
234 struct stat sb;
235
236 (*argvp)++;
237 ftsoptions &= ~FTS_NOSTAT;
238
239 if (stat(filename, &sb))
240 err(1, "%s", filename);
241 new = palloc(N_ANEWER, f_anewer);
242 new->t_data = sb.st_atime;
243 return (new);
244 }
245
246 /*
247 * -atime n functions --
248 *
249 * True if the difference between the file access time and the
250 * current time is n 24 hour periods.
251 */
252 int
253 f_atime(plan, entry)
254 PLAN *plan;
255 FTSENT *entry;
256 {
257 COMPARE((now - entry->fts_statp->st_atime +
258 SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
259 }
260
261 PLAN *
262 c_atime(argvp, isok)
263 char ***argvp;
264 int isok;
265 {
266 char *arg = **argvp;
267 PLAN *new;
268
269 (*argvp)++;
270 ftsoptions &= ~FTS_NOSTAT;
271
272 new = palloc(N_ATIME, f_atime);
273 new->t_data = find_parsenum(new, "-atime", arg, NULL);
274 TIME_CORRECT(new, N_ATIME);
275 return (new);
276 }
277 /*
278 * -cmin n functions --
279 *
280 * True if the difference between the last change of file
281 * status information and the current time is n 24 hour periods.
282 */
283 int
284 f_cmin(plan, entry)
285 PLAN *plan;
286 FTSENT *entry;
287 {
288 COMPARE((now - entry->fts_statp->st_ctime +
289 SECSPERMIN - 1) / SECSPERMIN, plan->t_data);
290 }
291
292 PLAN *
293 c_cmin(argvp, isok)
294 char ***argvp;
295 int isok;
296 {
297 char *arg = **argvp;
298 PLAN *new;
299
300 (*argvp)++;
301 ftsoptions &= ~FTS_NOSTAT;
302
303 new = palloc(N_CMIN, f_cmin);
304 new->t_data = find_parsenum(new, "-cmin", arg, NULL);
305 TIME_CORRECT(new, N_CMIN);
306 return (new);
307 }
308
309 /*
310 * -cnewer file functions --
311 *
312 * True if the current file has been changed more recently
313 * than the changed time of the file named by the pathname
314 * file.
315 */
316 int
317 f_cnewer(plan, entry)
318 PLAN *plan;
319 FTSENT *entry;
320 {
321
322 return (entry->fts_statp->st_ctime > plan->t_data);
323 }
324
325 PLAN *
326 c_cnewer(argvp, isok)
327 char ***argvp;
328 int isok;
329 {
330 char *filename = **argvp;
331 PLAN *new;
332 struct stat sb;
333
334 (*argvp)++;
335 ftsoptions &= ~FTS_NOSTAT;
336
337 if (stat(filename, &sb))
338 err(1, "%s", filename);
339 new = palloc(N_CNEWER, f_cnewer);
340 new->t_data = sb.st_ctime;
341 return (new);
342 }
343
344 /*
345 * -ctime n functions --
346 *
347 * True if the difference between the last change of file
348 * status information and the current time is n 24 hour periods.
349 */
350 int
351 f_ctime(plan, entry)
352 PLAN *plan;
353 FTSENT *entry;
354 {
355 COMPARE((now - entry->fts_statp->st_ctime +
356 SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
357 }
358
359 PLAN *
360 c_ctime(argvp, isok)
361 char ***argvp;
362 int isok;
363 {
364 char *arg = **argvp;
365 PLAN *new;
366
367 (*argvp)++;
368 ftsoptions &= ~FTS_NOSTAT;
369
370 new = palloc(N_CTIME, f_ctime);
371 new->t_data = find_parsenum(new, "-ctime", arg, NULL);
372 TIME_CORRECT(new, N_CTIME);
373 return (new);
374 }
375
376 /*
377 * -depth functions --
378 *
379 * Always true, causes descent of the directory hierarchy to be done
380 * so that all entries in a directory are acted on before the directory
381 * itself.
382 */
383 int
384 f_always_true(plan, entry)
385 PLAN *plan;
386 FTSENT *entry;
387 {
388
389 return (1);
390 }
391
392 PLAN *
393 c_depth(argvp, isok)
394 char ***argvp;
395 int isok;
396 {
397 isdepth = 1;
398
399 return (palloc(N_DEPTH, f_always_true));
400 }
401
402 /*
403 * -empty functions --
404 *
405 * True if the file or directory is empty
406 */
407 int
408 f_empty(plan, entry)
409 PLAN *plan;
410 FTSENT *entry;
411 {
412 if (S_ISREG(entry->fts_statp->st_mode) &&
413 entry->fts_statp->st_size == 0)
414 return (1);
415 if (S_ISDIR(entry->fts_statp->st_mode)) {
416 struct dirent *dp;
417 int empty;
418 DIR *dir;
419
420 empty = 1;
421 dir = opendir(entry->fts_accpath);
422 if (dir == NULL)
423 err(1, "%s", entry->fts_accpath);
424 for (dp = readdir(dir); dp; dp = readdir(dir))
425 if (dp->d_name[0] != '.' ||
426 (dp->d_name[1] != '\0' &&
427 (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
428 empty = 0;
429 break;
430 }
431 closedir(dir);
432 return (empty);
433 }
434 return (0);
435 }
436
437 PLAN *
438 c_empty(argvp, isok)
439 char ***argvp;
440 int isok;
441 {
442 ftsoptions &= ~FTS_NOSTAT;
443
444 return (palloc(N_EMPTY, f_empty));
445 }
446
447 /*
448 * [-exec | -ok] utility [arg ... ] ; functions --
449 *
450 * True if the executed utility returns a zero value as exit status.
451 * The end of the primary expression is delimited by a semicolon. If
452 * "{}" occurs anywhere, it gets replaced by the current pathname.
453 * The current directory for the execution of utility is the same as
454 * the current directory when the find utility was started.
455 *
456 * The primary -ok is different in that it requests affirmation of the
457 * user before executing the utility.
458 */
459 int
460 f_exec(plan, entry)
461 PLAN *plan;
462 FTSENT *entry;
463 {
464 int cnt;
465 pid_t pid;
466 int status;
467
468 for (cnt = 0; plan->e_argv[cnt]; ++cnt)
469 if (plan->e_len[cnt])
470 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
471 entry->fts_path, &plan->e_len[cnt]);
472
473 if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
474 return (0);
475
476 /* don't mix output of command with find output */
477 fflush(stdout);
478 fflush(stderr);
479
480 switch (pid = vfork()) {
481 case -1:
482 err(1, "vfork");
483 /* NOTREACHED */
484 case 0:
485 if (fchdir(dotfd)) {
486 warn("chdir");
487 _exit(1);
488 }
489 execvp(plan->e_argv[0], plan->e_argv);
490 warn("%s", plan->e_argv[0]);
491 _exit(1);
492 }
493 pid = waitpid(pid, &status, 0);
494 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
495 }
496
497 /*
498 * c_exec --
499 * build three parallel arrays, one with pointers to the strings passed
500 * on the command line, one with (possibly duplicated) pointers to the
501 * argv array, and one with integer values that are lengths of the
502 * strings, but also flags meaning that the string has to be massaged.
503 */
504 PLAN *
505 c_exec(argvp, isok)
506 char ***argvp;
507 int isok;
508 {
509 PLAN *new; /* node returned */
510 int cnt;
511 char **argv, **ap, *p;
512
513 isoutput = 1;
514
515 new = palloc(N_EXEC, f_exec);
516 if (isok)
517 new->flags = F_NEEDOK;
518
519 for (ap = argv = *argvp;; ++ap) {
520 if (!*ap)
521 errx(1,
522 "%s: no terminating \";\"", isok ? "-ok" : "-exec");
523 if (**ap == ';')
524 break;
525 }
526
527 cnt = ap - *argvp + 1;
528 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
529 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
530 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
531
532 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
533 new->e_orig[cnt] = *argv;
534 for (p = *argv; *p; ++p)
535 if (p[0] == '{' && p[1] == '}') {
536 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
537 new->e_len[cnt] = MAXPATHLEN;
538 break;
539 }
540 if (!*p) {
541 new->e_argv[cnt] = *argv;
542 new->e_len[cnt] = 0;
543 }
544 }
545 new->e_argv[cnt] = new->e_orig[cnt] = NULL;
546
547 *argvp = argv + 1;
548 return (new);
549 }
550
551 /*
552 * -execdir utility [arg ... ] ; functions --
553 *
554 * True if the executed utility returns a zero value as exit status.
555 * The end of the primary expression is delimited by a semicolon. If
556 * "{}" occurs anywhere, it gets replaced by the unqualified pathname.
557 * The current directory for the execution of utility is the same as
558 * the directory where the file lives.
559 */
560 int
561 f_execdir(plan, entry)
562 PLAN *plan;
563 FTSENT *entry;
564 {
565 int cnt;
566 pid_t pid;
567 int status;
568 char *file;
569
570 /* XXX - if file/dir ends in '/' this will not work -- can it? */
571 if ((file = strrchr(entry->fts_path, '/')))
572 file++;
573 else
574 file = entry->fts_path;
575
576 for (cnt = 0; plan->e_argv[cnt]; ++cnt)
577 if (plan->e_len[cnt])
578 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
579 file, &plan->e_len[cnt]);
580
581 /* don't mix output of command with find output */
582 fflush(stdout);
583 fflush(stderr);
584
585 switch (pid = vfork()) {
586 case -1:
587 err(1, "fork");
588 /* NOTREACHED */
589 case 0:
590 execvp(plan->e_argv[0], plan->e_argv);
591 warn("%s", plan->e_argv[0]);
592 _exit(1);
593 }
594 pid = waitpid(pid, &status, 0);
595 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
596 }
597
598 /*
599 * c_execdir --
600 * build three parallel arrays, one with pointers to the strings passed
601 * on the command line, one with (possibly duplicated) pointers to the
602 * argv array, and one with integer values that are lengths of the
603 * strings, but also flags meaning that the string has to be massaged.
604 */
605 PLAN *
606 c_execdir(argvp, isok)
607 char ***argvp;
608 int isok;
609 {
610 PLAN *new; /* node returned */
611 int cnt;
612 char **argv, **ap, *p;
613
614 ftsoptions &= ~FTS_NOSTAT;
615 isoutput = 1;
616
617 new = palloc(N_EXECDIR, f_execdir);
618
619 for (ap = argv = *argvp;; ++ap) {
620 if (!*ap)
621 errx(1,
622 "-execdir: no terminating \";\"");
623 if (**ap == ';')
624 break;
625 }
626
627 cnt = ap - *argvp + 1;
628 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
629 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
630 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
631
632 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
633 new->e_orig[cnt] = *argv;
634 for (p = *argv; *p; ++p)
635 if (p[0] == '{' && p[1] == '}') {
636 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
637 new->e_len[cnt] = MAXPATHLEN;
638 break;
639 }
640 if (!*p) {
641 new->e_argv[cnt] = *argv;
642 new->e_len[cnt] = 0;
643 }
644 }
645 new->e_argv[cnt] = new->e_orig[cnt] = NULL;
646
647 *argvp = argv + 1;
648 return (new);
649 }
650
651 PLAN *
652 c_exit(argvp, isok)
653 char ***argvp;
654 int isok;
655 {
656 char *arg = **argvp;
657 PLAN *new;
658
659 /* not technically true, but otherwise '-print' is implied */
660 isoutput = 1;
661
662 new = palloc(N_EXIT, f_always_true);
663
664 if (arg) {
665 (*argvp)++;
666 new->exit_val = find_parsenum(new, "-exit", arg, NULL);
667 } else
668 new->exit_val = 0;
669
670 return (new);
671 }
672
673
674 /*
675 * -false function
676 */
677 int
678 f_false(plan, entry)
679 PLAN *plan;
680 FTSENT *entry;
681 {
682
683 return (0);
684 }
685
686 PLAN *
687 c_false(argvp, isok)
688 char ***argvp;
689 int isok;
690 {
691 return (palloc(N_FALSE, f_false));
692 }
693
694
695 /*
696 * -flags [-]flags functions --
697 */
698 int
699 f_flags(plan, entry)
700 PLAN *plan;
701 FTSENT *entry;
702 {
703 u_int32_t flags;
704
705 flags = entry->fts_statp->st_flags;
706 if (plan->flags == F_ATLEAST)
707 return ((plan->f_data | flags) == flags);
708 else
709 return (flags == plan->f_data);
710 /* NOTREACHED */
711 }
712
713 PLAN *
714 c_flags(argvp, isok)
715 char ***argvp;
716 int isok;
717 {
718 char *flags = **argvp;
719 PLAN *new;
720 u_long flagset;
721
722 (*argvp)++;
723 ftsoptions &= ~FTS_NOSTAT;
724
725 new = palloc(N_FLAGS, f_flags);
726
727 if (*flags == '-') {
728 new->flags = F_ATLEAST;
729 ++flags;
730 }
731
732 flagset = 0;
733 if ((strcmp(flags, "none") != 0) &&
734 (string_to_flags(&flags, &flagset, NULL) != 0))
735 errx(1, "-flags: %s: illegal flags string", flags);
736 new->f_data = flagset;
737 return (new);
738 }
739
740 /*
741 * -follow functions --
742 *
743 * Always true, causes symbolic links to be followed on a global
744 * basis.
745 */
746 PLAN *
747 c_follow(argvp, isok)
748 char ***argvp;
749 int isok;
750 {
751 ftsoptions &= ~FTS_PHYSICAL;
752 ftsoptions |= FTS_LOGICAL;
753
754 return (palloc(N_FOLLOW, f_always_true));
755 }
756
757 /* -fprint functions --
758 *
759 * Causes the current pathame to be written to the defined output file.
760 */
761 int
762 f_fprint(plan, entry)
763 PLAN *plan;
764 FTSENT *entry;
765 {
766
767 if (-1 == fprintf(plan->fprint_file, "%s\n", entry->fts_path))
768 warn("fprintf");
769
770 return(1);
771
772 /* no descriptors are closed; they will be closed by
773 operating system when this find command exits. */
774 }
775
776 PLAN *
777 c_fprint(argvp, isok)
778 char ***argvp;
779 int isok;
780 {
781 PLAN *new;
782
783 isoutput = 1; /* do not assume -print */
784
785 new = palloc(N_FPRINT, f_fprint);
786
787 if (NULL == (new->fprint_file = fopen(**argvp, "w")))
788 err(1, "-fprint: %s: cannot create file", **argvp);
789
790 (*argvp)++;
791 return (new);
792 }
793
794 /*
795 * -fstype functions --
796 *
797 * True if the file is of a certain type.
798 */
799 int
800 f_fstype(plan, entry)
801 PLAN *plan;
802 FTSENT *entry;
803 {
804 static dev_t curdev; /* need a guaranteed illegal dev value */
805 static int first = 1;
806 struct statvfs sb;
807 static short val;
808 static char fstype[MFSNAMELEN];
809 char *p, save[2];
810
811 memset(&save, 0, sizeof save); /* XXX gcc */
812
813 /* Only check when we cross mount point. */
814 if (first || curdev != entry->fts_statp->st_dev) {
815 curdev = entry->fts_statp->st_dev;
816
817 /*
818 * Statfs follows symlinks; find wants the link's file system,
819 * not where it points.
820 */
821 if (entry->fts_info == FTS_SL ||
822 entry->fts_info == FTS_SLNONE) {
823 if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
824 ++p;
825 else
826 p = entry->fts_accpath;
827 save[0] = p[0];
828 p[0] = '.';
829 save[1] = p[1];
830 p[1] = '\0';
831
832 } else
833 p = NULL;
834
835 if (statvfs(entry->fts_accpath, &sb))
836 err(1, "%s", entry->fts_accpath);
837
838 if (p) {
839 p[0] = save[0];
840 p[1] = save[1];
841 }
842
843 first = 0;
844
845 /*
846 * Further tests may need both of these values, so
847 * always copy both of them.
848 */
849 val = sb.f_flag;
850 strlcpy(fstype, sb.f_fstypename, sizeof(fstype));
851 }
852 switch (plan->flags) {
853 case F_MTFLAG:
854 return (val & plan->mt_data);
855 case F_MTTYPE:
856 return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
857 default:
858 abort();
859 }
860 }
861
862 PLAN *
863 c_fstype(argvp, isok)
864 char ***argvp;
865 int isok;
866 {
867 char *arg = **argvp;
868 PLAN *new;
869
870 (*argvp)++;
871 ftsoptions &= ~FTS_NOSTAT;
872
873 new = palloc(N_FSTYPE, f_fstype);
874
875 switch (*arg) {
876 case 'l':
877 if (!strcmp(arg, "local")) {
878 new->flags = F_MTFLAG;
879 new->mt_data = MNT_LOCAL;
880 return (new);
881 }
882 break;
883 case 'r':
884 if (!strcmp(arg, "rdonly")) {
885 new->flags = F_MTFLAG;
886 new->mt_data = MNT_RDONLY;
887 return (new);
888 }
889 break;
890 }
891
892 new->flags = F_MTTYPE;
893 new->c_data = arg;
894 return (new);
895 }
896
897 /*
898 * -group gname functions --
899 *
900 * True if the file belongs to the group gname. If gname is numeric and
901 * an equivalent of the getgrnam() function does not return a valid group
902 * name, gname is taken as a group ID.
903 */
904 int
905 f_group(plan, entry)
906 PLAN *plan;
907 FTSENT *entry;
908 {
909
910 return (entry->fts_statp->st_gid == plan->g_data);
911 }
912
913 PLAN *
914 c_group(argvp, isok)
915 char ***argvp;
916 int isok;
917 {
918 char *gname = **argvp;
919 PLAN *new;
920 struct group *g;
921 gid_t gid;
922
923 (*argvp)++;
924 ftsoptions &= ~FTS_NOSTAT;
925
926 g = getgrnam(gname);
927 if (g == NULL) {
928 gid = atoi(gname);
929 if (gid == 0 && gname[0] != '0')
930 errx(1, "-group: %s: no such group", gname);
931 } else
932 gid = g->gr_gid;
933
934 new = palloc(N_GROUP, f_group);
935 new->g_data = gid;
936 return (new);
937 }
938
939 /*
940 * -inum n functions --
941 *
942 * True if the file has inode # n.
943 */
944 int
945 f_inum(plan, entry)
946 PLAN *plan;
947 FTSENT *entry;
948 {
949
950 COMPARE(entry->fts_statp->st_ino, plan->i_data);
951 }
952
953 PLAN *
954 c_inum(argvp, isok)
955 char ***argvp;
956 int isok;
957 {
958 char *arg = **argvp;
959 PLAN *new;
960
961 (*argvp)++;
962 ftsoptions &= ~FTS_NOSTAT;
963
964 new = palloc(N_INUM, f_inum);
965 new->i_data = find_parsenum(new, "-inum", arg, NULL);
966 return (new);
967 }
968
969 /*
970 * -links n functions --
971 *
972 * True if the file has n links.
973 */
974 int
975 f_links(plan, entry)
976 PLAN *plan;
977 FTSENT *entry;
978 {
979
980 COMPARE(entry->fts_statp->st_nlink, plan->l_data);
981 }
982
983 PLAN *
984 c_links(argvp, isok)
985 char ***argvp;
986 int isok;
987 {
988 char *arg = **argvp;
989 PLAN *new;
990
991 (*argvp)++;
992 ftsoptions &= ~FTS_NOSTAT;
993
994 new = palloc(N_LINKS, f_links);
995 new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
996 return (new);
997 }
998
999 /*
1000 * -ls functions --
1001 *
1002 * Always true - prints the current entry to stdout in "ls" format.
1003 */
1004 int
1005 f_ls(plan, entry)
1006 PLAN *plan;
1007 FTSENT *entry;
1008 {
1009
1010 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
1011 return (1);
1012 }
1013
1014 PLAN *
1015 c_ls(argvp, isok)
1016 char ***argvp;
1017 int isok;
1018 {
1019
1020 ftsoptions &= ~FTS_NOSTAT;
1021 isoutput = 1;
1022
1023 return (palloc(N_LS, f_ls));
1024 }
1025
1026 /*
1027 * - maxdepth n functions --
1028 *
1029 * True if the current search depth is less than or equal to the
1030 * maximum depth specified
1031 */
1032 int
1033 f_maxdepth(plan, entry)
1034 PLAN *plan;
1035 FTSENT *entry;
1036 {
1037 extern FTS *tree;
1038
1039 if (entry->fts_level >= plan->max_data)
1040 fts_set(tree, entry, FTS_SKIP);
1041 return (entry->fts_level <= plan->max_data);
1042 }
1043
1044 PLAN *
1045 c_maxdepth(argvp, isok)
1046 char ***argvp;
1047 int isok;
1048 {
1049 char *arg = **argvp;
1050 PLAN *new;
1051
1052 (*argvp)++;
1053 new = palloc(N_MAXDEPTH, f_maxdepth);
1054 new->max_data = atoi(arg);
1055 return (new);
1056 }
1057
1058 /*
1059 * - mindepth n functions --
1060 *
1061 * True if the current search depth is greater than or equal to the
1062 * minimum depth specified
1063 */
1064 int
1065 f_mindepth(plan, entry)
1066 PLAN *plan;
1067 FTSENT *entry;
1068 {
1069 return (entry->fts_level >= plan->min_data);
1070 }
1071
1072 PLAN *
1073 c_mindepth(argvp, isok)
1074 char ***argvp;
1075 int isok;
1076 {
1077 char *arg = **argvp;
1078 PLAN *new;
1079
1080 (*argvp)++;
1081 new = palloc(N_MINDEPTH, f_mindepth);
1082 new->min_data = atoi(arg);
1083 return (new);
1084 }
1085 /*
1086 * -mmin n functions --
1087 *
1088 * True if the difference between the file modification time and the
1089 * current time is n 24 hour periods.
1090 */
1091 int
1092 f_mmin(plan, entry)
1093 PLAN *plan;
1094 FTSENT *entry;
1095 {
1096 COMPARE((now - entry->fts_statp->st_mtime + SECSPERMIN - 1) /
1097 SECSPERMIN, plan->t_data);
1098 }
1099
1100 PLAN *
1101 c_mmin(argvp, isok)
1102 char ***argvp;
1103 int isok;
1104 {
1105 char *arg = **argvp;
1106 PLAN *new;
1107
1108 (*argvp)++;
1109 ftsoptions &= ~FTS_NOSTAT;
1110
1111 new = palloc(N_MMIN, f_mmin);
1112 new->t_data = find_parsenum(new, "-mmin", arg, NULL);
1113 TIME_CORRECT(new, N_MMIN);
1114 return (new);
1115 }
1116 /*
1117 * -mtime n functions --
1118 *
1119 * True if the difference between the file modification time and the
1120 * current time is n 24 hour periods.
1121 */
1122 int
1123 f_mtime(plan, entry)
1124 PLAN *plan;
1125 FTSENT *entry;
1126 {
1127 COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
1128 SECSPERDAY, plan->t_data);
1129 }
1130
1131 PLAN *
1132 c_mtime(argvp, isok)
1133 char ***argvp;
1134 int isok;
1135 {
1136 char *arg = **argvp;
1137 PLAN *new;
1138
1139 (*argvp)++;
1140 ftsoptions &= ~FTS_NOSTAT;
1141
1142 new = palloc(N_MTIME, f_mtime);
1143 new->t_data = find_parsenum(new, "-mtime", arg, NULL);
1144 TIME_CORRECT(new, N_MTIME);
1145 return (new);
1146 }
1147
1148 /*
1149 * -name functions --
1150 *
1151 * True if the basename of the filename being examined
1152 * matches pattern using Pattern Matching Notation S3.14
1153 */
1154 int
1155 f_name(plan, entry)
1156 PLAN *plan;
1157 FTSENT *entry;
1158 {
1159
1160 return (!fnmatch(plan->c_data, entry->fts_name, 0));
1161 }
1162
1163 PLAN *
1164 c_name(argvp, isok)
1165 char ***argvp;
1166 int isok;
1167 {
1168 char *pattern = **argvp;
1169 PLAN *new;
1170
1171 (*argvp)++;
1172 new = palloc(N_NAME, f_name);
1173 new->c_data = pattern;
1174 return (new);
1175 }
1176
1177 /*
1178 * -iname functions --
1179 *
1180 * Similar to -name, but does case insensitive matching
1181 *
1182 */
1183 int
1184 f_iname(plan, entry)
1185 PLAN *plan;
1186 FTSENT *entry;
1187 {
1188 return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD));
1189 }
1190
1191 PLAN *
1192 c_iname(argvp, isok)
1193 char ***argvp;
1194 int isok;
1195 {
1196 char *pattern = **argvp;
1197 PLAN *new;
1198
1199 (*argvp)++;
1200 new = palloc(N_INAME, f_iname);
1201 new->c_data = pattern;
1202 return (new);
1203 }
1204
1205 /*
1206 * -newer file functions --
1207 *
1208 * True if the current file has been modified more recently
1209 * than the modification time of the file named by the pathname
1210 * file.
1211 */
1212 int
1213 f_newer(plan, entry)
1214 PLAN *plan;
1215 FTSENT *entry;
1216 {
1217
1218 return (entry->fts_statp->st_mtime > plan->t_data);
1219 }
1220
1221 PLAN *
1222 c_newer(argvp, isok)
1223 char ***argvp;
1224 int isok;
1225 {
1226 char *filename = **argvp;
1227 PLAN *new;
1228 struct stat sb;
1229
1230 (*argvp)++;
1231 ftsoptions &= ~FTS_NOSTAT;
1232
1233 if (stat(filename, &sb))
1234 err(1, "%s", filename);
1235 new = palloc(N_NEWER, f_newer);
1236 new->t_data = sb.st_mtime;
1237 return (new);
1238 }
1239
1240 /*
1241 * -nogroup functions --
1242 *
1243 * True if file belongs to a user ID for which the equivalent
1244 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
1245 */
1246 int
1247 f_nogroup(plan, entry)
1248 PLAN *plan;
1249 FTSENT *entry;
1250 {
1251
1252 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
1253 }
1254
1255 PLAN *
1256 c_nogroup(argvp, isok)
1257 char ***argvp;
1258 int isok;
1259 {
1260 ftsoptions &= ~FTS_NOSTAT;
1261
1262 return (palloc(N_NOGROUP, f_nogroup));
1263 }
1264
1265 /*
1266 * -nouser functions --
1267 *
1268 * True if file belongs to a user ID for which the equivalent
1269 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
1270 */
1271 int
1272 f_nouser(plan, entry)
1273 PLAN *plan;
1274 FTSENT *entry;
1275 {
1276
1277 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
1278 }
1279
1280 PLAN *
1281 c_nouser(argvp, isok)
1282 char ***argvp;
1283 int isok;
1284 {
1285 ftsoptions &= ~FTS_NOSTAT;
1286
1287 return (palloc(N_NOUSER, f_nouser));
1288 }
1289
1290 /*
1291 * -path functions --
1292 *
1293 * True if the path of the filename being examined
1294 * matches pattern using Pattern Matching Notation S3.14
1295 */
1296 int
1297 f_path(plan, entry)
1298 PLAN *plan;
1299 FTSENT *entry;
1300 {
1301
1302 return (!fnmatch(plan->c_data, entry->fts_path, 0));
1303 }
1304
1305 PLAN *
1306 c_path(argvp, isok)
1307 char ***argvp;
1308 int isok;
1309 {
1310 char *pattern = **argvp;
1311 PLAN *new;
1312
1313 (*argvp)++;
1314 new = palloc(N_NAME, f_path);
1315 new->c_data = pattern;
1316 return (new);
1317 }
1318
1319 /*
1320 * -perm functions --
1321 *
1322 * The mode argument is used to represent file mode bits. If it starts
1323 * with a leading digit, it's treated as an octal mode, otherwise as a
1324 * symbolic mode.
1325 */
1326 int
1327 f_perm(plan, entry)
1328 PLAN *plan;
1329 FTSENT *entry;
1330 {
1331 mode_t mode;
1332
1333 mode = entry->fts_statp->st_mode &
1334 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1335 if (plan->flags == F_ATLEAST)
1336 return ((plan->m_data | mode) == mode);
1337 else
1338 return (mode == plan->m_data);
1339 /* NOTREACHED */
1340 }
1341
1342 PLAN *
1343 c_perm(argvp, isok)
1344 char ***argvp;
1345 int isok;
1346 {
1347 char *perm = **argvp;
1348 PLAN *new;
1349 mode_t *set;
1350
1351 (*argvp)++;
1352 ftsoptions &= ~FTS_NOSTAT;
1353
1354 new = palloc(N_PERM, f_perm);
1355
1356 if (*perm == '-') {
1357 new->flags = F_ATLEAST;
1358 ++perm;
1359 }
1360
1361 if ((set = setmode(perm)) == NULL)
1362 err(1, "-perm: Cannot set file mode `%s'", perm);
1363
1364 new->m_data = getmode(set, 0);
1365 free(set);
1366 return (new);
1367 }
1368
1369 /*
1370 * -print functions --
1371 *
1372 * Always true, causes the current pathame to be written to
1373 * standard output.
1374 */
1375 int
1376 f_print(plan, entry)
1377 PLAN *plan;
1378 FTSENT *entry;
1379 {
1380
1381 (void)printf("%s\n", entry->fts_path);
1382 return (1);
1383 }
1384
1385 int
1386 f_print0(plan, entry)
1387 PLAN *plan;
1388 FTSENT *entry;
1389 {
1390
1391 (void)fputs(entry->fts_path, stdout);
1392 (void)fputc('\0', stdout);
1393 return (1);
1394 }
1395
1396 int
1397 f_printx(plan, entry)
1398 PLAN *plan;
1399 FTSENT *entry;
1400 {
1401 char *cp;
1402
1403 for (cp = entry->fts_path; *cp; cp++) {
1404 if (*cp == '\'' || *cp == '\"' || *cp == ' ' ||
1405 *cp == '$' || *cp == '`' ||
1406 *cp == '\t' || *cp == '\n' || *cp == '\\')
1407 fputc('\\', stdout);
1408
1409 fputc(*cp, stdout);
1410 }
1411
1412 fputc('\n', stdout);
1413 return (1);
1414 }
1415
1416 PLAN *
1417 c_print(argvp, isok)
1418 char ***argvp;
1419 int isok;
1420 {
1421
1422 isoutput = 1;
1423
1424 return (palloc(N_PRINT, f_print));
1425 }
1426
1427 PLAN *
1428 c_print0(argvp, isok)
1429 char ***argvp;
1430 int isok;
1431 {
1432
1433 isoutput = 1;
1434
1435 return (palloc(N_PRINT0, f_print0));
1436 }
1437
1438 PLAN *
1439 c_printx(argvp, isok)
1440 char ***argvp;
1441 int isok;
1442 {
1443
1444 isoutput = 1;
1445
1446 return (palloc(N_PRINTX, f_printx));
1447 }
1448
1449 /*
1450 * -prune functions --
1451 *
1452 * Prune a portion of the hierarchy.
1453 */
1454 int
1455 f_prune(plan, entry)
1456 PLAN *plan;
1457 FTSENT *entry;
1458 {
1459 if (fts_set(tree, entry, FTS_SKIP))
1460 err(1, "%s", entry->fts_path);
1461 return (1);
1462 }
1463
1464 PLAN *
1465 c_prune(argvp, isok)
1466 char ***argvp;
1467 int isok;
1468 {
1469
1470 return (palloc(N_PRUNE, f_prune));
1471 }
1472
1473 /*
1474 * -regex regexp (and related) functions --
1475 *
1476 * True if the complete file path matches the regular expression regexp.
1477 * For -regex, regexp is a case-sensitive (basic) regular expression.
1478 * For -iregex, regexp is a case-insensitive (basic) regular expression.
1479 */
1480 int
1481 f_regex(plan, entry)
1482 PLAN *plan;
1483 FTSENT *entry;
1484 {
1485
1486 return (regexec(&plan->regexp_data, entry->fts_path, 0, NULL, 0) == 0);
1487 }
1488
1489 static PLAN *
1490 c_regex_common(argvp, isok, type, regcomp_flags)
1491 char ***argvp;
1492 int isok, regcomp_flags;
1493 enum ntype type;
1494 {
1495 char errbuf[LINE_MAX];
1496 regex_t reg;
1497 char *regexp = **argvp;
1498 char *lineregexp;
1499 PLAN *new;
1500 int rv;
1501
1502 (*argvp)++;
1503
1504 lineregexp = alloca(strlen(regexp) + 1 + 6); /* max needed */
1505 sprintf(lineregexp, "^%s(%s%s)$",
1506 (regcomp_flags & REG_EXTENDED) ? "" : "\\", regexp,
1507 (regcomp_flags & REG_EXTENDED) ? "" : "\\");
1508 rv = regcomp(®, lineregexp, REG_NOSUB|regcomp_flags);
1509 if (rv != 0) {
1510 regerror(rv, ®, errbuf, sizeof errbuf);
1511 errx(1, "regexp %s: %s", regexp, errbuf);
1512 }
1513
1514 new = palloc(type, f_regex);
1515 new->regexp_data = reg;
1516 return (new);
1517 }
1518
1519 PLAN *
1520 c_regex(argvp, isok)
1521 char ***argvp;
1522 int isok;
1523 {
1524
1525 return (c_regex_common(argvp, isok, N_REGEX, REG_BASIC));
1526 }
1527
1528 PLAN *
1529 c_iregex(argvp, isok)
1530 char ***argvp;
1531 int isok;
1532 {
1533
1534 return (c_regex_common(argvp, isok, N_IREGEX, REG_BASIC|REG_ICASE));
1535 }
1536
1537 /*
1538 * -size n[c] functions --
1539 *
1540 * True if the file size in bytes, divided by an implementation defined
1541 * value and rounded up to the next integer, is n. If n is followed by
1542 * a c, the size is in bytes.
1543 */
1544 #define FIND_SIZE 512
1545 static int divsize = 1;
1546
1547 int
1548 f_size(plan, entry)
1549 PLAN *plan;
1550 FTSENT *entry;
1551 {
1552 off_t size;
1553
1554 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1555 FIND_SIZE : entry->fts_statp->st_size;
1556 COMPARE(size, plan->o_data);
1557 }
1558
1559 PLAN *
1560 c_size(argvp, isok)
1561 char ***argvp;
1562 int isok;
1563 {
1564 char *arg = **argvp;
1565 PLAN *new;
1566 char endch;
1567
1568 (*argvp)++;
1569 ftsoptions &= ~FTS_NOSTAT;
1570
1571 new = palloc(N_SIZE, f_size);
1572 endch = 'c';
1573 new->o_data = find_parsenum(new, "-size", arg, &endch);
1574 if (endch == 'c')
1575 divsize = 0;
1576 return (new);
1577 }
1578
1579 /*
1580 * -type c functions --
1581 *
1582 * True if the type of the file is c, where c is b, c, d, p, f or w
1583 * for block special file, character special file, directory, FIFO,
1584 * regular file or whiteout respectively.
1585 */
1586 int
1587 f_type(plan, entry)
1588 PLAN *plan;
1589 FTSENT *entry;
1590 {
1591
1592 return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1593 }
1594
1595 PLAN *
1596 c_type(argvp, isok)
1597 char ***argvp;
1598 int isok;
1599 {
1600 char *typestring = **argvp;
1601 PLAN *new;
1602 mode_t mask = (mode_t)0;
1603
1604 (*argvp)++;
1605 ftsoptions &= ~FTS_NOSTAT;
1606
1607 switch (typestring[0]) {
1608 case 'b':
1609 mask = S_IFBLK;
1610 break;
1611 case 'c':
1612 mask = S_IFCHR;
1613 break;
1614 case 'd':
1615 mask = S_IFDIR;
1616 break;
1617 case 'f':
1618 mask = S_IFREG;
1619 break;
1620 case 'l':
1621 mask = S_IFLNK;
1622 break;
1623 case 'p':
1624 mask = S_IFIFO;
1625 break;
1626 case 's':
1627 mask = S_IFSOCK;
1628 break;
1629 #ifdef S_IFWHT
1630 case 'W':
1631 case 'w':
1632 mask = S_IFWHT;
1633 #ifdef FTS_WHITEOUT
1634 ftsoptions |= FTS_WHITEOUT;
1635 #endif
1636 break;
1637 #endif /* S_IFWHT */
1638 default:
1639 errx(1, "-type: %s: unknown type", typestring);
1640 }
1641
1642 new = palloc(N_TYPE, f_type);
1643 new->m_data = mask;
1644 return (new);
1645 }
1646
1647 /*
1648 * -user uname functions --
1649 *
1650 * True if the file belongs to the user uname. If uname is numeric and
1651 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1652 * return a valid user name, uname is taken as a user ID.
1653 */
1654 int
1655 f_user(plan, entry)
1656 PLAN *plan;
1657 FTSENT *entry;
1658 {
1659
1660 COMPARE(entry->fts_statp->st_uid, plan->u_data);
1661 }
1662
1663 PLAN *
1664 c_user(argvp, isok)
1665 char ***argvp;
1666 int isok;
1667 {
1668 char *username = **argvp;
1669 PLAN *new;
1670 struct passwd *p;
1671 uid_t uid;
1672
1673 (*argvp)++;
1674 ftsoptions &= ~FTS_NOSTAT;
1675
1676 new = palloc(N_USER, f_user);
1677 p = getpwnam(username);
1678 if (p == NULL) {
1679 if (atoi(username) == 0 && username[0] != '0' &&
1680 strcmp(username, "+0") && strcmp(username, "-0"))
1681 errx(1, "-user: %s: no such user", username);
1682 uid = find_parsenum(new, "-user", username, NULL);
1683
1684 } else {
1685 new->flags = F_EQUAL;
1686 uid = p->pw_uid;
1687 }
1688
1689 new->u_data = uid;
1690 return (new);
1691 }
1692
1693 /*
1694 * -xdev functions --
1695 *
1696 * Always true, causes find not to decend past directories that have a
1697 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1698 */
1699 PLAN *
1700 c_xdev(argvp, isok)
1701 char ***argvp;
1702 int isok;
1703 {
1704 ftsoptions |= FTS_XDEV;
1705
1706 return (palloc(N_XDEV, f_always_true));
1707 }
1708
1709 /*
1710 * ( expression ) functions --
1711 *
1712 * True if expression is true.
1713 */
1714 int
1715 f_expr(plan, entry)
1716 PLAN *plan;
1717 FTSENT *entry;
1718 {
1719 PLAN *p;
1720 int state;
1721
1722 state = 0;
1723 for (p = plan->p_data[0];
1724 p && (state = (p->eval)(p, entry)); p = p->next);
1725 return (state);
1726 }
1727
1728 /*
1729 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1730 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1731 * to a N_EXPR node containing the expression and the ')' node is discarded.
1732 */
1733 PLAN *
1734 c_openparen(argvp, isok)
1735 char ***argvp;
1736 int isok;
1737 {
1738
1739 return (palloc(N_OPENPAREN, (int (*) __P((PLAN *, FTSENT *)))-1));
1740 }
1741
1742 PLAN *
1743 c_closeparen(argvp, isok)
1744 char ***argvp;
1745 int isok;
1746 {
1747
1748 return (palloc(N_CLOSEPAREN, (int (*) __P((PLAN *, FTSENT *)))-1));
1749 }
1750
1751 /*
1752 * ! expression functions --
1753 *
1754 * Negation of a primary; the unary NOT operator.
1755 */
1756 int
1757 f_not(plan, entry)
1758 PLAN *plan;
1759 FTSENT *entry;
1760 {
1761 PLAN *p;
1762 int state;
1763
1764 state = 0;
1765 for (p = plan->p_data[0];
1766 p && (state = (p->eval)(p, entry)); p = p->next);
1767 return (!state);
1768 }
1769
1770 PLAN *
1771 c_not(argvp, isok)
1772 char ***argvp;
1773 int isok;
1774 {
1775
1776 return (palloc(N_NOT, f_not));
1777 }
1778
1779 /*
1780 * expression -o expression functions --
1781 *
1782 * Alternation of primaries; the OR operator. The second expression is
1783 * not evaluated if the first expression is true.
1784 */
1785 int
1786 f_or(plan, entry)
1787 PLAN *plan;
1788 FTSENT *entry;
1789 {
1790 PLAN *p;
1791 int state;
1792
1793 state = 0;
1794 for (p = plan->p_data[0];
1795 p && (state = (p->eval)(p, entry)); p = p->next);
1796
1797 if (state)
1798 return (1);
1799
1800 for (p = plan->p_data[1];
1801 p && (state = (p->eval)(p, entry)); p = p->next);
1802 return (state);
1803 }
1804
1805 PLAN *
1806 c_or(argvp, isok)
1807 char ***argvp;
1808 int isok;
1809 {
1810
1811 return (palloc(N_OR, f_or));
1812 }
1813
1814 PLAN *
1815 c_null(argvp, isok)
1816 char ***argvp;
1817 int isok;
1818 {
1819
1820 return (NULL);
1821 }
1822
1823 static PLAN *
1824 palloc(t, f)
1825 enum ntype t;
1826 int (*f) __P((PLAN *, FTSENT *));
1827 {
1828 PLAN *new;
1829
1830 if ((new = malloc(sizeof(PLAN))) == NULL)
1831 err(1, NULL);
1832 new->type = t;
1833 new->eval = f;
1834 new->flags = 0;
1835 new->next = NULL;
1836 return (new);
1837 }
1838