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