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