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