function.c revision 1.56 1 /* $NetBSD: function.c,v 1.56 2006/10/07 17:04:02 apb 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. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "from: @(#)function.c 8.10 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: function.c,v 1.56 2006/10/07 17:04:02 apb Exp $");
41 #endif
42 #endif /* not lint */
43
44 #include <sys/param.h>
45 #include <sys/stat.h>
46 #include <sys/wait.h>
47 #include <sys/mount.h>
48
49 #include <dirent.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <fnmatch.h>
53 #include <fts.h>
54 #include <grp.h>
55 #include <inttypes.h>
56 #include <limits.h>
57 #include <pwd.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <tzfile.h>
62 #include <unistd.h>
63 #include <util.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 int64_t find_parsenum __P((PLAN *, char *, char *, char *));
82 static void run_f_exec __P((PLAN *));
83 int f_always_true __P((PLAN *, FTSENT *));
84 int f_amin __P((PLAN *, FTSENT *));
85 int f_anewer __P((PLAN *, FTSENT *));
86 int f_atime __P((PLAN *, FTSENT *));
87 int f_cmin __P((PLAN *, FTSENT *));
88 int f_cnewer __P((PLAN *, FTSENT *));
89 int f_ctime __P((PLAN *, FTSENT *));
90 int f_empty __P((PLAN *, FTSENT *));
91 int f_exec __P((PLAN *, FTSENT *));
92 int f_execdir __P((PLAN *, FTSENT *));
93 int f_false __P((PLAN *, FTSENT *));
94 int f_flags __P((PLAN *, FTSENT *));
95 int f_fprint __P((PLAN *, FTSENT *));
96 int f_fstype __P((PLAN *, FTSENT *));
97 int f_group __P((PLAN *, FTSENT *));
98 int f_iname __P((PLAN *, FTSENT *));
99 int f_inum __P((PLAN *, FTSENT *));
100 int f_links __P((PLAN *, FTSENT *));
101 int f_ls __P((PLAN *, FTSENT *));
102 int f_mindepth __P((PLAN *, FTSENT *));
103 int f_maxdepth __P((PLAN *, FTSENT *));
104 int f_mmin __P((PLAN *, FTSENT *));
105 int f_mtime __P((PLAN *, FTSENT *));
106 int f_name __P((PLAN *, FTSENT *));
107 int f_newer __P((PLAN *, FTSENT *));
108 int f_nogroup __P((PLAN *, FTSENT *));
109 int f_nouser __P((PLAN *, FTSENT *));
110 int f_path __P((PLAN *, FTSENT *));
111 int f_perm __P((PLAN *, FTSENT *));
112 int f_print __P((PLAN *, FTSENT *));
113 int f_print0 __P((PLAN *, FTSENT *));
114 int f_printx __P((PLAN *, FTSENT *));
115 int f_prune __P((PLAN *, FTSENT *));
116 int f_regex __P((PLAN *, FTSENT *));
117 int f_size __P((PLAN *, FTSENT *));
118 int f_type __P((PLAN *, FTSENT *));
119 int f_user __P((PLAN *, FTSENT *));
120 int f_not __P((PLAN *, FTSENT *));
121 int f_or __P((PLAN *, FTSENT *));
122 static PLAN *c_regex_common __P((char ***, int, enum ntype, int));
123 static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *))));
124
125 extern int dotfd;
126 extern FTS *tree;
127 extern time_t now;
128
129 /*
130 * find_parsenum --
131 * Parse a string of the form [+-]# and return the value.
132 */
133 static int64_t
134 find_parsenum(plan, option, vp, endch)
135 PLAN *plan;
136 char *option, *vp, *endch;
137 {
138 int64_t value;
139 char *endchar, *str; /* Pointer to character ending conversion. */
140
141 /* Determine comparison from leading + or -. */
142 str = vp;
143 switch (*str) {
144 case '+':
145 ++str;
146 plan->flags = F_GREATER;
147 break;
148 case '-':
149 ++str;
150 plan->flags = F_LESSTHAN;
151 break;
152 default:
153 plan->flags = F_EQUAL;
154 break;
155 }
156
157 /*
158 * Convert the string with strtol(). Note, if strtol() returns zero
159 * and endchar points to the beginning of the string we know we have
160 * a syntax error.
161 */
162 value = strtoq(str, &endchar, 10);
163 if (value == 0 && endchar == str)
164 errx(1, "%s: %s: illegal numeric value", option, vp);
165 if (endchar[0] && (endch == NULL || endchar[0] != *endch))
166 errx(1, "%s: %s: illegal trailing character", option, vp);
167 if (endch)
168 *endch = endchar[0];
169 return (value);
170 }
171
172 /*
173 * The value of n for the inode times (atime, ctime, and mtime) is a range,
174 * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with
175 * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
176 * user wanted. Correct so that -1 is "less than 1".
177 */
178 #define TIME_CORRECT(p, ttype) \
179 if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \
180 ++((p)->t_data);
181
182 /*
183 * -amin n functions --
184 *
185 * True if the difference between the file access time and the
186 * current time is n 1 minute periods.
187 */
188 int
189 f_amin(plan, entry)
190 PLAN *plan;
191 FTSENT *entry;
192 {
193 COMPARE((now - entry->fts_statp->st_atime +
194 SECSPERMIN - 1) / SECSPERMIN, plan->t_data);
195 }
196
197 PLAN *
198 c_amin(argvp, isok)
199 char ***argvp;
200 int isok;
201 {
202 char *arg = **argvp;
203 PLAN *new;
204
205 (*argvp)++;
206 ftsoptions &= ~FTS_NOSTAT;
207
208 new = palloc(N_AMIN, f_amin);
209 new->t_data = find_parsenum(new, "-amin", arg, NULL);
210 TIME_CORRECT(new, N_AMIN);
211 return (new);
212 }
213
214 /*
215 * -anewer file functions --
216 *
217 * True if the current file has been accessed more recently
218 * than the access time of the file named by the pathname
219 * file.
220 */
221 int
222 f_anewer(plan, entry)
223 PLAN *plan;
224 FTSENT *entry;
225 {
226
227 return (entry->fts_statp->st_atime > plan->t_data);
228 }
229
230 PLAN *
231 c_anewer(argvp, isok)
232 char ***argvp;
233 int isok;
234 {
235 char *filename = **argvp;
236 PLAN *new;
237 struct stat sb;
238
239 (*argvp)++;
240 ftsoptions &= ~FTS_NOSTAT;
241
242 if (stat(filename, &sb))
243 err(1, "%s", filename);
244 new = palloc(N_ANEWER, f_anewer);
245 new->t_data = sb.st_atime;
246 return (new);
247 }
248
249 /*
250 * -atime n functions --
251 *
252 * True if the difference between the file access time and the
253 * current time is n 24 hour periods.
254 */
255 int
256 f_atime(plan, entry)
257 PLAN *plan;
258 FTSENT *entry;
259 {
260 COMPARE((now - entry->fts_statp->st_atime +
261 SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
262 }
263
264 PLAN *
265 c_atime(argvp, isok)
266 char ***argvp;
267 int isok;
268 {
269 char *arg = **argvp;
270 PLAN *new;
271
272 (*argvp)++;
273 ftsoptions &= ~FTS_NOSTAT;
274
275 new = palloc(N_ATIME, f_atime);
276 new->t_data = find_parsenum(new, "-atime", arg, NULL);
277 TIME_CORRECT(new, N_ATIME);
278 return (new);
279 }
280 /*
281 * -cmin n functions --
282 *
283 * True if the difference between the last change of file
284 * status information and the current time is n 24 hour periods.
285 */
286 int
287 f_cmin(plan, entry)
288 PLAN *plan;
289 FTSENT *entry;
290 {
291 COMPARE((now - entry->fts_statp->st_ctime +
292 SECSPERMIN - 1) / SECSPERMIN, plan->t_data);
293 }
294
295 PLAN *
296 c_cmin(argvp, isok)
297 char ***argvp;
298 int isok;
299 {
300 char *arg = **argvp;
301 PLAN *new;
302
303 (*argvp)++;
304 ftsoptions &= ~FTS_NOSTAT;
305
306 new = palloc(N_CMIN, f_cmin);
307 new->t_data = find_parsenum(new, "-cmin", arg, NULL);
308 TIME_CORRECT(new, N_CMIN);
309 return (new);
310 }
311
312 /*
313 * -cnewer file functions --
314 *
315 * True if the current file has been changed more recently
316 * than the changed time of the file named by the pathname
317 * file.
318 */
319 int
320 f_cnewer(plan, entry)
321 PLAN *plan;
322 FTSENT *entry;
323 {
324
325 return (entry->fts_statp->st_ctime > plan->t_data);
326 }
327
328 PLAN *
329 c_cnewer(argvp, isok)
330 char ***argvp;
331 int isok;
332 {
333 char *filename = **argvp;
334 PLAN *new;
335 struct stat sb;
336
337 (*argvp)++;
338 ftsoptions &= ~FTS_NOSTAT;
339
340 if (stat(filename, &sb))
341 err(1, "%s", filename);
342 new = palloc(N_CNEWER, f_cnewer);
343 new->t_data = sb.st_ctime;
344 return (new);
345 }
346
347 /*
348 * -ctime n functions --
349 *
350 * True if the difference between the last change of file
351 * status information and the current time is n 24 hour periods.
352 */
353 int
354 f_ctime(plan, entry)
355 PLAN *plan;
356 FTSENT *entry;
357 {
358 COMPARE((now - entry->fts_statp->st_ctime +
359 SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
360 }
361
362 PLAN *
363 c_ctime(argvp, isok)
364 char ***argvp;
365 int isok;
366 {
367 char *arg = **argvp;
368 PLAN *new;
369
370 (*argvp)++;
371 ftsoptions &= ~FTS_NOSTAT;
372
373 new = palloc(N_CTIME, f_ctime);
374 new->t_data = find_parsenum(new, "-ctime", arg, NULL);
375 TIME_CORRECT(new, N_CTIME);
376 return (new);
377 }
378
379 /*
380 * -depth functions --
381 *
382 * Always true, causes descent of the directory hierarchy to be done
383 * so that all entries in a directory are acted on before the directory
384 * itself.
385 */
386 int
387 f_always_true(plan, entry)
388 PLAN *plan;
389 FTSENT *entry;
390 {
391
392 return (1);
393 }
394
395 PLAN *
396 c_depth(argvp, isok)
397 char ***argvp;
398 int isok;
399 {
400 isdepth = 1;
401
402 return (palloc(N_DEPTH, f_always_true));
403 }
404
405 /*
406 * -empty functions --
407 *
408 * True if the file or directory is empty
409 */
410 int
411 f_empty(plan, entry)
412 PLAN *plan;
413 FTSENT *entry;
414 {
415 if (S_ISREG(entry->fts_statp->st_mode) &&
416 entry->fts_statp->st_size == 0)
417 return (1);
418 if (S_ISDIR(entry->fts_statp->st_mode)) {
419 struct dirent *dp;
420 int empty;
421 DIR *dir;
422
423 empty = 1;
424 dir = opendir(entry->fts_accpath);
425 if (dir == NULL)
426 err(1, "%s", entry->fts_accpath);
427 for (dp = readdir(dir); dp; dp = readdir(dir))
428 if (dp->d_name[0] != '.' ||
429 (dp->d_name[1] != '\0' &&
430 (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
431 empty = 0;
432 break;
433 }
434 closedir(dir);
435 return (empty);
436 }
437 return (0);
438 }
439
440 PLAN *
441 c_empty(argvp, isok)
442 char ***argvp;
443 int isok;
444 {
445 ftsoptions &= ~FTS_NOSTAT;
446
447 return (palloc(N_EMPTY, f_empty));
448 }
449
450 /*
451 * [-exec | -ok] utility [arg ... ] ; functions --
452 * [-exec | -ok] utility [arg ... ] {} + functions --
453 *
454 * If the end of the primary expression is delimited by a
455 * semicolon: true if the executed utility returns a zero value
456 * as exit status. If "{}" occurs anywhere, it gets replaced by
457 * the current pathname.
458 *
459 * If the end of the primary expression is delimited by a plus
460 * sign: always true. Pathnames for which the primary is
461 * evaluated shall be aggregated into sets. The utility will be
462 * executed once per set, with "{}" replaced by the entire set of
463 * pathnames (as if xargs). "{}" must appear last.
464 *
465 * The current directory for the execution of utility is the same
466 * as the current directory when the find utility was started.
467 *
468 * The primary -ok is different in that it requests affirmation
469 * of the user before executing the utility.
470 */
471 int
472 f_exec(plan, entry)
473 PLAN *plan;
474 FTSENT *entry;
475 {
476 int cnt, l;
477 pid_t pid;
478 int status;
479
480 if (plan->flags & F_PLUSSET) {
481 /*
482 * Confirm sufficient buffer space, then copy the path
483 * to the buffer.
484 */
485 l = strlen(entry->fts_path);
486 if (plan->ep_p + l < plan->ep_ebp) {
487 plan->ep_bxp[plan->ep_narg++] =
488 strcpy(plan->ep_p, entry->fts_path);
489 plan->ep_p += l + 1;
490
491 if (plan->ep_narg == plan->ep_maxargs)
492 run_f_exec(plan);
493 } else {
494 /*
495 * Without sufficient space to copy in the next
496 * argument, run the command to empty out the
497 * buffer before re-attepting the copy.
498 */
499 run_f_exec(plan);
500 if ((plan->ep_p + l < plan->ep_ebp)) {
501 plan->ep_bxp[plan->ep_narg++]
502 = strcpy(plan->ep_p, entry->fts_path);
503 plan->ep_p += l + 1;
504 } else
505 errx(1, "insufficient space for argument");
506 }
507 return (1);
508 } else {
509 for (cnt = 0; plan->e_argv[cnt]; ++cnt)
510 if (plan->e_len[cnt])
511 brace_subst(plan->e_orig[cnt],
512 &plan->e_argv[cnt],
513 entry->fts_path,
514 &plan->e_len[cnt]);
515 if (plan->flags & F_NEEDOK && !queryuser(plan->e_argv))
516 return (0);
517
518 /* Don't mix output of command with find output. */
519 fflush(stdout);
520 fflush(stderr);
521
522 switch (pid = vfork()) {
523 case -1:
524 err(1, "vfork");
525 /* NOTREACHED */
526 case 0:
527 if (fchdir(dotfd)) {
528 warn("chdir");
529 _exit(1);
530 }
531 execvp(plan->e_argv[0], plan->e_argv);
532 warn("%s", plan->e_argv[0]);
533 _exit(1);
534 }
535 pid = waitpid(pid, &status, 0);
536 return (pid != -1 && WIFEXITED(status)
537 && !WEXITSTATUS(status));
538 }
539 }
540
541 static void
542 run_f_exec(plan)
543 PLAN *plan;
544 {
545 pid_t pid;
546 int rval, status;
547
548 /* Ensure arg list is null terminated. */
549 plan->ep_bxp[plan->ep_narg] = NULL;
550
551 /* Don't mix output of command with find output. */
552 fflush(stdout);
553 fflush(stderr);
554
555 switch (pid = vfork()) {
556 case -1:
557 err(1, "vfork");
558 /* NOTREACHED */
559 case 0:
560 if (fchdir(dotfd)) {
561 warn("chdir");
562 _exit(1);
563 }
564 execvp(plan->e_argv[0], plan->e_argv);
565 warn("%s", plan->e_argv[0]);
566 _exit(1);
567 }
568
569 /* Clear out the argument list. */
570 plan->ep_narg = 0;
571 plan->ep_bxp[plan->ep_narg] = NULL;
572 /* As well as the argument buffer. */
573 plan->ep_p = plan->ep_bbp;
574 *plan->ep_p = '\0';
575
576 pid = waitpid(pid, &status, 0);
577 if (WIFEXITED(status))
578 rval = WEXITSTATUS(status);
579 else
580 rval = -1;
581
582 /*
583 * If we have a non-zero exit status, preserve it so find(1) can
584 * later exit with it.
585 */
586 if (rval)
587 plan->ep_rval = rval;
588 }
589
590 /*
591 * c_exec --
592 * build three parallel arrays, one with pointers to the strings passed
593 * on the command line, one with (possibly duplicated) pointers to the
594 * argv array, and one with integer values that are lengths of the
595 * strings, but also flags meaning that the string has to be massaged.
596 *
597 * If -exec ... {} +, use only the first array, but make it large
598 * enough to hold 5000 args (cf. src/usr.bin/xargs/xargs.c for a
599 * discussion), and then allocate ARG_MAX - 4K of space for args.
600 */
601 PLAN *
602 c_exec(argvp, isok)
603 char ***argvp;
604 int isok;
605 {
606 PLAN *new; /* node returned */
607 int cnt, brace, lastbrace;
608 char **argv, **ap, *p;
609
610 isoutput = 1;
611
612 new = palloc(N_EXEC, f_exec);
613 if (isok)
614 new->flags |= F_NEEDOK;
615
616 /*
617 * Terminate if we encounter an arg exacty equal to ";", or an
618 * arg exacty equal to "+" following an arg exacty equal to
619 * "{}".
620 */
621 for (ap = argv = *argvp, brace = 0;; ++ap) {
622 if (!*ap)
623 errx(1, "%s: no terminating \";\" or \"+\"",
624 isok ? "-ok" : "-exec");
625 lastbrace = brace;
626 if (strcmp(*ap, "{}") == 0)
627 brace = 1;
628 if (strcmp(*ap, ";") == 0)
629 break;
630 if (strcmp(*ap, "+") == 0 && lastbrace) {
631 new->flags |= F_PLUSSET;
632 break;
633 }
634 }
635
636 /*
637 * POSIX says -ok ... {} + "need not be supported," and it does
638 * not make much sense anyway.
639 */
640 if (new->flags & F_NEEDOK && new->flags & F_PLUSSET)
641 errx(1, "-ok: terminating \"+\" not permitted.");
642
643 if (new->flags & F_PLUSSET) {
644 u_int c, bufsize;
645
646 cnt = ap - *argvp - 1; /* units are words */
647 new->ep_maxargs = 5000;
648 new->e_argv = (char **)emalloc((u_int)(cnt + new->ep_maxargs)
649 * sizeof(char **));
650
651 /* We start stuffing arguments after the user's last one. */
652 new->ep_bxp = &new->e_argv[cnt];
653 new->ep_narg = 0;
654
655 /*
656 * Count up the space of the user's arguments, and
657 * subtract that from what we allocate.
658 */
659 for (argv = *argvp, c = 0, cnt = 0;
660 argv < ap;
661 ++argv, ++cnt) {
662 c += strlen(*argv) + 1;
663 new->e_argv[cnt] = *argv;
664 }
665 bufsize = ARG_MAX - 4 * 1024 - c;
666
667
668 /*
669 * Allocate, and then initialize current, base, and
670 * end pointers.
671 */
672 new->ep_p = new->ep_bbp = malloc(bufsize + 1);
673 new->ep_ebp = new->ep_bbp + bufsize - 1;
674 new->ep_rval = 0;
675 } else { /* !F_PLUSSET */
676 cnt = ap - *argvp + 1;
677 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
678 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
679 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
680
681 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
682 new->e_orig[cnt] = *argv;
683 for (p = *argv; *p; ++p)
684 if (p[0] == '{' && p[1] == '}') {
685 new->e_argv[cnt] =
686 emalloc((u_int)MAXPATHLEN);
687 new->e_len[cnt] = MAXPATHLEN;
688 break;
689 }
690 if (!*p) {
691 new->e_argv[cnt] = *argv;
692 new->e_len[cnt] = 0;
693 }
694 }
695 new->e_orig[cnt] = NULL;
696 }
697
698 new->e_argv[cnt] = NULL;
699 *argvp = argv + 1;
700 return (new);
701 }
702
703 /*
704 * -execdir utility [arg ... ] ; functions --
705 *
706 * True if the executed utility returns a zero value as exit status.
707 * The end of the primary expression is delimited by a semicolon. If
708 * "{}" occurs anywhere, it gets replaced by the unqualified pathname.
709 * The current directory for the execution of utility is the same as
710 * the directory where the file lives.
711 */
712 int
713 f_execdir(plan, entry)
714 PLAN *plan;
715 FTSENT *entry;
716 {
717 int cnt;
718 pid_t pid;
719 int status;
720 char *file;
721
722 /* XXX - if file/dir ends in '/' this will not work -- can it? */
723 if ((file = strrchr(entry->fts_path, '/')))
724 file++;
725 else
726 file = entry->fts_path;
727
728 for (cnt = 0; plan->e_argv[cnt]; ++cnt)
729 if (plan->e_len[cnt])
730 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
731 file, &plan->e_len[cnt]);
732
733 /* don't mix output of command with find output */
734 fflush(stdout);
735 fflush(stderr);
736
737 switch (pid = vfork()) {
738 case -1:
739 err(1, "fork");
740 /* NOTREACHED */
741 case 0:
742 execvp(plan->e_argv[0], plan->e_argv);
743 warn("%s", plan->e_argv[0]);
744 _exit(1);
745 }
746 pid = waitpid(pid, &status, 0);
747 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
748 }
749
750 /*
751 * c_execdir --
752 * build three parallel arrays, one with pointers to the strings passed
753 * on the command line, one with (possibly duplicated) pointers to the
754 * argv array, and one with integer values that are lengths of the
755 * strings, but also flags meaning that the string has to be massaged.
756 */
757 PLAN *
758 c_execdir(argvp, isok)
759 char ***argvp;
760 int isok;
761 {
762 PLAN *new; /* node returned */
763 int cnt;
764 char **argv, **ap, *p;
765
766 ftsoptions &= ~FTS_NOSTAT;
767 isoutput = 1;
768
769 new = palloc(N_EXECDIR, f_execdir);
770
771 for (ap = argv = *argvp;; ++ap) {
772 if (!*ap)
773 errx(1,
774 "-execdir: no terminating \";\"");
775 if (**ap == ';')
776 break;
777 }
778
779 cnt = ap - *argvp + 1;
780 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
781 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
782 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
783
784 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
785 new->e_orig[cnt] = *argv;
786 for (p = *argv; *p; ++p)
787 if (p[0] == '{' && p[1] == '}') {
788 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
789 new->e_len[cnt] = MAXPATHLEN;
790 break;
791 }
792 if (!*p) {
793 new->e_argv[cnt] = *argv;
794 new->e_len[cnt] = 0;
795 }
796 }
797 new->e_argv[cnt] = new->e_orig[cnt] = NULL;
798
799 *argvp = argv + 1;
800 return (new);
801 }
802
803 PLAN *
804 c_exit(argvp, isok)
805 char ***argvp;
806 int isok;
807 {
808 char *arg = **argvp;
809 PLAN *new;
810
811 /* not technically true, but otherwise '-print' is implied */
812 isoutput = 1;
813
814 new = palloc(N_EXIT, f_always_true);
815
816 if (arg) {
817 (*argvp)++;
818 new->exit_val = find_parsenum(new, "-exit", arg, NULL);
819 } else
820 new->exit_val = 0;
821
822 return (new);
823 }
824
825
826 /*
827 * -false function
828 */
829 int
830 f_false(plan, entry)
831 PLAN *plan;
832 FTSENT *entry;
833 {
834
835 return (0);
836 }
837
838 PLAN *
839 c_false(argvp, isok)
840 char ***argvp;
841 int isok;
842 {
843 return (palloc(N_FALSE, f_false));
844 }
845
846
847 /*
848 * -flags [-]flags functions --
849 */
850 int
851 f_flags(plan, entry)
852 PLAN *plan;
853 FTSENT *entry;
854 {
855 u_int32_t flags;
856
857 flags = entry->fts_statp->st_flags;
858 if (plan->flags == F_ATLEAST)
859 return ((plan->f_data | flags) == flags);
860 else
861 return (flags == plan->f_data);
862 /* NOTREACHED */
863 }
864
865 PLAN *
866 c_flags(argvp, isok)
867 char ***argvp;
868 int isok;
869 {
870 char *flags = **argvp;
871 PLAN *new;
872 u_long flagset;
873
874 (*argvp)++;
875 ftsoptions &= ~FTS_NOSTAT;
876
877 new = palloc(N_FLAGS, f_flags);
878
879 if (*flags == '-') {
880 new->flags = F_ATLEAST;
881 ++flags;
882 }
883
884 flagset = 0;
885 if ((strcmp(flags, "none") != 0) &&
886 (string_to_flags(&flags, &flagset, NULL) != 0))
887 errx(1, "-flags: %s: illegal flags string", flags);
888 new->f_data = flagset;
889 return (new);
890 }
891
892 /*
893 * -follow functions --
894 *
895 * Always true, causes symbolic links to be followed on a global
896 * basis.
897 */
898 PLAN *
899 c_follow(argvp, isok)
900 char ***argvp;
901 int isok;
902 {
903 ftsoptions &= ~FTS_PHYSICAL;
904 ftsoptions |= FTS_LOGICAL;
905
906 return (palloc(N_FOLLOW, f_always_true));
907 }
908
909 /* -fprint functions --
910 *
911 * Causes the current pathame to be written to the defined output file.
912 */
913 int
914 f_fprint(plan, entry)
915 PLAN *plan;
916 FTSENT *entry;
917 {
918
919 if (-1 == fprintf(plan->fprint_file, "%s\n", entry->fts_path))
920 warn("fprintf");
921
922 return(1);
923
924 /* no descriptors are closed; they will be closed by
925 operating system when this find command exits. */
926 }
927
928 PLAN *
929 c_fprint(argvp, isok)
930 char ***argvp;
931 int isok;
932 {
933 PLAN *new;
934
935 isoutput = 1; /* do not assume -print */
936
937 new = palloc(N_FPRINT, f_fprint);
938
939 if (NULL == (new->fprint_file = fopen(**argvp, "w")))
940 err(1, "-fprint: %s: cannot create file", **argvp);
941
942 (*argvp)++;
943 return (new);
944 }
945
946 /*
947 * -fstype functions --
948 *
949 * True if the file is of a certain type.
950 */
951 int
952 f_fstype(plan, entry)
953 PLAN *plan;
954 FTSENT *entry;
955 {
956 static dev_t curdev; /* need a guaranteed illegal dev value */
957 static int first = 1;
958 struct statvfs sb;
959 static short val;
960 static char fstype[MFSNAMELEN];
961 char *p, save[2];
962
963 memset(&save, 0, sizeof save); /* XXX gcc */
964
965 /* Only check when we cross mount point. */
966 if (first || curdev != entry->fts_statp->st_dev) {
967 curdev = entry->fts_statp->st_dev;
968
969 /*
970 * Statfs follows symlinks; find wants the link's file system,
971 * not where it points.
972 */
973 if (entry->fts_info == FTS_SL ||
974 entry->fts_info == FTS_SLNONE) {
975 if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
976 ++p;
977 else
978 p = entry->fts_accpath;
979 save[0] = p[0];
980 p[0] = '.';
981 save[1] = p[1];
982 p[1] = '\0';
983
984 } else
985 p = NULL;
986
987 if (statvfs(entry->fts_accpath, &sb))
988 err(1, "%s", entry->fts_accpath);
989
990 if (p) {
991 p[0] = save[0];
992 p[1] = save[1];
993 }
994
995 first = 0;
996
997 /*
998 * Further tests may need both of these values, so
999 * always copy both of them.
1000 */
1001 val = sb.f_flag;
1002 strlcpy(fstype, sb.f_fstypename, sizeof(fstype));
1003 }
1004 switch (plan->flags) {
1005 case F_MTFLAG:
1006 return (val & plan->mt_data);
1007 case F_MTTYPE:
1008 return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
1009 default:
1010 abort();
1011 }
1012 }
1013
1014 PLAN *
1015 c_fstype(argvp, isok)
1016 char ***argvp;
1017 int isok;
1018 {
1019 char *arg = **argvp;
1020 PLAN *new;
1021
1022 (*argvp)++;
1023 ftsoptions &= ~FTS_NOSTAT;
1024
1025 new = palloc(N_FSTYPE, f_fstype);
1026
1027 switch (*arg) {
1028 case 'l':
1029 if (!strcmp(arg, "local")) {
1030 new->flags = F_MTFLAG;
1031 new->mt_data = MNT_LOCAL;
1032 return (new);
1033 }
1034 break;
1035 case 'r':
1036 if (!strcmp(arg, "rdonly")) {
1037 new->flags = F_MTFLAG;
1038 new->mt_data = MNT_RDONLY;
1039 return (new);
1040 }
1041 break;
1042 }
1043
1044 new->flags = F_MTTYPE;
1045 new->c_data = arg;
1046 return (new);
1047 }
1048
1049 /*
1050 * -group gname functions --
1051 *
1052 * True if the file belongs to the group gname. If gname is numeric and
1053 * an equivalent of the getgrnam() function does not return a valid group
1054 * name, gname is taken as a group ID.
1055 */
1056 int
1057 f_group(plan, entry)
1058 PLAN *plan;
1059 FTSENT *entry;
1060 {
1061
1062 return (entry->fts_statp->st_gid == plan->g_data);
1063 }
1064
1065 PLAN *
1066 c_group(argvp, isok)
1067 char ***argvp;
1068 int isok;
1069 {
1070 char *gname = **argvp;
1071 PLAN *new;
1072 struct group *g;
1073 gid_t gid;
1074
1075 (*argvp)++;
1076 ftsoptions &= ~FTS_NOSTAT;
1077
1078 g = getgrnam(gname);
1079 if (g == NULL) {
1080 gid = atoi(gname);
1081 if (gid == 0 && gname[0] != '0')
1082 errx(1, "-group: %s: no such group", gname);
1083 } else
1084 gid = g->gr_gid;
1085
1086 new = palloc(N_GROUP, f_group);
1087 new->g_data = gid;
1088 return (new);
1089 }
1090
1091 /*
1092 * -inum n functions --
1093 *
1094 * True if the file has inode # n.
1095 */
1096 int
1097 f_inum(plan, entry)
1098 PLAN *plan;
1099 FTSENT *entry;
1100 {
1101
1102 COMPARE(entry->fts_statp->st_ino, plan->i_data);
1103 }
1104
1105 PLAN *
1106 c_inum(argvp, isok)
1107 char ***argvp;
1108 int isok;
1109 {
1110 char *arg = **argvp;
1111 PLAN *new;
1112
1113 (*argvp)++;
1114 ftsoptions &= ~FTS_NOSTAT;
1115
1116 new = palloc(N_INUM, f_inum);
1117 new->i_data = find_parsenum(new, "-inum", arg, NULL);
1118 return (new);
1119 }
1120
1121 /*
1122 * -links n functions --
1123 *
1124 * True if the file has n links.
1125 */
1126 int
1127 f_links(plan, entry)
1128 PLAN *plan;
1129 FTSENT *entry;
1130 {
1131
1132 COMPARE(entry->fts_statp->st_nlink, plan->l_data);
1133 }
1134
1135 PLAN *
1136 c_links(argvp, isok)
1137 char ***argvp;
1138 int isok;
1139 {
1140 char *arg = **argvp;
1141 PLAN *new;
1142
1143 (*argvp)++;
1144 ftsoptions &= ~FTS_NOSTAT;
1145
1146 new = palloc(N_LINKS, f_links);
1147 new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
1148 return (new);
1149 }
1150
1151 /*
1152 * -ls functions --
1153 *
1154 * Always true - prints the current entry to stdout in "ls" format.
1155 */
1156 int
1157 f_ls(plan, entry)
1158 PLAN *plan;
1159 FTSENT *entry;
1160 {
1161
1162 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
1163 return (1);
1164 }
1165
1166 PLAN *
1167 c_ls(argvp, isok)
1168 char ***argvp;
1169 int isok;
1170 {
1171
1172 ftsoptions &= ~FTS_NOSTAT;
1173 isoutput = 1;
1174
1175 return (palloc(N_LS, f_ls));
1176 }
1177
1178 /*
1179 * - maxdepth n functions --
1180 *
1181 * True if the current search depth is less than or equal to the
1182 * maximum depth specified
1183 */
1184 int
1185 f_maxdepth(plan, entry)
1186 PLAN *plan;
1187 FTSENT *entry;
1188 {
1189 extern FTS *tree;
1190
1191 if (entry->fts_level >= plan->max_data)
1192 fts_set(tree, entry, FTS_SKIP);
1193 return (entry->fts_level <= plan->max_data);
1194 }
1195
1196 PLAN *
1197 c_maxdepth(argvp, isok)
1198 char ***argvp;
1199 int isok;
1200 {
1201 char *arg = **argvp;
1202 PLAN *new;
1203
1204 (*argvp)++;
1205 new = palloc(N_MAXDEPTH, f_maxdepth);
1206 new->max_data = atoi(arg);
1207 return (new);
1208 }
1209
1210 /*
1211 * - mindepth n functions --
1212 *
1213 * True if the current search depth is greater than or equal to the
1214 * minimum depth specified
1215 */
1216 int
1217 f_mindepth(plan, entry)
1218 PLAN *plan;
1219 FTSENT *entry;
1220 {
1221 return (entry->fts_level >= plan->min_data);
1222 }
1223
1224 PLAN *
1225 c_mindepth(argvp, isok)
1226 char ***argvp;
1227 int isok;
1228 {
1229 char *arg = **argvp;
1230 PLAN *new;
1231
1232 (*argvp)++;
1233 new = palloc(N_MINDEPTH, f_mindepth);
1234 new->min_data = atoi(arg);
1235 return (new);
1236 }
1237 /*
1238 * -mmin n functions --
1239 *
1240 * True if the difference between the file modification time and the
1241 * current time is n 24 hour periods.
1242 */
1243 int
1244 f_mmin(plan, entry)
1245 PLAN *plan;
1246 FTSENT *entry;
1247 {
1248 COMPARE((now - entry->fts_statp->st_mtime + SECSPERMIN - 1) /
1249 SECSPERMIN, plan->t_data);
1250 }
1251
1252 PLAN *
1253 c_mmin(argvp, isok)
1254 char ***argvp;
1255 int isok;
1256 {
1257 char *arg = **argvp;
1258 PLAN *new;
1259
1260 (*argvp)++;
1261 ftsoptions &= ~FTS_NOSTAT;
1262
1263 new = palloc(N_MMIN, f_mmin);
1264 new->t_data = find_parsenum(new, "-mmin", arg, NULL);
1265 TIME_CORRECT(new, N_MMIN);
1266 return (new);
1267 }
1268 /*
1269 * -mtime n functions --
1270 *
1271 * True if the difference between the file modification time and the
1272 * current time is n 24 hour periods.
1273 */
1274 int
1275 f_mtime(plan, entry)
1276 PLAN *plan;
1277 FTSENT *entry;
1278 {
1279 COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
1280 SECSPERDAY, plan->t_data);
1281 }
1282
1283 PLAN *
1284 c_mtime(argvp, isok)
1285 char ***argvp;
1286 int isok;
1287 {
1288 char *arg = **argvp;
1289 PLAN *new;
1290
1291 (*argvp)++;
1292 ftsoptions &= ~FTS_NOSTAT;
1293
1294 new = palloc(N_MTIME, f_mtime);
1295 new->t_data = find_parsenum(new, "-mtime", arg, NULL);
1296 TIME_CORRECT(new, N_MTIME);
1297 return (new);
1298 }
1299
1300 /*
1301 * -name functions --
1302 *
1303 * True if the basename of the filename being examined
1304 * matches pattern using Pattern Matching Notation S3.14
1305 */
1306 int
1307 f_name(plan, entry)
1308 PLAN *plan;
1309 FTSENT *entry;
1310 {
1311
1312 return (!fnmatch(plan->c_data, entry->fts_name, 0));
1313 }
1314
1315 PLAN *
1316 c_name(argvp, isok)
1317 char ***argvp;
1318 int isok;
1319 {
1320 char *pattern = **argvp;
1321 PLAN *new;
1322
1323 (*argvp)++;
1324 new = palloc(N_NAME, f_name);
1325 new->c_data = pattern;
1326 return (new);
1327 }
1328
1329 /*
1330 * -iname functions --
1331 *
1332 * Similar to -name, but does case insensitive matching
1333 *
1334 */
1335 int
1336 f_iname(plan, entry)
1337 PLAN *plan;
1338 FTSENT *entry;
1339 {
1340 return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD));
1341 }
1342
1343 PLAN *
1344 c_iname(argvp, isok)
1345 char ***argvp;
1346 int isok;
1347 {
1348 char *pattern = **argvp;
1349 PLAN *new;
1350
1351 (*argvp)++;
1352 new = palloc(N_INAME, f_iname);
1353 new->c_data = pattern;
1354 return (new);
1355 }
1356
1357 /*
1358 * -newer file functions --
1359 *
1360 * True if the current file has been modified more recently
1361 * than the modification time of the file named by the pathname
1362 * file.
1363 */
1364 int
1365 f_newer(plan, entry)
1366 PLAN *plan;
1367 FTSENT *entry;
1368 {
1369
1370 return (entry->fts_statp->st_mtime > plan->t_data);
1371 }
1372
1373 PLAN *
1374 c_newer(argvp, isok)
1375 char ***argvp;
1376 int isok;
1377 {
1378 char *filename = **argvp;
1379 PLAN *new;
1380 struct stat sb;
1381
1382 (*argvp)++;
1383 ftsoptions &= ~FTS_NOSTAT;
1384
1385 if (stat(filename, &sb))
1386 err(1, "%s", filename);
1387 new = palloc(N_NEWER, f_newer);
1388 new->t_data = sb.st_mtime;
1389 return (new);
1390 }
1391
1392 /*
1393 * -nogroup functions --
1394 *
1395 * True if file belongs to a user ID for which the equivalent
1396 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
1397 */
1398 int
1399 f_nogroup(plan, entry)
1400 PLAN *plan;
1401 FTSENT *entry;
1402 {
1403
1404 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
1405 }
1406
1407 PLAN *
1408 c_nogroup(argvp, isok)
1409 char ***argvp;
1410 int isok;
1411 {
1412 ftsoptions &= ~FTS_NOSTAT;
1413
1414 return (palloc(N_NOGROUP, f_nogroup));
1415 }
1416
1417 /*
1418 * -nouser functions --
1419 *
1420 * True if file belongs to a user ID for which the equivalent
1421 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
1422 */
1423 int
1424 f_nouser(plan, entry)
1425 PLAN *plan;
1426 FTSENT *entry;
1427 {
1428
1429 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
1430 }
1431
1432 PLAN *
1433 c_nouser(argvp, isok)
1434 char ***argvp;
1435 int isok;
1436 {
1437 ftsoptions &= ~FTS_NOSTAT;
1438
1439 return (palloc(N_NOUSER, f_nouser));
1440 }
1441
1442 /*
1443 * -path functions --
1444 *
1445 * True if the path of the filename being examined
1446 * matches pattern using Pattern Matching Notation S3.14
1447 */
1448 int
1449 f_path(plan, entry)
1450 PLAN *plan;
1451 FTSENT *entry;
1452 {
1453
1454 return (!fnmatch(plan->c_data, entry->fts_path, 0));
1455 }
1456
1457 PLAN *
1458 c_path(argvp, isok)
1459 char ***argvp;
1460 int isok;
1461 {
1462 char *pattern = **argvp;
1463 PLAN *new;
1464
1465 (*argvp)++;
1466 new = palloc(N_NAME, f_path);
1467 new->c_data = pattern;
1468 return (new);
1469 }
1470
1471 /*
1472 * -perm functions --
1473 *
1474 * The mode argument is used to represent file mode bits. If it starts
1475 * with a leading digit, it's treated as an octal mode, otherwise as a
1476 * symbolic mode.
1477 */
1478 int
1479 f_perm(plan, entry)
1480 PLAN *plan;
1481 FTSENT *entry;
1482 {
1483 mode_t mode;
1484
1485 mode = entry->fts_statp->st_mode &
1486 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1487 if (plan->flags == F_ATLEAST)
1488 return ((plan->m_data | mode) == mode);
1489 else
1490 return (mode == plan->m_data);
1491 /* NOTREACHED */
1492 }
1493
1494 PLAN *
1495 c_perm(argvp, isok)
1496 char ***argvp;
1497 int isok;
1498 {
1499 char *perm = **argvp;
1500 PLAN *new;
1501 mode_t *set;
1502
1503 (*argvp)++;
1504 ftsoptions &= ~FTS_NOSTAT;
1505
1506 new = palloc(N_PERM, f_perm);
1507
1508 if (*perm == '-') {
1509 new->flags = F_ATLEAST;
1510 ++perm;
1511 }
1512
1513 if ((set = setmode(perm)) == NULL)
1514 err(1, "-perm: Cannot set file mode `%s'", perm);
1515
1516 new->m_data = getmode(set, 0);
1517 free(set);
1518 return (new);
1519 }
1520
1521 /*
1522 * -print functions --
1523 *
1524 * Always true, causes the current pathame to be written to
1525 * standard output.
1526 */
1527 int
1528 f_print(plan, entry)
1529 PLAN *plan;
1530 FTSENT *entry;
1531 {
1532
1533 (void)printf("%s\n", entry->fts_path);
1534 return (1);
1535 }
1536
1537 int
1538 f_print0(plan, entry)
1539 PLAN *plan;
1540 FTSENT *entry;
1541 {
1542
1543 (void)fputs(entry->fts_path, stdout);
1544 (void)fputc('\0', stdout);
1545 return (1);
1546 }
1547
1548 int
1549 f_printx(plan, entry)
1550 PLAN *plan;
1551 FTSENT *entry;
1552 {
1553 char *cp;
1554
1555 for (cp = entry->fts_path; *cp; cp++) {
1556 if (*cp == '\'' || *cp == '\"' || *cp == ' ' ||
1557 *cp == '$' || *cp == '`' ||
1558 *cp == '\t' || *cp == '\n' || *cp == '\\')
1559 fputc('\\', stdout);
1560
1561 fputc(*cp, stdout);
1562 }
1563
1564 fputc('\n', stdout);
1565 return (1);
1566 }
1567
1568 PLAN *
1569 c_print(argvp, isok)
1570 char ***argvp;
1571 int isok;
1572 {
1573
1574 isoutput = 1;
1575
1576 return (palloc(N_PRINT, f_print));
1577 }
1578
1579 PLAN *
1580 c_print0(argvp, isok)
1581 char ***argvp;
1582 int isok;
1583 {
1584
1585 isoutput = 1;
1586
1587 return (palloc(N_PRINT0, f_print0));
1588 }
1589
1590 PLAN *
1591 c_printx(argvp, isok)
1592 char ***argvp;
1593 int isok;
1594 {
1595
1596 isoutput = 1;
1597
1598 return (palloc(N_PRINTX, f_printx));
1599 }
1600
1601 /*
1602 * -prune functions --
1603 *
1604 * Prune a portion of the hierarchy.
1605 */
1606 int
1607 f_prune(plan, entry)
1608 PLAN *plan;
1609 FTSENT *entry;
1610 {
1611 if (fts_set(tree, entry, FTS_SKIP))
1612 err(1, "%s", entry->fts_path);
1613 return (1);
1614 }
1615
1616 PLAN *
1617 c_prune(argvp, isok)
1618 char ***argvp;
1619 int isok;
1620 {
1621
1622 return (palloc(N_PRUNE, f_prune));
1623 }
1624
1625 /*
1626 * -regex regexp (and related) functions --
1627 *
1628 * True if the complete file path matches the regular expression regexp.
1629 * For -regex, regexp is a case-sensitive (basic) regular expression.
1630 * For -iregex, regexp is a case-insensitive (basic) regular expression.
1631 */
1632 int
1633 f_regex(plan, entry)
1634 PLAN *plan;
1635 FTSENT *entry;
1636 {
1637
1638 return (regexec(&plan->regexp_data, entry->fts_path, 0, NULL, 0) == 0);
1639 }
1640
1641 static PLAN *
1642 c_regex_common(argvp, isok, type, regcomp_flags)
1643 char ***argvp;
1644 int isok, regcomp_flags;
1645 enum ntype type;
1646 {
1647 char errbuf[LINE_MAX];
1648 regex_t reg;
1649 char *regexp = **argvp;
1650 char *lineregexp;
1651 PLAN *new;
1652 int rv;
1653
1654 (*argvp)++;
1655
1656 lineregexp = alloca(strlen(regexp) + 1 + 6); /* max needed */
1657 sprintf(lineregexp, "^%s(%s%s)$",
1658 (regcomp_flags & REG_EXTENDED) ? "" : "\\", regexp,
1659 (regcomp_flags & REG_EXTENDED) ? "" : "\\");
1660 rv = regcomp(®, lineregexp, REG_NOSUB|regcomp_flags);
1661 if (rv != 0) {
1662 regerror(rv, ®, errbuf, sizeof errbuf);
1663 errx(1, "regexp %s: %s", regexp, errbuf);
1664 }
1665
1666 new = palloc(type, f_regex);
1667 new->regexp_data = reg;
1668 return (new);
1669 }
1670
1671 PLAN *
1672 c_regex(argvp, isok)
1673 char ***argvp;
1674 int isok;
1675 {
1676
1677 return (c_regex_common(argvp, isok, N_REGEX, REG_BASIC));
1678 }
1679
1680 PLAN *
1681 c_iregex(argvp, isok)
1682 char ***argvp;
1683 int isok;
1684 {
1685
1686 return (c_regex_common(argvp, isok, N_IREGEX, REG_BASIC|REG_ICASE));
1687 }
1688
1689 /*
1690 * -size n[c] functions --
1691 *
1692 * True if the file size in bytes, divided by an implementation defined
1693 * value and rounded up to the next integer, is n. If n is followed by
1694 * a c, the size is in bytes.
1695 */
1696 #define FIND_SIZE 512
1697 static int divsize = 1;
1698
1699 int
1700 f_size(plan, entry)
1701 PLAN *plan;
1702 FTSENT *entry;
1703 {
1704 off_t size;
1705
1706 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1707 FIND_SIZE : entry->fts_statp->st_size;
1708 COMPARE(size, plan->o_data);
1709 }
1710
1711 PLAN *
1712 c_size(argvp, isok)
1713 char ***argvp;
1714 int isok;
1715 {
1716 char *arg = **argvp;
1717 PLAN *new;
1718 char endch;
1719
1720 (*argvp)++;
1721 ftsoptions &= ~FTS_NOSTAT;
1722
1723 new = palloc(N_SIZE, f_size);
1724 endch = 'c';
1725 new->o_data = find_parsenum(new, "-size", arg, &endch);
1726 if (endch == 'c')
1727 divsize = 0;
1728 return (new);
1729 }
1730
1731 /*
1732 * -type c functions --
1733 *
1734 * True if the type of the file is c, where c is b, c, d, p, f or w
1735 * for block special file, character special file, directory, FIFO,
1736 * regular file or whiteout respectively.
1737 */
1738 int
1739 f_type(plan, entry)
1740 PLAN *plan;
1741 FTSENT *entry;
1742 {
1743
1744 return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1745 }
1746
1747 PLAN *
1748 c_type(argvp, isok)
1749 char ***argvp;
1750 int isok;
1751 {
1752 char *typestring = **argvp;
1753 PLAN *new;
1754 mode_t mask = (mode_t)0;
1755
1756 (*argvp)++;
1757 ftsoptions &= ~FTS_NOSTAT;
1758
1759 switch (typestring[0]) {
1760 case 'b':
1761 mask = S_IFBLK;
1762 break;
1763 case 'c':
1764 mask = S_IFCHR;
1765 break;
1766 case 'd':
1767 mask = S_IFDIR;
1768 break;
1769 case 'f':
1770 mask = S_IFREG;
1771 break;
1772 case 'l':
1773 mask = S_IFLNK;
1774 break;
1775 case 'p':
1776 mask = S_IFIFO;
1777 break;
1778 case 's':
1779 mask = S_IFSOCK;
1780 break;
1781 #ifdef S_IFWHT
1782 case 'W':
1783 case 'w':
1784 mask = S_IFWHT;
1785 #ifdef FTS_WHITEOUT
1786 ftsoptions |= FTS_WHITEOUT;
1787 #endif
1788 break;
1789 #endif /* S_IFWHT */
1790 default:
1791 errx(1, "-type: %s: unknown type", typestring);
1792 }
1793
1794 new = palloc(N_TYPE, f_type);
1795 new->m_data = mask;
1796 return (new);
1797 }
1798
1799 /*
1800 * -user uname functions --
1801 *
1802 * True if the file belongs to the user uname. If uname is numeric and
1803 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1804 * return a valid user name, uname is taken as a user ID.
1805 */
1806 int
1807 f_user(plan, entry)
1808 PLAN *plan;
1809 FTSENT *entry;
1810 {
1811
1812 COMPARE(entry->fts_statp->st_uid, plan->u_data);
1813 }
1814
1815 PLAN *
1816 c_user(argvp, isok)
1817 char ***argvp;
1818 int isok;
1819 {
1820 char *username = **argvp;
1821 PLAN *new;
1822 struct passwd *p;
1823 uid_t uid;
1824
1825 (*argvp)++;
1826 ftsoptions &= ~FTS_NOSTAT;
1827
1828 new = palloc(N_USER, f_user);
1829 p = getpwnam(username);
1830 if (p == NULL) {
1831 if (atoi(username) == 0 && username[0] != '0' &&
1832 strcmp(username, "+0") && strcmp(username, "-0"))
1833 errx(1, "-user: %s: no such user", username);
1834 uid = find_parsenum(new, "-user", username, NULL);
1835
1836 } else {
1837 new->flags = F_EQUAL;
1838 uid = p->pw_uid;
1839 }
1840
1841 new->u_data = uid;
1842 return (new);
1843 }
1844
1845 /*
1846 * -xdev functions --
1847 *
1848 * Always true, causes find not to decend past directories that have a
1849 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1850 */
1851 PLAN *
1852 c_xdev(argvp, isok)
1853 char ***argvp;
1854 int isok;
1855 {
1856 ftsoptions |= FTS_XDEV;
1857
1858 return (palloc(N_XDEV, f_always_true));
1859 }
1860
1861 /*
1862 * ( expression ) functions --
1863 *
1864 * True if expression is true.
1865 */
1866 int
1867 f_expr(plan, entry)
1868 PLAN *plan;
1869 FTSENT *entry;
1870 {
1871 PLAN *p;
1872 int state;
1873
1874 state = 0;
1875 for (p = plan->p_data[0];
1876 p && (state = (p->eval)(p, entry)); p = p->next);
1877 return (state);
1878 }
1879
1880 /*
1881 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1882 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1883 * to a N_EXPR node containing the expression and the ')' node is discarded.
1884 */
1885 PLAN *
1886 c_openparen(argvp, isok)
1887 char ***argvp;
1888 int isok;
1889 {
1890
1891 return (palloc(N_OPENPAREN, (int (*) __P((PLAN *, FTSENT *)))-1));
1892 }
1893
1894 PLAN *
1895 c_closeparen(argvp, isok)
1896 char ***argvp;
1897 int isok;
1898 {
1899
1900 return (palloc(N_CLOSEPAREN, (int (*) __P((PLAN *, FTSENT *)))-1));
1901 }
1902
1903 /*
1904 * ! expression functions --
1905 *
1906 * Negation of a primary; the unary NOT operator.
1907 */
1908 int
1909 f_not(plan, entry)
1910 PLAN *plan;
1911 FTSENT *entry;
1912 {
1913 PLAN *p;
1914 int state;
1915
1916 state = 0;
1917 for (p = plan->p_data[0];
1918 p && (state = (p->eval)(p, entry)); p = p->next);
1919 return (!state);
1920 }
1921
1922 PLAN *
1923 c_not(argvp, isok)
1924 char ***argvp;
1925 int isok;
1926 {
1927
1928 return (palloc(N_NOT, f_not));
1929 }
1930
1931 /*
1932 * expression -o expression functions --
1933 *
1934 * Alternation of primaries; the OR operator. The second expression is
1935 * not evaluated if the first expression is true.
1936 */
1937 int
1938 f_or(plan, entry)
1939 PLAN *plan;
1940 FTSENT *entry;
1941 {
1942 PLAN *p;
1943 int state;
1944
1945 state = 0;
1946 for (p = plan->p_data[0];
1947 p && (state = (p->eval)(p, entry)); p = p->next);
1948
1949 if (state)
1950 return (1);
1951
1952 for (p = plan->p_data[1];
1953 p && (state = (p->eval)(p, entry)); p = p->next);
1954 return (state);
1955 }
1956
1957 PLAN *
1958 c_or(argvp, isok)
1959 char ***argvp;
1960 int isok;
1961 {
1962
1963 return (palloc(N_OR, f_or));
1964 }
1965
1966 PLAN *
1967 c_null(argvp, isok)
1968 char ***argvp;
1969 int isok;
1970 {
1971
1972 return (NULL);
1973 }
1974
1975
1976 /*
1977 * plan_cleanup --
1978 * Check and see if the specified plan has any residual state,
1979 * and if so, clean it up as appropriate.
1980 *
1981 * At the moment, only N_EXEC has state. Two kinds: 1)
1982 * lists of files to feed to subprocesses 2) State on exit
1983 * statusses of past subprocesses.
1984 */
1985 /* ARGSUSED1 */
1986 int
1987 plan_cleanup(plan, arg)
1988 PLAN *plan;
1989 void *arg;
1990 {
1991 if (plan->type==N_EXEC && plan->ep_narg)
1992 run_f_exec(plan);
1993
1994 return plan->ep_rval; /* Passed save exit-status up chain */
1995 }
1996
1997 static PLAN *
1998 palloc(t, f)
1999 enum ntype t;
2000 int (*f) __P((PLAN *, FTSENT *));
2001 {
2002 PLAN *new;
2003
2004 if ((new = malloc(sizeof(PLAN))) == NULL)
2005 err(1, NULL);
2006 new->type = t;
2007 new->eval = f;
2008 new->flags = 0;
2009 new->next = NULL;
2010 return (new);
2011 }
2012