function.c revision 1.9 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.9 1993/10/27 17:52:41 jtc 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 /* ARGSUSED */
714 f_print0(plan, entry)
715 PLAN *plan;
716 FTSENT *entry;
717 {
718 (void)fputs(entry->fts_path, stdout);
719 (void)fputc('\0', stdout);
720 return(1);
721 }
722
723 PLAN *
724 c_print()
725 {
726 isoutput = 1;
727
728 return(palloc(N_PRINT, f_print));
729 }
730
731 PLAN *
732 c_print0()
733 {
734 isoutput = 1;
735
736 return(palloc(N_PRINT0, f_print0));
737 }
738
739 /*
740 * -prune functions --
741 *
742 * Prune a portion of the hierarchy.
743 */
744 /* ARGSUSED */
745 f_prune(plan, entry)
746 PLAN *plan;
747 FTSENT *entry;
748 {
749 extern FTS *tree;
750
751 if (fts_set(tree, entry, FTS_SKIP))
752 err("%s: %s", entry->fts_path, strerror(errno));
753 return(1);
754 }
755
756 PLAN *
757 c_prune()
758 {
759 return(palloc(N_PRUNE, f_prune));
760 }
761
762 /*
763 * -size n[c] functions --
764 *
765 * True if the file size in bytes, divided by an implementation defined
766 * value and rounded up to the next integer, is n. If n is followed by
767 * a c, the size is in bytes.
768 */
769 #define FIND_SIZE 512
770 static int divsize = 1;
771
772 f_size(plan, entry)
773 PLAN *plan;
774 FTSENT *entry;
775 {
776 off_t size;
777
778 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
779 FIND_SIZE : entry->fts_statp->st_size;
780 COMPARE(size, plan->o_data);
781 }
782
783 PLAN *
784 c_size(arg)
785 char *arg;
786 {
787 PLAN *new;
788 char endch;
789
790 ftsoptions &= ~FTS_NOSTAT;
791
792 new = palloc(N_SIZE, f_size);
793 endch = 'c';
794 new->o_data = find_parsenum(new, "-size", arg, &endch);
795 if (endch == 'c')
796 divsize = 0;
797 return(new);
798 }
799
800 /*
801 * -type c functions --
802 *
803 * True if the type of the file is c, where c is b, c, d, p, or f for
804 * block special file, character special file, directory, FIFO, or
805 * regular file, respectively.
806 */
807 f_type(plan, entry)
808 PLAN *plan;
809 FTSENT *entry;
810 {
811 return((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
812 }
813
814 PLAN *
815 c_type(typestring)
816 char *typestring;
817 {
818 PLAN *new;
819 mode_t mask;
820
821 ftsoptions &= ~FTS_NOSTAT;
822
823 switch (typestring[0]) {
824 case 'b':
825 mask = S_IFBLK;
826 break;
827 case 'c':
828 mask = S_IFCHR;
829 break;
830 case 'd':
831 mask = S_IFDIR;
832 break;
833 case 'f':
834 mask = S_IFREG;
835 break;
836 case 'l':
837 mask = S_IFLNK;
838 break;
839 case 'p':
840 mask = S_IFIFO;
841 break;
842 case 's':
843 mask = S_IFSOCK;
844 break;
845 default:
846 err("%s: %s", "-type", "unknown type");
847 }
848
849 new = palloc(N_TYPE, f_type);
850 new->m_data = mask;
851 return(new);
852 }
853
854 /*
855 * -user uname functions --
856 *
857 * True if the file belongs to the user uname. If uname is numeric and
858 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
859 * return a valid user name, uname is taken as a user ID.
860 */
861 f_user(plan, entry)
862 PLAN *plan;
863 FTSENT *entry;
864 {
865 return(entry->fts_statp->st_uid == plan->u_data);
866 }
867
868 PLAN *
869 c_user(username)
870 char *username;
871 {
872 PLAN *new;
873 struct passwd *p;
874 uid_t uid;
875
876 ftsoptions &= ~FTS_NOSTAT;
877
878 p = getpwnam(username);
879 if (p == NULL) {
880 uid = atoi(username);
881 if (uid == 0 && username[0] != '0')
882 err("%s: %s", "-user", "no such user");
883 } else
884 uid = p->pw_uid;
885
886 new = palloc(N_USER, f_user);
887 new->u_data = uid;
888 return(new);
889 }
890
891 /*
892 * -xdev functions --
893 *
894 * Always true, causes find not to decend past directories that have a
895 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
896 */
897 PLAN *
898 c_xdev()
899 {
900 ftsoptions |= FTS_XDEV;
901
902 return(palloc(N_XDEV, f_always_true));
903 }
904
905 /*
906 * ( expression ) functions --
907 *
908 * True if expression is true.
909 */
910 f_expr(plan, entry)
911 PLAN *plan;
912 FTSENT *entry;
913 {
914 register PLAN *p;
915 register int state;
916
917 for (p = plan->p_data[0];
918 p && (state = (p->eval)(p, entry)); p = p->next);
919 return(state);
920 }
921
922 /*
923 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
924 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
925 * to a N_EXPR node containing the expression and the ')' node is discarded.
926 */
927 PLAN *
928 c_openparen()
929 {
930 return(palloc(N_OPENPAREN, (int (*)())-1));
931 }
932
933 PLAN *
934 c_closeparen()
935 {
936 return(palloc(N_CLOSEPAREN, (int (*)())-1));
937 }
938
939 /*
940 * -mtime n functions --
941 *
942 * True if the difference between the file modification time and the
943 * current time is n 24 hour periods.
944 */
945 f_mtime(plan, entry)
946 PLAN *plan;
947 FTSENT *entry;
948 {
949 extern time_t now;
950
951 COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
952 SECSPERDAY, plan->t_data);
953 }
954
955 PLAN *
956 c_mtime(arg)
957 char *arg;
958 {
959 PLAN *new;
960
961 ftsoptions &= ~FTS_NOSTAT;
962
963 new = palloc(N_MTIME, f_mtime);
964 new->t_data = find_parsenum(new, "-mtime", arg, (char *)NULL);
965 return(new);
966 }
967
968 /*
969 * ! expression functions --
970 *
971 * Negation of a primary; the unary NOT operator.
972 */
973 f_not(plan, entry)
974 PLAN *plan;
975 FTSENT *entry;
976 {
977 register PLAN *p;
978 register int state;
979
980 for (p = plan->p_data[0];
981 p && (state = (p->eval)(p, entry)); p = p->next);
982 return(!state);
983 }
984
985 PLAN *
986 c_not()
987 {
988 return(palloc(N_NOT, f_not));
989 }
990
991 /*
992 * expression -o expression functions --
993 *
994 * Alternation of primaries; the OR operator. The second expression is
995 * not evaluated if the first expression is true.
996 */
997 f_or(plan, entry)
998 PLAN *plan;
999 FTSENT *entry;
1000 {
1001 register PLAN *p;
1002 register int state;
1003
1004 for (p = plan->p_data[0];
1005 p && (state = (p->eval)(p, entry)); p = p->next);
1006
1007 if (state)
1008 return(1);
1009
1010 for (p = plan->p_data[1];
1011 p && (state = (p->eval)(p, entry)); p = p->next);
1012 return(state);
1013 }
1014
1015 PLAN *
1016 c_or()
1017 {
1018 return(palloc(N_OR, f_or));
1019 }
1020
1021 static PLAN *
1022 palloc(t, f)
1023 enum ntype t;
1024 int (*f)();
1025 {
1026 PLAN *new;
1027
1028 if (new = malloc(sizeof(PLAN))) {
1029 new->type = t;
1030 new->eval = f;
1031 new->flags = 0;
1032 new->next = NULL;
1033 return(new);
1034 }
1035 err("%s", strerror(errno));
1036 /* NOTREACHED */
1037 }
1038