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