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