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