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