This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.bin / find / function.c
CommitLineData
15637ed4
RG
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
38static 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>
4ca3ede5 54#include <fnmatch.h>
15637ed4
RG
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
73static PLAN *palloc __P((enum ntype, int (*)()));
74
75/*
76 * find_parsenum --
77 * Parse a string of the form [+-]# and return the value.
78 */
79long
80find_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 */
123f_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
133PLAN *
134c_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 */
151f_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
161PLAN *
162c_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 */
182f_always_true(plan, entry)
183 PLAN *plan;
184 FTSENT *entry;
185{
186 return(1);
187}
188
189PLAN *
190c_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 */
209f_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 */
252PLAN *
253c_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 */
304PLAN *
305c_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 */
318f_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;
4ca3ede5 324 static struct statfs sb;
15637ed4
RG
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;
15637ed4
RG
358 }
359 return(plan->flags == MOUNT_NONE ?
4ca3ede5 360 sb.f_flags & plan->m_flags : sb.f_type == plan->flags);
15637ed4
RG
361}
362
363PLAN *
364c_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) {
4ca3ede5
C
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;
15637ed4
RG
383 case 'i':
384 if (!strcmp(arg, "isofs")) {
385 new->flags = MOUNT_ISOFS;
386 return(new);
387 }
388 break;
4ca3ede5
C
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;
15637ed4
RG
399 case 'l':
400 if (!strcmp(arg, "local")) {
401 new->flags = MOUNT_NONE;
4ca3ede5 402 new->m_flags = MNT_LOCAL;
15637ed4
RG
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;
4ca3ede5
C
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;
15637ed4
RG
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 */
447f_group(plan, entry)
448 PLAN *plan;
449 FTSENT *entry;
450{
451 return(entry->fts_statb.st_gid == plan->g_data);
452}
453
454PLAN *
455c_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 */
482f_inum(plan, entry)
483 PLAN *plan;
484 FTSENT *entry;
485{
486 COMPARE(entry->fts_statb.st_ino, plan->i_data);
487}
488
489PLAN *
490c_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 */
507f_links(plan, entry)
508 PLAN *plan;
509 FTSENT *entry;
510{
511 COMPARE(entry->fts_statb.st_nlink, plan->l_data);
512}
513
514PLAN *
515c_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 */
533f_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
541PLAN *
542c_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 */
556f_name(plan, entry)
557 PLAN *plan;
558 FTSENT *entry;
559{
4ca3ede5 560 return(!fnmatch(plan->c_data, entry->fts_name, 0));
15637ed4
RG
561}
562
563PLAN *
564c_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 */
581f_newer(plan, entry)
582 PLAN *plan;
583 FTSENT *entry;
584{
585 return(entry->fts_statb.st_mtime > plan->t_data);
586}
587
588PLAN *
589c_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 */
611f_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
620PLAN *
621c_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 */
635f_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
644PLAN *
645c_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 */
659f_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
674PLAN *
675c_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 */
704f_print(plan, entry)
705 PLAN *plan;
706 FTSENT *entry;
707{
708 (void)printf("%s\n", entry->fts_path);
709 return(1);
710}
711
712PLAN *
713c_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 */
726f_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
737PLAN *
738c_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
751static int divsize = 1;
752
753f_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
764PLAN *
765c_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 */
787f_type(plan, entry)
788 PLAN *plan;
789 FTSENT *entry;
790{
791 return((entry->fts_statb.st_mode & S_IFMT) == plan->m_data);
792}
793
794PLAN *
795c_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 */
841f_user(plan, entry)
842 PLAN *plan;
843 FTSENT *entry;
844{
845 return(entry->fts_statb.st_uid == plan->u_data);
846}
847
848PLAN *
849c_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 */
877PLAN *
878c_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 */
890f_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 */
907PLAN *
908c_openparen()
909{
910 return(palloc(N_OPENPAREN, (int (*)())-1));
911}
912
913PLAN *
914c_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 */
925f_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
935PLAN *
936c_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 */
953f_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
965PLAN *
966c_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 */
977f_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
995PLAN *
996c_or()
997{
998 return(palloc(N_OR, f_or));
999}
1000
1001static PLAN *
1002palloc(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}