function.c revision 1.5 1 /*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Cimarron D. Taylor of the University of California, Berkeley.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #ifndef lint
38 static char sccsid[] = "@(#)function.c 5.17 (Berkeley) 5/24/91";
39 #endif /* not lint */
40
41 #include <sys/param.h>
42 #include <sys/stat.h>
43 #include <sys/wait.h>
44 #include <sys/mount.h>
45 #include <errno.h>
46 #include <grp.h>
47 #include <pwd.h>
48 #include <fts.h>
49 #include <unistd.h>
50 #include <tzfile.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <fnmatch.h>
55 #include "find.h"
56
57 #define FIND_EQUAL 0
58 #define FIND_LESSTHAN 1
59 #define FIND_GREATER 2
60
61 #define COMPARE(a, b) { \
62 switch(plan->flags) { \
63 case FIND_EQUAL: \
64 return(a == b); \
65 case FIND_LESSTHAN: \
66 return(a < b); \
67 case FIND_GREATER: \
68 return(a > b); \
69 } \
70 return(0); \
71 }
72
73 static PLAN *palloc __P((enum ntype, int (*)()));
74
75 /*
76 * find_parsenum --
77 * Parse a string of the form [+-]# and return the value.
78 */
79 long
80 find_parsenum(plan, option, str, endch)
81 PLAN *plan;
82 char *option, *str, *endch;
83 {
84 long value;
85 char *endchar; /* pointer to character ending conversion */
86
87 /* determine comparison from leading + or - */
88 switch(*str) {
89 case '+':
90 ++str;
91 plan->flags = FIND_GREATER;
92 break;
93 case '-':
94 ++str;
95 plan->flags = FIND_LESSTHAN;
96 break;
97 default:
98 plan->flags = FIND_EQUAL;
99 break;
100 }
101
102 /*
103 * convert the string with strtol(). Note, if strtol() returns zero
104 * and endchar points to the beginning of the string we know we have
105 * a syntax error.
106 */
107 value = strtol(str, &endchar, 10);
108 if (!value && endchar == str ||
109 endchar[0] && (!endch || endchar[0] != *endch))
110 err("%s: %s", option, "illegal numeric value");
111 if (endch)
112 *endch = endchar[0];
113 return(value);
114 }
115
116 /*
117 * -atime n functions --
118 *
119 * True if the difference between the file access time and the
120 * current time is n 24 hour periods.
121 *
122 */
123 f_atime(plan, entry)
124 PLAN *plan;
125 FTSENT *entry;
126 {
127 extern time_t now;
128
129 COMPARE((now - entry->fts_statb.st_atime +
130 SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
131 }
132
133 PLAN *
134 c_atime(arg)
135 char *arg;
136 {
137 PLAN *new;
138
139 ftsoptions &= ~FTS_NOSTAT;
140
141 new = palloc(N_ATIME, f_atime);
142 new->t_data = find_parsenum(new, "-atime", arg, NULL);
143 return(new);
144 }
145 /*
146 * -ctime n functions --
147 *
148 * True if the difference between the last change of file
149 * status information and the current time is n 24 hour periods.
150 */
151 f_ctime(plan, entry)
152 PLAN *plan;
153 FTSENT *entry;
154 {
155 extern time_t now;
156
157 COMPARE((now - entry->fts_statb.st_ctime +
158 SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
159 }
160
161 PLAN *
162 c_ctime(arg)
163 char *arg;
164 {
165 PLAN *new;
166
167 ftsoptions &= ~FTS_NOSTAT;
168
169 new = palloc(N_CTIME, f_ctime);
170 new->t_data = find_parsenum(new, "-ctime", arg, (char *)NULL);
171 return(new);
172 }
173
174 /*
175 * -depth functions --
176 *
177 * Always true, causes descent of the directory hierarchy to be done
178 * so that all entries in a directory are acted on before the directory
179 * itself.
180 */
181 /* ARGSUSED */
182 f_always_true(plan, entry)
183 PLAN *plan;
184 FTSENT *entry;
185 {
186 return(1);
187 }
188
189 PLAN *
190 c_depth()
191 {
192 isdepth = 1;
193
194 return(palloc(N_DEPTH, f_always_true));
195 }
196
197 /*
198 * [-exec | -ok] utility [arg ... ] ; functions --
199 *
200 * True if the executed utility returns a zero value as exit status.
201 * The end of the primary expression is delimited by a semicolon. If
202 * "{}" occurs anywhere, it gets replaced by the current pathname.
203 * The current directory for the execution of utility is the same as
204 * the current directory when the find utility was started.
205 *
206 * The primary -ok is different in that it requests affirmation of the
207 * user before executing the utility.
208 */
209 f_exec(plan, entry)
210 register PLAN *plan;
211 FTSENT *entry;
212 {
213 extern int dotfd;
214 register int cnt;
215 pid_t pid;
216 int status;
217
218 for (cnt = 0; plan->e_argv[cnt]; ++cnt)
219 if (plan->e_len[cnt])
220 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
221 entry->fts_path, plan->e_len[cnt]);
222
223 if (plan->flags && !queryuser(plan->e_argv))
224 return(0);
225
226 switch(pid = vfork()) {
227 case -1:
228 err("fork: %s", strerror(errno));
229 /* NOTREACHED */
230 case 0:
231 if (fchdir(dotfd)) {
232 (void)fprintf(stderr,
233 "find: chdir: %s\n", strerror(errno));
234 _exit(1);
235 }
236 execvp(plan->e_argv[0], plan->e_argv);
237 (void)fprintf(stderr,
238 "find: %s: %s\n", plan->e_argv[0], strerror(errno));
239 _exit(1);
240 }
241 pid = waitpid(pid, &status, 0);
242 return(pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
243 }
244
245 /*
246 * c_exec --
247 * build three parallel arrays, one with pointers to the strings passed
248 * on the command line, one with (possibly duplicated) pointers to the
249 * argv array, and one with integer values that are lengths of the
250 * strings, but also flags meaning that the string has to be massaged.
251 */
252 PLAN *
253 c_exec(argvp, isok)
254 char ***argvp;
255 int isok;
256 {
257 PLAN *new; /* node returned */
258 register int cnt;
259 register char **argv, **ap, *p;
260
261 isoutput = 1;
262
263 new = palloc(N_EXEC, f_exec);
264 new->flags = isok;
265
266 for (ap = argv = *argvp;; ++ap) {
267 if (!*ap)
268 err("%s: %s",
269 isok ? "-ok" : "-exec", "no terminating \";\"");
270 if (**ap == ';')
271 break;
272 }
273
274 cnt = ap - *argvp + 1;
275 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
276 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
277 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
278
279 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
280 new->e_orig[cnt] = *argv;
281 for (p = *argv; *p; ++p)
282 if (p[0] == '{' && p[1] == '}') {
283 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
284 new->e_len[cnt] = MAXPATHLEN;
285 break;
286 }
287 if (!*p) {
288 new->e_argv[cnt] = *argv;
289 new->e_len[cnt] = 0;
290 }
291 }
292 new->e_argv[cnt] = new->e_orig[cnt] = NULL;
293
294 *argvp = argv + 1;
295 return(new);
296 }
297
298 /*
299 * -follow functions --
300 *
301 * Always true, causes symbolic links to be followed on a global
302 * basis.
303 */
304 PLAN *
305 c_follow()
306 {
307 ftsoptions &= ~FTS_PHYSICAL;
308 ftsoptions |= FTS_LOGICAL;
309
310 return(palloc(N_FOLLOW, f_always_true));
311 }
312
313 /*
314 * -fstype functions --
315 *
316 * True if the file is of a certain type.
317 */
318 f_fstype(plan, entry)
319 PLAN *plan;
320 FTSENT *entry;
321 {
322 static dev_t curdev; /* need a guaranteed illegal dev value */
323 static int first = 1;
324 static struct statfs sb;
325 char *p, save[2];
326
327 /* only check when we cross mount point */
328 if (first || curdev != entry->fts_statb.st_dev) {
329 curdev = entry->fts_statb.st_dev;
330
331 /*
332 * Statfs follows symlinks; find wants the link's file system,
333 * not where it points.
334 */
335 if (entry->fts_info == FTS_SL ||
336 entry->fts_info == FTS_SLNONE) {
337 if (p = rindex(entry->fts_accpath, '/'))
338 ++p;
339 else
340 p = entry->fts_accpath;
341 save[0] = p[0];
342 p[0] = '.';
343 save[1] = p[1];
344 p[1] = '\0';
345
346 } else
347 p = NULL;
348
349 if (statfs(entry->fts_accpath, &sb))
350 err("%s: %s", entry->fts_accpath, strerror(errno));
351
352 if (p) {
353 p[0] = save[0];
354 p[1] = save[1];
355 }
356
357 first = 0;
358 }
359 return(plan->flags == MOUNT_NONE ?
360 sb.f_flags & plan->m_flags : sb.f_type == plan->flags);
361 }
362
363 PLAN *
364 c_fstype(arg)
365 char *arg;
366 {
367 register PLAN *new;
368
369 ftsoptions &= ~FTS_NOSTAT;
370
371 new = palloc(N_FSTYPE, f_fstype);
372 switch(*arg) {
373 case 'f':
374 if (!strcmp(arg, "fdesc")) {
375 #ifdef MOUNT_FDESC
376 new->flags = MOUNT_FDESC;
377 return(new);
378 #else
379 err("unknown file type %s", arg);
380 #endif
381 }
382 break;
383 case 'i':
384 if (!strcmp(arg, "isofs")) {
385 new->flags = MOUNT_ISOFS;
386 return(new);
387 }
388 break;
389 case 'k':
390 if (!strcmp(arg, "kernfs")) {
391 #ifdef MOUNT_KERNFS
392 new->flags = MOUNT_KERNFS;
393 return(new);
394 #else
395 err("unknown file type %s", arg);
396 #endif
397 }
398 break;
399 case 'l':
400 if (!strcmp(arg, "local")) {
401 new->flags = MOUNT_NONE;
402 new->m_flags = MNT_LOCAL;
403 return(new);
404 }
405 break;
406 case 'm':
407 if (!strcmp(arg, "mfs")) {
408 new->flags = MOUNT_MFS;
409 return(new);
410 }
411 if (!strcmp(arg, "msdos")) {
412 new->flags = MOUNT_MSDOS;
413 return(new);
414 }
415 break;
416 case 'n':
417 if (!strcmp(arg, "nfs")) {
418 new->flags = MOUNT_NFS;
419 return(new);
420 }
421 break;
422 case 'r':
423 if (!strcmp(arg, "rdonly")) {
424 new->flags = MOUNT_NONE;
425 new->m_flags = MNT_RDONLY;
426 return(new);
427 }
428 break;
429 case 'u':
430 if (!strcmp(arg, "ufs")) {
431 new->flags = MOUNT_UFS;
432 return(new);
433 }
434 break;
435 }
436 err("unknown file type %s", arg);
437 /* NOTREACHED */
438 }
439
440 /*
441 * -group gname functions --
442 *
443 * True if the file belongs to the group gname. If gname is numeric and
444 * an equivalent of the getgrnam() function does not return a valid group
445 * name, gname is taken as a group ID.
446 */
447 f_group(plan, entry)
448 PLAN *plan;
449 FTSENT *entry;
450 {
451 return(entry->fts_statb.st_gid == plan->g_data);
452 }
453
454 PLAN *
455 c_group(gname)
456 char *gname;
457 {
458 PLAN *new;
459 struct group *g;
460 gid_t gid;
461
462 ftsoptions &= ~FTS_NOSTAT;
463
464 g = getgrnam(gname);
465 if (g == NULL) {
466 gid = atoi(gname);
467 if (gid == 0 && gname[0] != '0')
468 err("%s: %s", "-group", "no such group");
469 } else
470 gid = g->gr_gid;
471
472 new = palloc(N_GROUP, f_group);
473 new->g_data = gid;
474 return(new);
475 }
476
477 /*
478 * -inum n functions --
479 *
480 * True if the file has inode # n.
481 */
482 f_inum(plan, entry)
483 PLAN *plan;
484 FTSENT *entry;
485 {
486 COMPARE(entry->fts_statb.st_ino, plan->i_data);
487 }
488
489 PLAN *
490 c_inum(arg)
491 char *arg;
492 {
493 PLAN *new;
494
495 ftsoptions &= ~FTS_NOSTAT;
496
497 new = palloc(N_INUM, f_inum);
498 new->i_data = find_parsenum(new, "-inum", arg, (char *)NULL);
499 return(new);
500 }
501
502 /*
503 * -links n functions --
504 *
505 * True if the file has n links.
506 */
507 f_links(plan, entry)
508 PLAN *plan;
509 FTSENT *entry;
510 {
511 COMPARE(entry->fts_statb.st_nlink, plan->l_data);
512 }
513
514 PLAN *
515 c_links(arg)
516 char *arg;
517 {
518 PLAN *new;
519
520 ftsoptions &= ~FTS_NOSTAT;
521
522 new = palloc(N_LINKS, f_links);
523 new->l_data = (nlink_t)find_parsenum(new, "-links", arg, (char *)NULL);
524 return(new);
525 }
526
527 /*
528 * -ls functions --
529 *
530 * Always true - prints the current entry to stdout in "ls" format.
531 */
532 /* ARGSUSED */
533 f_ls(plan, entry)
534 PLAN *plan;
535 FTSENT *entry;
536 {
537 printlong(entry->fts_path, entry->fts_accpath, &entry->fts_statb);
538 return(1);
539 }
540
541 PLAN *
542 c_ls()
543 {
544 ftsoptions &= ~FTS_NOSTAT;
545 isoutput = 1;
546
547 return(palloc(N_LS, f_ls));
548 }
549
550 /*
551 * -name functions --
552 *
553 * True if the basename of the filename being examined
554 * matches pattern using Pattern Matching Notation S3.14
555 */
556 f_name(plan, entry)
557 PLAN *plan;
558 FTSENT *entry;
559 {
560 return(!fnmatch(plan->c_data, entry->fts_name, 0));
561 }
562
563 PLAN *
564 c_name(pattern)
565 char *pattern;
566 {
567 PLAN *new;
568
569 new = palloc(N_NAME, f_name);
570 new->c_data = pattern;
571 return(new);
572 }
573
574 /*
575 * -newer file functions --
576 *
577 * True if the current file has been modified more recently
578 * then the modification time of the file named by the pathname
579 * file.
580 */
581 f_newer(plan, entry)
582 PLAN *plan;
583 FTSENT *entry;
584 {
585 return(entry->fts_statb.st_mtime > plan->t_data);
586 }
587
588 PLAN *
589 c_newer(filename)
590 char *filename;
591 {
592 PLAN *new;
593 struct stat sb;
594
595 ftsoptions &= ~FTS_NOSTAT;
596
597 if (stat(filename, &sb))
598 err("%s: %s", filename, strerror(errno));
599 new = palloc(N_NEWER, f_newer);
600 new->t_data = sb.st_mtime;
601 return(new);
602 }
603
604 /*
605 * -nogroup functions --
606 *
607 * True if file belongs to a user ID for which the equivalent
608 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
609 */
610 /* ARGSUSED */
611 f_nogroup(plan, entry)
612 PLAN *plan;
613 FTSENT *entry;
614 {
615 char *group_from_gid();
616
617 return(group_from_gid(entry->fts_statb.st_gid, 1) ? 1 : 0);
618 }
619
620 PLAN *
621 c_nogroup()
622 {
623 ftsoptions &= ~FTS_NOSTAT;
624
625 return(palloc(N_NOGROUP, f_nogroup));
626 }
627
628 /*
629 * -nouser functions --
630 *
631 * True if file belongs to a user ID for which the equivalent
632 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
633 */
634 /* ARGSUSED */
635 f_nouser(plan, entry)
636 PLAN *plan;
637 FTSENT *entry;
638 {
639 char *user_from_uid();
640
641 return(user_from_uid(entry->fts_statb.st_uid, 1) ? 1 : 0);
642 }
643
644 PLAN *
645 c_nouser()
646 {
647 ftsoptions &= ~FTS_NOSTAT;
648
649 return(palloc(N_NOUSER, f_nouser));
650 }
651
652 /*
653 * -perm functions --
654 *
655 * The mode argument is used to represent file mode bits. If it starts
656 * with a leading digit, it's treated as an octal mode, otherwise as a
657 * symbolic mode.
658 */
659 f_perm(plan, entry)
660 PLAN *plan;
661 FTSENT *entry;
662 {
663 mode_t mode;
664
665 mode = entry->fts_statb.st_mode &
666 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
667 if (plan->flags)
668 return((plan->m_data | mode) == mode);
669 else
670 return(mode == plan->m_data);
671 /* NOTREACHED */
672 }
673
674 PLAN *
675 c_perm(perm)
676 char *perm;
677 {
678 PLAN *new;
679 mode_t *set;
680
681 ftsoptions &= ~FTS_NOSTAT;
682
683 new = palloc(N_PERM, f_perm);
684
685 if (*perm == '-') {
686 new->flags = 1;
687 ++perm;
688 }
689
690 if ((set = setmode(perm)) == NULL)
691 err("%s: %s", "-perm", "illegal mode string");
692
693 new->m_data = getmode(set, 0);
694 return(new);
695 }
696
697 /*
698 * -print functions --
699 *
700 * Always true, causes the current pathame to be written to
701 * standard output.
702 */
703 /* ARGSUSED */
704 f_print(plan, entry)
705 PLAN *plan;
706 FTSENT *entry;
707 {
708 (void)printf("%s\n", entry->fts_path);
709 return(1);
710 }
711
712 PLAN *
713 c_print()
714 {
715 isoutput = 1;
716
717 return(palloc(N_PRINT, f_print));
718 }
719
720 /*
721 * -prune functions --
722 *
723 * Prune a portion of the hierarchy.
724 */
725 /* ARGSUSED */
726 f_prune(plan, entry)
727 PLAN *plan;
728 FTSENT *entry;
729 {
730 extern FTS *tree;
731
732 if (fts_set(tree, entry, FTS_SKIP))
733 err("%s: %s", entry->fts_path, strerror(errno));
734 return(1);
735 }
736
737 PLAN *
738 c_prune()
739 {
740 return(palloc(N_PRUNE, f_prune));
741 }
742
743 /*
744 * -size n[c] functions --
745 *
746 * True if the file size in bytes, divided by an implementation defined
747 * value and rounded up to the next integer, is n. If n is followed by
748 * a c, the size is in bytes.
749 */
750 #define FIND_SIZE 512
751 static int divsize = 1;
752
753 f_size(plan, entry)
754 PLAN *plan;
755 FTSENT *entry;
756 {
757 off_t size;
758
759 size = divsize ? (entry->fts_statb.st_size + FIND_SIZE - 1) /
760 FIND_SIZE : entry->fts_statb.st_size;
761 COMPARE(size, plan->o_data);
762 }
763
764 PLAN *
765 c_size(arg)
766 char *arg;
767 {
768 PLAN *new;
769 char endch;
770
771 ftsoptions &= ~FTS_NOSTAT;
772
773 new = palloc(N_SIZE, f_size);
774 new->o_data = find_parsenum(new, "-size", arg, &endch);
775 if (endch == 'c')
776 divsize = 0;
777 return(new);
778 }
779
780 /*
781 * -type c functions --
782 *
783 * True if the type of the file is c, where c is b, c, d, p, or f for
784 * block special file, character special file, directory, FIFO, or
785 * regular file, respectively.
786 */
787 f_type(plan, entry)
788 PLAN *plan;
789 FTSENT *entry;
790 {
791 return((entry->fts_statb.st_mode & S_IFMT) == plan->m_data);
792 }
793
794 PLAN *
795 c_type(typestring)
796 char *typestring;
797 {
798 PLAN *new;
799 mode_t mask;
800
801 ftsoptions &= ~FTS_NOSTAT;
802
803 switch (typestring[0]) {
804 case 'b':
805 mask = S_IFBLK;
806 break;
807 case 'c':
808 mask = S_IFCHR;
809 break;
810 case 'd':
811 mask = S_IFDIR;
812 break;
813 case 'f':
814 mask = S_IFREG;
815 break;
816 case 'l':
817 mask = S_IFLNK;
818 break;
819 case 'p':
820 mask = S_IFIFO;
821 break;
822 case 's':
823 mask = S_IFSOCK;
824 break;
825 default:
826 err("%s: %s", "-type", "unknown type");
827 }
828
829 new = palloc(N_TYPE, f_type);
830 new->m_data = mask;
831 return(new);
832 }
833
834 /*
835 * -user uname functions --
836 *
837 * True if the file belongs to the user uname. If uname is numeric and
838 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
839 * return a valid user name, uname is taken as a user ID.
840 */
841 f_user(plan, entry)
842 PLAN *plan;
843 FTSENT *entry;
844 {
845 return(entry->fts_statb.st_uid == plan->u_data);
846 }
847
848 PLAN *
849 c_user(username)
850 char *username;
851 {
852 PLAN *new;
853 struct passwd *p;
854 uid_t uid;
855
856 ftsoptions &= ~FTS_NOSTAT;
857
858 p = getpwnam(username);
859 if (p == NULL) {
860 uid = atoi(username);
861 if (uid == 0 && username[0] != '0')
862 err("%s: %s", "-user", "no such user");
863 } else
864 uid = p->pw_uid;
865
866 new = palloc(N_USER, f_user);
867 new->u_data = uid;
868 return(new);
869 }
870
871 /*
872 * -xdev functions --
873 *
874 * Always true, causes find not to decend past directories that have a
875 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
876 */
877 PLAN *
878 c_xdev()
879 {
880 ftsoptions |= FTS_XDEV;
881
882 return(palloc(N_XDEV, f_always_true));
883 }
884
885 /*
886 * ( expression ) functions --
887 *
888 * True if expression is true.
889 */
890 f_expr(plan, entry)
891 PLAN *plan;
892 FTSENT *entry;
893 {
894 register PLAN *p;
895 register int state;
896
897 for (p = plan->p_data[0];
898 p && (state = (p->eval)(p, entry)); p = p->next);
899 return(state);
900 }
901
902 /*
903 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
904 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
905 * to a N_EXPR node containing the expression and the ')' node is discarded.
906 */
907 PLAN *
908 c_openparen()
909 {
910 return(palloc(N_OPENPAREN, (int (*)())-1));
911 }
912
913 PLAN *
914 c_closeparen()
915 {
916 return(palloc(N_CLOSEPAREN, (int (*)())-1));
917 }
918
919 /*
920 * -mtime n functions --
921 *
922 * True if the difference between the file modification time and the
923 * current time is n 24 hour periods.
924 */
925 f_mtime(plan, entry)
926 PLAN *plan;
927 FTSENT *entry;
928 {
929 extern time_t now;
930
931 COMPARE((now - entry->fts_statb.st_mtime + SECSPERDAY - 1) /
932 SECSPERDAY, plan->t_data);
933 }
934
935 PLAN *
936 c_mtime(arg)
937 char *arg;
938 {
939 PLAN *new;
940
941 ftsoptions &= ~FTS_NOSTAT;
942
943 new = palloc(N_MTIME, f_mtime);
944 new->t_data = find_parsenum(new, "-mtime", arg, (char *)NULL);
945 return(new);
946 }
947
948 /*
949 * ! expression functions --
950 *
951 * Negation of a primary; the unary NOT operator.
952 */
953 f_not(plan, entry)
954 PLAN *plan;
955 FTSENT *entry;
956 {
957 register PLAN *p;
958 register int state;
959
960 for (p = plan->p_data[0];
961 p && (state = (p->eval)(p, entry)); p = p->next);
962 return(!state);
963 }
964
965 PLAN *
966 c_not()
967 {
968 return(palloc(N_NOT, f_not));
969 }
970
971 /*
972 * expression -o expression functions --
973 *
974 * Alternation of primaries; the OR operator. The second expression is
975 * not evaluated if the first expression is true.
976 */
977 f_or(plan, entry)
978 PLAN *plan;
979 FTSENT *entry;
980 {
981 register PLAN *p;
982 register int state;
983
984 for (p = plan->p_data[0];
985 p && (state = (p->eval)(p, entry)); p = p->next);
986
987 if (state)
988 return(1);
989
990 for (p = plan->p_data[1];
991 p && (state = (p->eval)(p, entry)); p = p->next);
992 return(state);
993 }
994
995 PLAN *
996 c_or()
997 {
998 return(palloc(N_OR, f_or));
999 }
1000
1001 static PLAN *
1002 palloc(t, f)
1003 enum ntype t;
1004 int (*f)();
1005 {
1006 PLAN *new;
1007
1008 if (new = malloc(sizeof(PLAN))) {
1009 new->type = t;
1010 new->eval = f;
1011 new->flags = 0;
1012 new->next = NULL;
1013 return(new);
1014 }
1015 err("%s", strerror(errno));
1016 /* NOTREACHED */
1017 }
1018