function.c revision 1.52 1 /* $NetBSD: function.c,v 1.52 2005/11/09 00:47:16 reed 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.52 2005/11/09 00:47:16 reed 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 /*
652 * -false function
653 */
654 int
655 f_false(plan, entry)
656 PLAN *plan;
657 FTSENT *entry;
658 {
659
660 return (0);
661 }
662
663 PLAN *
664 c_false(argvp, isok)
665 char ***argvp;
666 int isok;
667 {
668 return (palloc(N_FALSE, f_false));
669 }
670
671
672 /*
673 * -flags [-]flags functions --
674 */
675 int
676 f_flags(plan, entry)
677 PLAN *plan;
678 FTSENT *entry;
679 {
680 u_int32_t flags;
681
682 flags = entry->fts_statp->st_flags;
683 if (plan->flags == F_ATLEAST)
684 return ((plan->f_data | flags) == flags);
685 else
686 return (flags == plan->f_data);
687 /* NOTREACHED */
688 }
689
690 PLAN *
691 c_flags(argvp, isok)
692 char ***argvp;
693 int isok;
694 {
695 char *flags = **argvp;
696 PLAN *new;
697 u_long flagset;
698
699 (*argvp)++;
700 ftsoptions &= ~FTS_NOSTAT;
701
702 new = palloc(N_FLAGS, f_flags);
703
704 if (*flags == '-') {
705 new->flags = F_ATLEAST;
706 ++flags;
707 }
708
709 flagset = 0;
710 if ((strcmp(flags, "none") != 0) &&
711 (string_to_flags(&flags, &flagset, NULL) != 0))
712 errx(1, "-flags: %s: illegal flags string", flags);
713 new->f_data = flagset;
714 return (new);
715 }
716
717 /*
718 * -follow functions --
719 *
720 * Always true, causes symbolic links to be followed on a global
721 * basis.
722 */
723 PLAN *
724 c_follow(argvp, isok)
725 char ***argvp;
726 int isok;
727 {
728 ftsoptions &= ~FTS_PHYSICAL;
729 ftsoptions |= FTS_LOGICAL;
730
731 return (palloc(N_FOLLOW, f_always_true));
732 }
733
734 /* -fprint functions --
735 *
736 * Causes the current pathame to be written to the defined output file.
737 */
738 int
739 f_fprint(plan, entry)
740 PLAN *plan;
741 FTSENT *entry;
742 {
743
744 if (-1 == fprintf(plan->fprint_file, "%s\n", entry->fts_path))
745 warn("fprintf");
746
747 return(1);
748
749 /* no descriptors are closed; they will be closed by
750 operating system when this find command exits. */
751 }
752
753 PLAN *
754 c_fprint(argvp, isok)
755 char ***argvp;
756 int isok;
757 {
758 PLAN *new;
759
760 isoutput = 1; /* do not assume -print */
761
762 new = palloc(N_FPRINT, f_fprint);
763
764 if (NULL == (new->fprint_file = fopen(**argvp, "w")))
765 err(1, "-fprint: %s: cannot create file", **argvp);
766
767 (*argvp)++;
768 return (new);
769 }
770
771 /*
772 * -fstype functions --
773 *
774 * True if the file is of a certain type.
775 */
776 int
777 f_fstype(plan, entry)
778 PLAN *plan;
779 FTSENT *entry;
780 {
781 static dev_t curdev; /* need a guaranteed illegal dev value */
782 static int first = 1;
783 struct statvfs sb;
784 static short val;
785 static char fstype[MFSNAMELEN];
786 char *p, save[2];
787
788 /* Only check when we cross mount point. */
789 if (first || curdev != entry->fts_statp->st_dev) {
790 curdev = entry->fts_statp->st_dev;
791
792 /*
793 * Statfs follows symlinks; find wants the link's file system,
794 * not where it points.
795 */
796 if (entry->fts_info == FTS_SL ||
797 entry->fts_info == FTS_SLNONE) {
798 if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
799 ++p;
800 else
801 p = entry->fts_accpath;
802 save[0] = p[0];
803 p[0] = '.';
804 save[1] = p[1];
805 p[1] = '\0';
806
807 } else
808 p = NULL;
809
810 if (statvfs(entry->fts_accpath, &sb))
811 err(1, "%s", entry->fts_accpath);
812
813 if (p) {
814 p[0] = save[0];
815 p[1] = save[1];
816 }
817
818 first = 0;
819
820 /*
821 * Further tests may need both of these values, so
822 * always copy both of them.
823 */
824 val = sb.f_flag;
825 strlcpy(fstype, sb.f_fstypename, sizeof(fstype));
826 }
827 switch (plan->flags) {
828 case F_MTFLAG:
829 return (val & plan->mt_data);
830 case F_MTTYPE:
831 return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
832 default:
833 abort();
834 }
835 }
836
837 PLAN *
838 c_fstype(argvp, isok)
839 char ***argvp;
840 int isok;
841 {
842 char *arg = **argvp;
843 PLAN *new;
844
845 (*argvp)++;
846 ftsoptions &= ~FTS_NOSTAT;
847
848 new = palloc(N_FSTYPE, f_fstype);
849
850 switch (*arg) {
851 case 'l':
852 if (!strcmp(arg, "local")) {
853 new->flags = F_MTFLAG;
854 new->mt_data = MNT_LOCAL;
855 return (new);
856 }
857 break;
858 case 'r':
859 if (!strcmp(arg, "rdonly")) {
860 new->flags = F_MTFLAG;
861 new->mt_data = MNT_RDONLY;
862 return (new);
863 }
864 break;
865 }
866
867 new->flags = F_MTTYPE;
868 new->c_data = arg;
869 return (new);
870 }
871
872 /*
873 * -group gname functions --
874 *
875 * True if the file belongs to the group gname. If gname is numeric and
876 * an equivalent of the getgrnam() function does not return a valid group
877 * name, gname is taken as a group ID.
878 */
879 int
880 f_group(plan, entry)
881 PLAN *plan;
882 FTSENT *entry;
883 {
884
885 return (entry->fts_statp->st_gid == plan->g_data);
886 }
887
888 PLAN *
889 c_group(argvp, isok)
890 char ***argvp;
891 int isok;
892 {
893 char *gname = **argvp;
894 PLAN *new;
895 struct group *g;
896 gid_t gid;
897
898 (*argvp)++;
899 ftsoptions &= ~FTS_NOSTAT;
900
901 g = getgrnam(gname);
902 if (g == NULL) {
903 gid = atoi(gname);
904 if (gid == 0 && gname[0] != '0')
905 errx(1, "-group: %s: no such group", gname);
906 } else
907 gid = g->gr_gid;
908
909 new = palloc(N_GROUP, f_group);
910 new->g_data = gid;
911 return (new);
912 }
913
914 /*
915 * -inum n functions --
916 *
917 * True if the file has inode # n.
918 */
919 int
920 f_inum(plan, entry)
921 PLAN *plan;
922 FTSENT *entry;
923 {
924
925 COMPARE(entry->fts_statp->st_ino, plan->i_data);
926 }
927
928 PLAN *
929 c_inum(argvp, isok)
930 char ***argvp;
931 int isok;
932 {
933 char *arg = **argvp;
934 PLAN *new;
935
936 (*argvp)++;
937 ftsoptions &= ~FTS_NOSTAT;
938
939 new = palloc(N_INUM, f_inum);
940 new->i_data = find_parsenum(new, "-inum", arg, NULL);
941 return (new);
942 }
943
944 /*
945 * -links n functions --
946 *
947 * True if the file has n links.
948 */
949 int
950 f_links(plan, entry)
951 PLAN *plan;
952 FTSENT *entry;
953 {
954
955 COMPARE(entry->fts_statp->st_nlink, plan->l_data);
956 }
957
958 PLAN *
959 c_links(argvp, isok)
960 char ***argvp;
961 int isok;
962 {
963 char *arg = **argvp;
964 PLAN *new;
965
966 (*argvp)++;
967 ftsoptions &= ~FTS_NOSTAT;
968
969 new = palloc(N_LINKS, f_links);
970 new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
971 return (new);
972 }
973
974 /*
975 * -ls functions --
976 *
977 * Always true - prints the current entry to stdout in "ls" format.
978 */
979 int
980 f_ls(plan, entry)
981 PLAN *plan;
982 FTSENT *entry;
983 {
984
985 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
986 return (1);
987 }
988
989 PLAN *
990 c_ls(argvp, isok)
991 char ***argvp;
992 int isok;
993 {
994
995 ftsoptions &= ~FTS_NOSTAT;
996 isoutput = 1;
997
998 return (palloc(N_LS, f_ls));
999 }
1000
1001 /*
1002 * - maxdepth n functions --
1003 *
1004 * True if the current search depth is less than or equal to the
1005 * maximum depth specified
1006 */
1007 int
1008 f_maxdepth(plan, entry)
1009 PLAN *plan;
1010 FTSENT *entry;
1011 {
1012 extern FTS *tree;
1013
1014 if (entry->fts_level >= plan->max_data)
1015 fts_set(tree, entry, FTS_SKIP);
1016 return (entry->fts_level <= plan->max_data);
1017 }
1018
1019 PLAN *
1020 c_maxdepth(argvp, isok)
1021 char ***argvp;
1022 int isok;
1023 {
1024 char *arg = **argvp;
1025 PLAN *new;
1026
1027 (*argvp)++;
1028 new = palloc(N_MAXDEPTH, f_maxdepth);
1029 new->max_data = atoi(arg);
1030 return (new);
1031 }
1032
1033 /*
1034 * - mindepth n functions --
1035 *
1036 * True if the current search depth is greater than or equal to the
1037 * minimum depth specified
1038 */
1039 int
1040 f_mindepth(plan, entry)
1041 PLAN *plan;
1042 FTSENT *entry;
1043 {
1044 return (entry->fts_level >= plan->min_data);
1045 }
1046
1047 PLAN *
1048 c_mindepth(argvp, isok)
1049 char ***argvp;
1050 int isok;
1051 {
1052 char *arg = **argvp;
1053 PLAN *new;
1054
1055 (*argvp)++;
1056 new = palloc(N_MINDEPTH, f_mindepth);
1057 new->min_data = atoi(arg);
1058 return (new);
1059 }
1060 /*
1061 * -mmin n functions --
1062 *
1063 * True if the difference between the file modification time and the
1064 * current time is n 24 hour periods.
1065 */
1066 int
1067 f_mmin(plan, entry)
1068 PLAN *plan;
1069 FTSENT *entry;
1070 {
1071 COMPARE((now - entry->fts_statp->st_mtime + SECSPERMIN - 1) /
1072 SECSPERMIN, plan->t_data);
1073 }
1074
1075 PLAN *
1076 c_mmin(argvp, isok)
1077 char ***argvp;
1078 int isok;
1079 {
1080 char *arg = **argvp;
1081 PLAN *new;
1082
1083 (*argvp)++;
1084 ftsoptions &= ~FTS_NOSTAT;
1085
1086 new = palloc(N_MMIN, f_mmin);
1087 new->t_data = find_parsenum(new, "-mmin", arg, NULL);
1088 TIME_CORRECT(new, N_MMIN);
1089 return (new);
1090 }
1091 /*
1092 * -mtime n functions --
1093 *
1094 * True if the difference between the file modification time and the
1095 * current time is n 24 hour periods.
1096 */
1097 int
1098 f_mtime(plan, entry)
1099 PLAN *plan;
1100 FTSENT *entry;
1101 {
1102 COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
1103 SECSPERDAY, plan->t_data);
1104 }
1105
1106 PLAN *
1107 c_mtime(argvp, isok)
1108 char ***argvp;
1109 int isok;
1110 {
1111 char *arg = **argvp;
1112 PLAN *new;
1113
1114 (*argvp)++;
1115 ftsoptions &= ~FTS_NOSTAT;
1116
1117 new = palloc(N_MTIME, f_mtime);
1118 new->t_data = find_parsenum(new, "-mtime", arg, NULL);
1119 TIME_CORRECT(new, N_MTIME);
1120 return (new);
1121 }
1122
1123 /*
1124 * -name functions --
1125 *
1126 * True if the basename of the filename being examined
1127 * matches pattern using Pattern Matching Notation S3.14
1128 */
1129 int
1130 f_name(plan, entry)
1131 PLAN *plan;
1132 FTSENT *entry;
1133 {
1134
1135 return (!fnmatch(plan->c_data, entry->fts_name, 0));
1136 }
1137
1138 PLAN *
1139 c_name(argvp, isok)
1140 char ***argvp;
1141 int isok;
1142 {
1143 char *pattern = **argvp;
1144 PLAN *new;
1145
1146 (*argvp)++;
1147 new = palloc(N_NAME, f_name);
1148 new->c_data = pattern;
1149 return (new);
1150 }
1151
1152 /*
1153 * -iname functions --
1154 *
1155 * Similar to -name, but does case insensitive matching
1156 *
1157 */
1158 int
1159 f_iname(plan, entry)
1160 PLAN *plan;
1161 FTSENT *entry;
1162 {
1163 return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD));
1164 }
1165
1166 PLAN *
1167 c_iname(argvp, isok)
1168 char ***argvp;
1169 int isok;
1170 {
1171 char *pattern = **argvp;
1172 PLAN *new;
1173
1174 (*argvp)++;
1175 new = palloc(N_INAME, f_iname);
1176 new->c_data = pattern;
1177 return (new);
1178 }
1179
1180 /*
1181 * -newer file functions --
1182 *
1183 * True if the current file has been modified more recently
1184 * than the modification time of the file named by the pathname
1185 * file.
1186 */
1187 int
1188 f_newer(plan, entry)
1189 PLAN *plan;
1190 FTSENT *entry;
1191 {
1192
1193 return (entry->fts_statp->st_mtime > plan->t_data);
1194 }
1195
1196 PLAN *
1197 c_newer(argvp, isok)
1198 char ***argvp;
1199 int isok;
1200 {
1201 char *filename = **argvp;
1202 PLAN *new;
1203 struct stat sb;
1204
1205 (*argvp)++;
1206 ftsoptions &= ~FTS_NOSTAT;
1207
1208 if (stat(filename, &sb))
1209 err(1, "%s", filename);
1210 new = palloc(N_NEWER, f_newer);
1211 new->t_data = sb.st_mtime;
1212 return (new);
1213 }
1214
1215 /*
1216 * -nogroup functions --
1217 *
1218 * True if file belongs to a user ID for which the equivalent
1219 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
1220 */
1221 int
1222 f_nogroup(plan, entry)
1223 PLAN *plan;
1224 FTSENT *entry;
1225 {
1226
1227 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
1228 }
1229
1230 PLAN *
1231 c_nogroup(argvp, isok)
1232 char ***argvp;
1233 int isok;
1234 {
1235 ftsoptions &= ~FTS_NOSTAT;
1236
1237 return (palloc(N_NOGROUP, f_nogroup));
1238 }
1239
1240 /*
1241 * -nouser functions --
1242 *
1243 * True if file belongs to a user ID for which the equivalent
1244 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
1245 */
1246 int
1247 f_nouser(plan, entry)
1248 PLAN *plan;
1249 FTSENT *entry;
1250 {
1251
1252 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
1253 }
1254
1255 PLAN *
1256 c_nouser(argvp, isok)
1257 char ***argvp;
1258 int isok;
1259 {
1260 ftsoptions &= ~FTS_NOSTAT;
1261
1262 return (palloc(N_NOUSER, f_nouser));
1263 }
1264
1265 /*
1266 * -path functions --
1267 *
1268 * True if the path of the filename being examined
1269 * matches pattern using Pattern Matching Notation S3.14
1270 */
1271 int
1272 f_path(plan, entry)
1273 PLAN *plan;
1274 FTSENT *entry;
1275 {
1276
1277 return (!fnmatch(plan->c_data, entry->fts_path, 0));
1278 }
1279
1280 PLAN *
1281 c_path(argvp, isok)
1282 char ***argvp;
1283 int isok;
1284 {
1285 char *pattern = **argvp;
1286 PLAN *new;
1287
1288 (*argvp)++;
1289 new = palloc(N_NAME, f_path);
1290 new->c_data = pattern;
1291 return (new);
1292 }
1293
1294 /*
1295 * -perm functions --
1296 *
1297 * The mode argument is used to represent file mode bits. If it starts
1298 * with a leading digit, it's treated as an octal mode, otherwise as a
1299 * symbolic mode.
1300 */
1301 int
1302 f_perm(plan, entry)
1303 PLAN *plan;
1304 FTSENT *entry;
1305 {
1306 mode_t mode;
1307
1308 mode = entry->fts_statp->st_mode &
1309 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1310 if (plan->flags == F_ATLEAST)
1311 return ((plan->m_data | mode) == mode);
1312 else
1313 return (mode == plan->m_data);
1314 /* NOTREACHED */
1315 }
1316
1317 PLAN *
1318 c_perm(argvp, isok)
1319 char ***argvp;
1320 int isok;
1321 {
1322 char *perm = **argvp;
1323 PLAN *new;
1324 mode_t *set;
1325
1326 (*argvp)++;
1327 ftsoptions &= ~FTS_NOSTAT;
1328
1329 new = palloc(N_PERM, f_perm);
1330
1331 if (*perm == '-') {
1332 new->flags = F_ATLEAST;
1333 ++perm;
1334 }
1335
1336 if ((set = setmode(perm)) == NULL)
1337 err(1, "-perm: Cannot set file mode `%s'", perm);
1338
1339 new->m_data = getmode(set, 0);
1340 free(set);
1341 return (new);
1342 }
1343
1344 /*
1345 * -print functions --
1346 *
1347 * Always true, causes the current pathame to be written to
1348 * standard output.
1349 */
1350 int
1351 f_print(plan, entry)
1352 PLAN *plan;
1353 FTSENT *entry;
1354 {
1355
1356 (void)printf("%s\n", entry->fts_path);
1357 return (1);
1358 }
1359
1360 int
1361 f_print0(plan, entry)
1362 PLAN *plan;
1363 FTSENT *entry;
1364 {
1365
1366 (void)fputs(entry->fts_path, stdout);
1367 (void)fputc('\0', stdout);
1368 return (1);
1369 }
1370
1371 int
1372 f_printx(plan, entry)
1373 PLAN *plan;
1374 FTSENT *entry;
1375 {
1376 char *cp;
1377
1378 for (cp = entry->fts_path; *cp; cp++) {
1379 if (*cp == '\'' || *cp == '\"' || *cp == ' ' ||
1380 *cp == '$' || *cp == '`' ||
1381 *cp == '\t' || *cp == '\n' || *cp == '\\')
1382 fputc('\\', stdout);
1383
1384 fputc(*cp, stdout);
1385 }
1386
1387 fputc('\n', stdout);
1388 return (1);
1389 }
1390
1391 PLAN *
1392 c_print(argvp, isok)
1393 char ***argvp;
1394 int isok;
1395 {
1396
1397 isoutput = 1;
1398
1399 return (palloc(N_PRINT, f_print));
1400 }
1401
1402 PLAN *
1403 c_print0(argvp, isok)
1404 char ***argvp;
1405 int isok;
1406 {
1407
1408 isoutput = 1;
1409
1410 return (palloc(N_PRINT0, f_print0));
1411 }
1412
1413 PLAN *
1414 c_printx(argvp, isok)
1415 char ***argvp;
1416 int isok;
1417 {
1418
1419 isoutput = 1;
1420
1421 return (palloc(N_PRINTX, f_printx));
1422 }
1423
1424 /*
1425 * -prune functions --
1426 *
1427 * Prune a portion of the hierarchy.
1428 */
1429 int
1430 f_prune(plan, entry)
1431 PLAN *plan;
1432 FTSENT *entry;
1433 {
1434 if (fts_set(tree, entry, FTS_SKIP))
1435 err(1, "%s", entry->fts_path);
1436 return (1);
1437 }
1438
1439 PLAN *
1440 c_prune(argvp, isok)
1441 char ***argvp;
1442 int isok;
1443 {
1444
1445 return (palloc(N_PRUNE, f_prune));
1446 }
1447
1448 /*
1449 * -regex regexp (and related) functions --
1450 *
1451 * True if the complete file path matches the regular expression regexp.
1452 * For -regex, regexp is a case-sensitive (basic) regular expression.
1453 * For -iregex, regexp is a case-insensitive (basic) regular expression.
1454 */
1455 int
1456 f_regex(plan, entry)
1457 PLAN *plan;
1458 FTSENT *entry;
1459 {
1460
1461 return (regexec(&plan->regexp_data, entry->fts_path, 0, NULL, 0) == 0);
1462 }
1463
1464 static PLAN *
1465 c_regex_common(argvp, isok, type, regcomp_flags)
1466 char ***argvp;
1467 int isok, regcomp_flags;
1468 enum ntype type;
1469 {
1470 char errbuf[LINE_MAX];
1471 regex_t reg;
1472 char *regexp = **argvp;
1473 char *lineregexp;
1474 PLAN *new;
1475 int rv;
1476
1477 (*argvp)++;
1478
1479 lineregexp = alloca(strlen(regexp) + 1 + 6); /* max needed */
1480 sprintf(lineregexp, "^%s(%s%s)$",
1481 (regcomp_flags & REG_EXTENDED) ? "" : "\\", regexp,
1482 (regcomp_flags & REG_EXTENDED) ? "" : "\\");
1483 rv = regcomp(®, lineregexp, REG_NOSUB|regcomp_flags);
1484 if (rv != 0) {
1485 regerror(rv, ®, errbuf, sizeof errbuf);
1486 errx(1, "regexp %s: %s", regexp, errbuf);
1487 }
1488
1489 new = palloc(type, f_regex);
1490 new->regexp_data = reg;
1491 return (new);
1492 }
1493
1494 PLAN *
1495 c_regex(argvp, isok)
1496 char ***argvp;
1497 int isok;
1498 {
1499
1500 return (c_regex_common(argvp, isok, N_REGEX, REG_BASIC));
1501 }
1502
1503 PLAN *
1504 c_iregex(argvp, isok)
1505 char ***argvp;
1506 int isok;
1507 {
1508
1509 return (c_regex_common(argvp, isok, N_IREGEX, REG_BASIC|REG_ICASE));
1510 }
1511
1512 /*
1513 * -size n[c] functions --
1514 *
1515 * True if the file size in bytes, divided by an implementation defined
1516 * value and rounded up to the next integer, is n. If n is followed by
1517 * a c, the size is in bytes.
1518 */
1519 #define FIND_SIZE 512
1520 static int divsize = 1;
1521
1522 int
1523 f_size(plan, entry)
1524 PLAN *plan;
1525 FTSENT *entry;
1526 {
1527 off_t size;
1528
1529 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1530 FIND_SIZE : entry->fts_statp->st_size;
1531 COMPARE(size, plan->o_data);
1532 }
1533
1534 PLAN *
1535 c_size(argvp, isok)
1536 char ***argvp;
1537 int isok;
1538 {
1539 char *arg = **argvp;
1540 PLAN *new;
1541 char endch;
1542
1543 (*argvp)++;
1544 ftsoptions &= ~FTS_NOSTAT;
1545
1546 new = palloc(N_SIZE, f_size);
1547 endch = 'c';
1548 new->o_data = find_parsenum(new, "-size", arg, &endch);
1549 if (endch == 'c')
1550 divsize = 0;
1551 return (new);
1552 }
1553
1554 /*
1555 * -type c functions --
1556 *
1557 * True if the type of the file is c, where c is b, c, d, p, f or w
1558 * for block special file, character special file, directory, FIFO,
1559 * regular file or whiteout respectively.
1560 */
1561 int
1562 f_type(plan, entry)
1563 PLAN *plan;
1564 FTSENT *entry;
1565 {
1566
1567 return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1568 }
1569
1570 PLAN *
1571 c_type(argvp, isok)
1572 char ***argvp;
1573 int isok;
1574 {
1575 char *typestring = **argvp;
1576 PLAN *new;
1577 mode_t mask = (mode_t)0;
1578
1579 (*argvp)++;
1580 ftsoptions &= ~FTS_NOSTAT;
1581
1582 switch (typestring[0]) {
1583 case 'b':
1584 mask = S_IFBLK;
1585 break;
1586 case 'c':
1587 mask = S_IFCHR;
1588 break;
1589 case 'd':
1590 mask = S_IFDIR;
1591 break;
1592 case 'f':
1593 mask = S_IFREG;
1594 break;
1595 case 'l':
1596 mask = S_IFLNK;
1597 break;
1598 case 'p':
1599 mask = S_IFIFO;
1600 break;
1601 case 's':
1602 mask = S_IFSOCK;
1603 break;
1604 #ifdef S_IFWHT
1605 case 'W':
1606 case 'w':
1607 mask = S_IFWHT;
1608 #ifdef FTS_WHITEOUT
1609 ftsoptions |= FTS_WHITEOUT;
1610 #endif
1611 break;
1612 #endif /* S_IFWHT */
1613 default:
1614 errx(1, "-type: %s: unknown type", typestring);
1615 }
1616
1617 new = palloc(N_TYPE, f_type);
1618 new->m_data = mask;
1619 return (new);
1620 }
1621
1622 /*
1623 * -user uname functions --
1624 *
1625 * True if the file belongs to the user uname. If uname is numeric and
1626 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1627 * return a valid user name, uname is taken as a user ID.
1628 */
1629 int
1630 f_user(plan, entry)
1631 PLAN *plan;
1632 FTSENT *entry;
1633 {
1634
1635 COMPARE(entry->fts_statp->st_uid, plan->u_data);
1636 }
1637
1638 PLAN *
1639 c_user(argvp, isok)
1640 char ***argvp;
1641 int isok;
1642 {
1643 char *username = **argvp;
1644 PLAN *new;
1645 struct passwd *p;
1646 uid_t uid;
1647
1648 (*argvp)++;
1649 ftsoptions &= ~FTS_NOSTAT;
1650
1651 new = palloc(N_USER, f_user);
1652 p = getpwnam(username);
1653 if (p == NULL) {
1654 if (atoi(username) == 0 && username[0] != '0' &&
1655 strcmp(username, "+0") && strcmp(username, "-0"))
1656 errx(1, "-user: %s: no such user", username);
1657 uid = find_parsenum(new, "-user", username, NULL);
1658
1659 } else {
1660 new->flags = F_EQUAL;
1661 uid = p->pw_uid;
1662 }
1663
1664 new->u_data = uid;
1665 return (new);
1666 }
1667
1668 /*
1669 * -xdev functions --
1670 *
1671 * Always true, causes find not to decend past directories that have a
1672 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1673 */
1674 PLAN *
1675 c_xdev(argvp, isok)
1676 char ***argvp;
1677 int isok;
1678 {
1679 ftsoptions |= FTS_XDEV;
1680
1681 return (palloc(N_XDEV, f_always_true));
1682 }
1683
1684 /*
1685 * ( expression ) functions --
1686 *
1687 * True if expression is true.
1688 */
1689 int
1690 f_expr(plan, entry)
1691 PLAN *plan;
1692 FTSENT *entry;
1693 {
1694 PLAN *p;
1695 int state;
1696
1697 state = 0;
1698 for (p = plan->p_data[0];
1699 p && (state = (p->eval)(p, entry)); p = p->next);
1700 return (state);
1701 }
1702
1703 /*
1704 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1705 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1706 * to a N_EXPR node containing the expression and the ')' node is discarded.
1707 */
1708 PLAN *
1709 c_openparen(argvp, isok)
1710 char ***argvp;
1711 int isok;
1712 {
1713
1714 return (palloc(N_OPENPAREN, (int (*) __P((PLAN *, FTSENT *)))-1));
1715 }
1716
1717 PLAN *
1718 c_closeparen(argvp, isok)
1719 char ***argvp;
1720 int isok;
1721 {
1722
1723 return (palloc(N_CLOSEPAREN, (int (*) __P((PLAN *, FTSENT *)))-1));
1724 }
1725
1726 /*
1727 * ! expression functions --
1728 *
1729 * Negation of a primary; the unary NOT operator.
1730 */
1731 int
1732 f_not(plan, entry)
1733 PLAN *plan;
1734 FTSENT *entry;
1735 {
1736 PLAN *p;
1737 int state;
1738
1739 state = 0;
1740 for (p = plan->p_data[0];
1741 p && (state = (p->eval)(p, entry)); p = p->next);
1742 return (!state);
1743 }
1744
1745 PLAN *
1746 c_not(argvp, isok)
1747 char ***argvp;
1748 int isok;
1749 {
1750
1751 return (palloc(N_NOT, f_not));
1752 }
1753
1754 /*
1755 * expression -o expression functions --
1756 *
1757 * Alternation of primaries; the OR operator. The second expression is
1758 * not evaluated if the first expression is true.
1759 */
1760 int
1761 f_or(plan, entry)
1762 PLAN *plan;
1763 FTSENT *entry;
1764 {
1765 PLAN *p;
1766 int state;
1767
1768 state = 0;
1769 for (p = plan->p_data[0];
1770 p && (state = (p->eval)(p, entry)); p = p->next);
1771
1772 if (state)
1773 return (1);
1774
1775 for (p = plan->p_data[1];
1776 p && (state = (p->eval)(p, entry)); p = p->next);
1777 return (state);
1778 }
1779
1780 PLAN *
1781 c_or(argvp, isok)
1782 char ***argvp;
1783 int isok;
1784 {
1785
1786 return (palloc(N_OR, f_or));
1787 }
1788
1789 PLAN *
1790 c_null(argvp, isok)
1791 char ***argvp;
1792 int isok;
1793 {
1794
1795 return (NULL);
1796 }
1797
1798 static PLAN *
1799 palloc(t, f)
1800 enum ntype t;
1801 int (*f) __P((PLAN *, FTSENT *));
1802 {
1803 PLAN *new;
1804
1805 if ((new = malloc(sizeof(PLAN))) == NULL)
1806 err(1, NULL);
1807 new->type = t;
1808 new->eval = f;
1809 new->flags = 0;
1810 new->next = NULL;
1811 return (new);
1812 }
1813