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