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