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