kmalloc.9: Mention kmalloc_cachealign() in the NAME section, too.
[dragonfly.git] / usr.bin / find / function.c
1 /*-
2  * Copyright (c) 1990, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Cimarron D. Taylor of the University of California, Berkeley.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)function.c       8.10 (Berkeley) 5/4/95
33  * $FreeBSD: head/usr.bin/find/function.c 253886 2013-08-02 14:14:23Z jilles $
34  */
35
36 #include <sys/param.h>
37 #include <sys/ucred.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <sys/mount.h>
42
43 #include <dirent.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <fnmatch.h>
47 #include <fts.h>
48 #include <grp.h>
49 #include <limits.h>
50 #include <pwd.h>
51 #include <regex.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <ctype.h>
57
58 #include "find.h"
59
60 static PLAN *palloc(OPTION *);
61 static long long find_parsenum(PLAN *, const char *, char *, char *);
62 static long long find_parsetime(PLAN *, const char *, char *);
63 static char *nextarg(OPTION *, char ***);
64
65 extern char **environ;
66
67 static PLAN *lastexecplus = NULL;
68
69 #define COMPARE(a, b) do {                                              \
70         switch (plan->flags & F_ELG_MASK) {                             \
71         case F_EQUAL:                                                   \
72                 return (a == b);                                        \
73         case F_LESSTHAN:                                                \
74                 return (a < b);                                         \
75         case F_GREATER:                                                 \
76                 return (a > b);                                         \
77         default:                                                        \
78                 abort();                                                \
79         }                                                               \
80 } while(0)
81
82 static PLAN *
83 palloc(OPTION *option)
84 {
85         PLAN *new;
86
87         if ((new = malloc(sizeof(PLAN))) == NULL)
88                 err(1, NULL);
89         new->execute = option->execute;
90         new->flags = option->flags;
91         new->next = NULL;
92         return new;
93 }
94
95 /*
96  * find_parsenum --
97  *      Parse a string of the form [+-]# and return the value.
98  */
99 static long long
100 find_parsenum(PLAN *plan, const char *option, char *vp, char *endch)
101 {
102         long long value;
103         char *endchar, *str;    /* Pointer to character ending conversion. */
104
105         /* Determine comparison from leading + or -. */
106         str = vp;
107         switch (*str) {
108         case '+':
109                 ++str;
110                 plan->flags |= F_GREATER;
111                 break;
112         case '-':
113                 ++str;
114                 plan->flags |= F_LESSTHAN;
115                 break;
116         default:
117                 plan->flags |= F_EQUAL;
118                 break;
119         }
120
121         /*
122          * Convert the string with strtoq().  Note, if strtoq() returns zero
123          * and endchar points to the beginning of the string we know we have
124          * a syntax error.
125          */
126         value = strtoq(str, &endchar, 10);
127         if (value == 0 && endchar == str)
128                 errx(1, "%s: %s: illegal numeric value", option, vp);
129         if (endchar[0] && endch == NULL)
130                 errx(1, "%s: %s: illegal trailing character", option, vp);
131         if (endch)
132                 *endch = endchar[0];
133         return value;
134 }
135
136 /*
137  * find_parsetime --
138  *      Parse a string of the form [+-]([0-9]+[smhdw]?)+ and return the value.
139  */
140 static long long
141 find_parsetime(PLAN *plan, const char *option, char *vp)
142 {
143         long long secs, value;
144         char *str, *unit;       /* Pointer to character ending conversion. */
145
146         /* Determine comparison from leading + or -. */
147         str = vp;
148         switch (*str) {
149         case '+':
150                 ++str;
151                 plan->flags |= F_GREATER;
152                 break;
153         case '-':
154                 ++str;
155                 plan->flags |= F_LESSTHAN;
156                 break;
157         default:
158                 plan->flags |= F_EQUAL;
159                 break;
160         }
161
162         value = strtoq(str, &unit, 10);
163         if (value == 0 && unit == str) {
164                 errx(1, "%s: %s: illegal time value", option, vp);
165                 /* NOTREACHED */
166         }
167         if (*unit == '\0')
168                 return value;
169
170         /* Units syntax. */
171         secs = 0;
172         for (;;) {
173                 switch(*unit) {
174                 case 's':       /* seconds */
175                         secs += value;
176                         break;
177                 case 'm':       /* minutes */
178                         secs += value * 60;
179                         break;
180                 case 'h':       /* hours */
181                         secs += value * 3600;
182                         break;
183                 case 'd':       /* days */
184                         secs += value * 86400;
185                         break;
186                 case 'w':       /* weeks */
187                         secs += value * 604800;
188                         break;
189                 default:
190                         errx(1, "%s: %s: bad unit '%c'", option, vp, *unit);
191                         /* NOTREACHED */
192                 }
193                 str = unit + 1;
194                 if (*str == '\0')       /* EOS */
195                         break;
196                 value = strtoq(str, &unit, 10);
197                 if (value == 0 && unit == str) {
198                         errx(1, "%s: %s: illegal time value", option, vp);
199                         /* NOTREACHED */
200                 }
201                 if (*unit == '\0') {
202                         errx(1, "%s: %s: missing trailing unit", option, vp);
203                         /* NOTREACHED */
204                 }
205         }
206         plan->flags |= F_EXACTTIME;
207         return secs;
208 }
209
210 /*
211  * nextarg --
212  *      Check that another argument still exists, return a pointer to it,
213  *      and increment the argument vector pointer.
214  */
215 static char *
216 nextarg(OPTION *option, char ***argvp)
217 {
218         char *arg;
219
220         if ((arg = **argvp) == NULL)
221                 errx(1, "%s: requires additional arguments", option->name);
222         (*argvp)++;
223         return arg;
224 } /* nextarg() */
225
226 /*
227  * The value of n for the inode times (atime, birthtime, ctime, mtime) is a
228  * range, i.e. n matches from (n - 1) to n 24 hour periods.  This interacts
229  * with -n, such that "-mtime -1" would be less than 0 days, which isn't what
230  * the user wanted.  Correct so that -1 is "less than 1".
231  */
232 #define TIME_CORRECT(p) \
233         if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \
234                 ++((p)->t_data.tv_sec);
235
236 /*
237  * -[acm]min n functions --
238  *
239  *    True if the difference between the
240  *              file access time (-amin)
241  *              file birth time (-Bmin)  (Not supported on DragonFly)
242  *              last change of file status information (-cmin)
243  *              file modification time (-mmin)
244  *    and the current time is n min periods.
245  */
246 int
247 f_Xmin(PLAN *plan, FTSENT *entry)
248 {
249         if (plan->flags & F_TIME_C) {
250                 COMPARE((now - entry->fts_statp->st_ctime +
251                     60 - 1) / 60, plan->t_data.tv_sec);
252         } else if (plan->flags & F_TIME_A) {
253                 COMPARE((now - entry->fts_statp->st_atime +
254                     60 - 1) / 60, plan->t_data.tv_sec);
255         } else {
256                 COMPARE((now - entry->fts_statp->st_mtime +
257                     60 - 1) / 60, plan->t_data.tv_sec);
258         }
259 }
260
261 PLAN *
262 c_Xmin(OPTION *option, char ***argvp)
263 {
264         char *nmins;
265         PLAN *new;
266
267         nmins = nextarg(option, argvp);
268         ftsoptions &= ~FTS_NOSTAT;
269
270         new = palloc(option);
271         new->t_data.tv_sec = find_parsenum(new, option->name, nmins, NULL);
272         new->t_data.tv_nsec = 0;
273         TIME_CORRECT(new);
274         return new;
275 }
276
277 /*
278  * -[acm]time n functions --
279  *
280  *      True if the difference between the
281  *              file access time (-atime)
282  *              file birth time (-Btime) (Not supported on DragonFly)
283  *              last change of file status information (-ctime)
284  *              file modification time (-mtime)
285  *      and the current time is n 24 hour periods.
286  */
287
288 int
289 f_Xtime(PLAN *plan, FTSENT *entry)
290 {
291         time_t xtime;
292
293         if (plan->flags & F_TIME_A)
294                 xtime = entry->fts_statp->st_atime;
295         else if (plan->flags & F_TIME_C)
296                 xtime = entry->fts_statp->st_ctime;
297         else
298                 xtime = entry->fts_statp->st_mtime;
299
300         if (plan->flags & F_EXACTTIME)
301                 COMPARE(now - xtime, plan->t_data.tv_sec);
302         else
303                 COMPARE((now - xtime + 86400 - 1) / 86400, plan->t_data.tv_sec);
304 }
305
306 PLAN *
307 c_Xtime(OPTION *option, char ***argvp)
308 {
309         char *value;
310         PLAN *new;
311
312         value = nextarg(option, argvp);
313         ftsoptions &= ~FTS_NOSTAT;
314
315         new = palloc(option);
316         new->t_data.tv_sec = find_parsetime(new, option->name, value);
317         new->t_data.tv_nsec = 0;
318         if (!(new->flags & F_EXACTTIME))
319                 TIME_CORRECT(new);
320         return new;
321 }
322
323 /*
324  * -maxdepth/-mindepth n functions --
325  *
326  *        Does the same as -prune if the level of the current file is
327  *        greater/less than the specified maximum/minimum depth.
328  *
329  *        Note that -maxdepth and -mindepth are handled specially in
330  *        find_execute() so their f_* functions are set to f_always_true().
331  */
332 PLAN *
333 c_mXXdepth(OPTION *option, char ***argvp)
334 {
335         char *dstr;
336         PLAN *new;
337
338         dstr = nextarg(option, argvp);
339         if (dstr[0] == '-')
340                 /* all other errors handled by find_parsenum() */
341                 errx(1, "%s: %s: value must be positive", option->name, dstr);
342
343         new = palloc(option);
344         if (option->flags & F_MAXDEPTH)
345                 maxdepth = find_parsenum(new, option->name, dstr, NULL);
346         else
347                 mindepth = find_parsenum(new, option->name, dstr, NULL);
348         return new;
349 }
350
351 /*
352  * -delete functions --
353  *
354  *      True always.  Makes its best shot and continues on regardless.
355  */
356 int
357 f_delete(PLAN *plan __unused, FTSENT *entry)
358 {
359         /* ignore these from fts */
360         if (strcmp(entry->fts_accpath, ".") == 0 ||
361             strcmp(entry->fts_accpath, "..") == 0)
362                 return 1;
363
364         /* sanity check */
365         if (isdepth == 0 ||                     /* depth off */
366             (ftsoptions & FTS_NOSTAT))          /* not stat()ing */
367                 errx(1, "-delete: insecure options got turned on");
368
369         if (!(ftsoptions & FTS_PHYSICAL) ||     /* physical off */
370             (ftsoptions & FTS_LOGICAL))         /* or finally, logical on */
371                 errx(1, "-delete: forbidden when symlinks are followed");
372
373         /* Potentially unsafe - do not accept relative paths whatsoever */
374         if (entry->fts_level > FTS_ROOTLEVEL &&
375             strchr(entry->fts_accpath, '/') != NULL)
376                 errx(1, "-delete: %s: relative path potentially not safe",
377                         entry->fts_accpath);
378
379         /* Turn off user immutable bits if running as root */
380         if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
381             !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
382             geteuid() == 0)
383                 lchflags(entry->fts_accpath,
384                        entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
385
386         /* rmdir directories, unlink everything else */
387         if (S_ISDIR(entry->fts_statp->st_mode)) {
388                 if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
389                         warn("-delete: rmdir(%s)", entry->fts_path);
390         } else {
391                 if (unlink(entry->fts_accpath) < 0)
392                         warn("-delete: unlink(%s)", entry->fts_path);
393         }
394
395         /* "succeed" */
396         return 1;
397 }
398
399 PLAN *
400 c_delete(OPTION *option, char ***argvp __unused)
401 {
402
403         ftsoptions &= ~FTS_NOSTAT;      /* no optimise */
404         isoutput = 1;                   /* possible output */
405         isdepth = 1;                    /* -depth implied */
406
407         /*
408          * Try to avoid the confusing error message about relative paths
409          * being potentially not safe.
410          */
411         if (ftsoptions & FTS_NOCHDIR)
412                 errx(1, "%s: forbidden when the current directory cannot be opened",
413                     "-delete");
414
415         return palloc(option);
416 }
417
418
419 /*
420  * always_true --
421  *
422  *      Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true
423  */
424 int
425 f_always_true(PLAN *plan __unused, FTSENT *entry __unused)
426 {
427         return 1;
428 }
429
430 /*
431  * -depth functions --
432  *
433  *      With argument: True if the file is at level n.
434  *      Without argument: Always true, causes descent of the directory hierarchy
435  *      to be done so that all entries in a directory are acted on before the
436  *      directory itself.
437  */
438 int
439 f_depth(PLAN *plan, FTSENT *entry)
440 {
441         if (plan->flags & F_DEPTH)
442                 COMPARE(entry->fts_level, plan->d_data);
443         else
444                 return 1;
445 }
446
447 PLAN *
448 c_depth(OPTION *option, char ***argvp)
449 {
450         PLAN *new;
451         char *str;
452
453         new = palloc(option);
454
455         str = **argvp;
456         if (str && !(new->flags & F_DEPTH)) {
457                 /* skip leading + or - */
458                 if (*str == '+' || *str == '-')
459                         str++;
460                 /* skip sign */
461                 if (*str == '+' || *str == '-')
462                         str++;
463                 if (isdigit(*str))
464                         new->flags |= F_DEPTH;
465         }
466
467         if (new->flags & F_DEPTH) {     /* -depth n */
468                 char *ndepth;
469
470                 ndepth = nextarg(option, argvp);
471                 new->d_data = find_parsenum(new, option->name, ndepth, NULL);
472         } else {                        /* -d */
473                 isdepth = 1;
474         }
475
476         return new;
477 }
478  
479 /*
480  * -empty functions --
481  *
482  *      True if the file or directory is empty
483  */
484 int
485 f_empty(PLAN *plan __unused, FTSENT *entry)
486 {
487         if (S_ISREG(entry->fts_statp->st_mode) &&
488             entry->fts_statp->st_size == 0)
489                 return 1;
490         if (S_ISDIR(entry->fts_statp->st_mode)) {
491                 struct dirent *dp;
492                 int empty;
493                 DIR *dir;
494
495                 empty = 1;
496                 dir = opendir(entry->fts_accpath);
497                 if (dir == NULL)
498                         return 0;
499                 for (dp = readdir(dir); dp; dp = readdir(dir))
500                         if (dp->d_name[0] != '.' ||
501                             (dp->d_name[1] != '\0' &&
502                              (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
503                                 empty = 0;
504                                 break;
505                         }
506                 closedir(dir);
507                 return empty;
508         }
509         return 0;
510 }
511
512 PLAN *
513 c_empty(OPTION *option, char ***argvp __unused)
514 {
515         ftsoptions &= ~FTS_NOSTAT;
516
517         return palloc(option);
518 }
519
520 /*
521  * [-exec | -execdir | -ok] utility [arg ... ] ; functions --
522  *
523  *      True if the executed utility returns a zero value as exit status.
524  *      The end of the primary expression is delimited by a semicolon.  If
525  *      "{}" occurs anywhere, it gets replaced by the current pathname,
526  *      or, in the case of -execdir, the current basename (filename
527  *      without leading directory prefix). For -exec and -ok,
528  *      the current directory for the execution of utility is the same as
529  *      the current directory when the find utility was started, whereas
530  *      for -execdir, it is the directory the file resides in.
531  *
532  *      The primary -ok differs from -exec in that it requests affirmation
533  *      of the user before executing the utility.
534  */
535 int
536 f_exec(PLAN *plan, FTSENT *entry)
537 {
538         int cnt;
539         pid_t pid;
540         int status;
541         char *file;
542
543         if (entry == NULL && plan->flags & F_EXECPLUS) {
544                 if (plan->e_ppos == plan->e_pbnum)
545                         return (1);
546                 plan->e_argv[plan->e_ppos] = NULL;
547                 goto doexec;
548         }
549
550         /* XXX - if file/dir ends in '/' this will not work -- can it? */
551         if ((plan->flags & F_EXECDIR) && \
552             (file = strrchr(entry->fts_path, '/')))
553                 file++;
554         else
555                 file = entry->fts_path;
556
557         if (plan->flags & F_EXECPLUS) {
558                 if ((plan->e_argv[plan->e_ppos] = strdup(file)) == NULL)
559                         err(1, NULL);
560                 plan->e_len[plan->e_ppos] = strlen(file);
561                 plan->e_psize += plan->e_len[plan->e_ppos];
562                 if (++plan->e_ppos < plan->e_pnummax &&
563                     plan->e_psize < plan->e_psizemax)
564                         return (1);
565                 plan->e_argv[plan->e_ppos] = NULL;
566         } else {
567                 for (cnt = 0; plan->e_argv[cnt]; ++cnt)
568                         if (plan->e_len[cnt])
569                                 brace_subst(plan->e_orig[cnt],
570                                     &plan->e_argv[cnt], file,
571                                     plan->e_len[cnt]);
572         }
573
574 doexec: if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv))
575                 return 0;
576
577         /* make sure find output is interspersed correctly with subprocesses */
578         fflush(stdout);
579         fflush(stderr);
580
581         switch (pid = fork()) {
582         case -1:
583                 err(1, "fork");
584                 /* NOTREACHED */
585         case 0:
586                 /* change dir back from where we started */
587                 if (!(plan->flags & F_EXECDIR) &&
588                     !(ftsoptions & FTS_NOCHDIR) && fchdir(dotfd)) {
589                         warn("chdir");
590                         _exit(1);
591                 }
592                 execvp(plan->e_argv[0], plan->e_argv);
593                 warn("%s", plan->e_argv[0]);
594                 _exit(1);
595         }
596         if (plan->flags & F_EXECPLUS) {
597                 while (--plan->e_ppos >= plan->e_pbnum)
598                         free(plan->e_argv[plan->e_ppos]);
599                 plan->e_ppos = plan->e_pbnum;
600                 plan->e_psize = plan->e_pbsize;
601         }
602         pid = waitpid(pid, &status, 0);
603         return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
604 }
605
606 /*
607  * c_exec, c_execdir, c_ok --
608  *      build three parallel arrays, one with pointers to the strings passed
609  *      on the command line, one with (possibly duplicated) pointers to the
610  *      argv array, and one with integer values that are lengths of the
611  *      strings, but also flags meaning that the string has to be massaged.
612  */
613 PLAN *
614 c_exec(OPTION *option, char ***argvp)
615 {
616         PLAN *new;                      /* node returned */
617         long argmax;
618         int cnt, i;
619         char **argv, **ap, **ep, *p;
620
621         /* This would defeat -execdir's intended security. */
622         if (option->flags & F_EXECDIR && ftsoptions & FTS_NOCHDIR)
623                 errx(1, "%s: forbidden when the current directory cannot be opened",
624                     "-execdir");
625
626         /* XXX - was in c_execdir, but seems unnecessary!?
627         ftsoptions &= ~FTS_NOSTAT;
628         */
629         isoutput = 1;
630
631         /* XXX - this is a change from the previous coding */
632         new = palloc(option);
633
634         for (ap = argv = *argvp;; ++ap) {
635                 if (!*ap)
636                         errx(1,
637                             "%s: no terminating \";\" or \"+\"", option->name);
638                 if (**ap == ';')
639                         break;
640                 if (**ap == '+' && ap != argv && strcmp(*(ap - 1), "{}") == 0) {
641                         new->flags |= F_EXECPLUS;
642                         break;
643                 }
644         }
645
646         if (ap == argv)
647                 errx(1, "%s: no command specified", option->name);
648
649         cnt = ap - *argvp + 1;
650         if (new->flags & F_EXECPLUS) {
651                 new->e_ppos = new->e_pbnum = cnt - 2;
652                 if ((argmax = sysconf(_SC_ARG_MAX)) == -1) {
653                         warn("sysconf(_SC_ARG_MAX)");
654                         argmax = _POSIX_ARG_MAX;
655                 }
656                 argmax -= 1024;
657                 for (ep = environ; *ep != NULL; ep++)
658                         argmax -= strlen(*ep) + 1 + sizeof(*ep);
659                 argmax -= 1 + sizeof(*ep);
660                 /*
661                  * Ensure that -execdir ... {} + does not mix files
662                  * from different directories in one invocation.
663                  * Files from the same directory should be handled
664                  * in one invocation but there is no code for it.
665                  */
666                 new->e_pnummax = new->flags & F_EXECDIR ? 1 : argmax / 16;
667                 argmax -= sizeof(char *) * new->e_pnummax;
668                 if (argmax <= 0)
669                         errx(1, "no space for arguments");
670                 new->e_psizemax = argmax;
671                 new->e_pbsize = 0;
672                 cnt += new->e_pnummax + 1;
673                 new->e_next = lastexecplus;
674                 lastexecplus = new;
675         }
676         if ((new->e_argv = malloc(cnt * sizeof(char *))) == NULL)
677                 err(1, NULL);
678         if ((new->e_orig = malloc(cnt * sizeof(char *))) == NULL)
679                 err(1, NULL);
680         if ((new->e_len = malloc(cnt * sizeof(int))) == NULL)
681                 err(1, NULL);
682
683         for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
684                 new->e_orig[cnt] = *argv;
685                 if (new->flags & F_EXECPLUS)
686                         new->e_pbsize += strlen(*argv) + 1;
687                 for (p = *argv; *p; ++p)
688                         if (!(new->flags & F_EXECPLUS) && p[0] == '{' &&
689                             p[1] == '}') {
690                                 if ((new->e_argv[cnt] =
691                                     malloc(MAXPATHLEN)) == NULL)
692                                         err(1, NULL);
693                                 new->e_len[cnt] = MAXPATHLEN;
694                                 break;
695                         }
696                 if (!*p) {
697                         new->e_argv[cnt] = *argv;
698                         new->e_len[cnt] = 0;
699                 }
700         }
701         if (new->flags & F_EXECPLUS) {
702                 new->e_psize = new->e_pbsize;
703                 cnt--;
704                 for (i = 0; i < new->e_pnummax; i++) {
705                         new->e_argv[cnt] = NULL;
706                         new->e_len[cnt] = 0;
707                         cnt++;
708                 }
709                 argv = ap;
710                 goto done;
711         }
712         new->e_argv[cnt] = new->e_orig[cnt] = NULL;
713
714 done:   *argvp = argv + 1;
715         return new;
716 }
717
718 /* Finish any pending -exec ... {} + functions. */
719 void
720 finish_execplus(void)
721 {
722         PLAN *p;
723
724         p = lastexecplus;
725         while (p != NULL) {
726                 (p->execute)(p, NULL);
727                 p = p->e_next;
728         }
729 }
730
731 int
732 f_flags(PLAN *plan, FTSENT *entry)
733 {
734         u_long flags;
735
736         flags = entry->fts_statp->st_flags;
737         if (plan->flags & F_ATLEAST)
738                 return (flags | plan->fl_flags) == flags &&
739                     !(flags & plan->fl_notflags);
740         else if (plan->flags & F_ANY)
741                 return (flags & plan->fl_flags) ||
742                     (flags | plan->fl_notflags) != flags;
743         else
744                 return flags == plan->fl_flags &&
745                     !(plan->fl_flags & plan->fl_notflags);
746 }
747
748 PLAN *
749 c_flags(OPTION *option, char ***argvp)
750 {
751         char *flags_str;
752         PLAN *new;
753         u_long flags, notflags;
754
755         flags_str = nextarg(option, argvp);
756         ftsoptions &= ~FTS_NOSTAT;
757
758         new = palloc(option);
759
760         if (*flags_str == '-') {
761                 new->flags |= F_ATLEAST;
762                 flags_str++;
763         } else if (*flags_str == '+') {
764                 new->flags |= F_ANY;
765                 flags_str++;
766         }
767         if (strtofflags(&flags_str, &flags, &notflags) == 1)
768                 errx(1, "%s: %s: illegal flags string", option->name, flags_str);
769
770         new->fl_flags = flags;
771         new->fl_notflags = notflags;
772         return new;
773 }
774
775 /*
776  * -follow functions --
777  *
778  *      Always true, causes symbolic links to be followed on a global
779  *      basis.
780  */
781 PLAN *
782 c_follow(OPTION *option, char ***argvp __unused)
783 {
784         ftsoptions &= ~FTS_PHYSICAL;
785         ftsoptions |= FTS_LOGICAL;
786
787         return palloc(option);
788 }
789
790 /*
791  * -fstype functions --
792  *
793  *      True if the file is of a certain type.
794  */
795 int
796 f_fstype(PLAN *plan, FTSENT *entry)
797 {
798         static dev_t curdev;    /* need a guaranteed illegal dev value */
799         static int first = 1;
800         struct statfs sb;
801         static int val_type, val_flags;
802         char *p, save[2] = {0,0};
803
804         if ((plan->flags & F_MTMASK) == F_MTUNKNOWN)
805                 return 0;
806
807         /* Only check when we cross mount point. */
808         if (first || curdev != entry->fts_statp->st_dev) {
809                 curdev = entry->fts_statp->st_dev;
810
811                 /*
812                  * Statfs follows symlinks; find wants the link's filesystem,
813                  * not where it points.
814                  */
815                 if (entry->fts_info == FTS_SL ||
816                     entry->fts_info == FTS_SLNONE) {
817                         if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
818                                 ++p;
819                         else
820                                 p = entry->fts_accpath;
821                         save[0] = p[0];
822                         p[0] = '.';
823                         save[1] = p[1];
824                         p[1] = '\0';
825                 } else
826                         p = NULL;
827
828                 if (statfs(entry->fts_accpath, &sb)) {
829                         warn("%s", entry->fts_accpath);
830                         return 0;
831                 }
832
833                 if (p) {
834                         p[0] = save[0];
835                         p[1] = save[1];
836                 }
837
838                 first = 0;
839
840                 /*
841                  * Further tests may need both of these values, so
842                  * always copy both of them.
843                  */
844                 val_flags = sb.f_flags;
845                 val_type = sb.f_type;
846         }
847         switch (plan->flags & F_MTMASK) {
848         case F_MTFLAG:
849                 return val_flags & plan->mt_data;
850         case F_MTTYPE:
851                 return val_type == plan->mt_data;
852         default:
853                 abort();
854         }
855 }
856
857 PLAN *
858 c_fstype(OPTION *option, char ***argvp)
859 {
860         char *fsname;
861         PLAN *new;
862         struct vfsconf vfc;
863
864         fsname = nextarg(option, argvp);
865         ftsoptions &= ~FTS_NOSTAT;
866
867         new = palloc(option);
868
869         /*
870          * Check first for a filesystem name.
871          */
872         if (getvfsbyname(fsname, &vfc) == 0) {
873                 new->flags |= F_MTTYPE;
874                 new->mt_data = vfc.vfc_typenum;
875                 return new;
876         }
877
878         switch (*fsname) {
879         case 'l':
880                 if (!strcmp(fsname, "local")) {
881                         new->flags |= F_MTFLAG;
882                         new->mt_data = MNT_LOCAL;
883                         return new;
884                 }
885                 break;
886         case 'r':
887                 if (!strcmp(fsname, "rdonly")) {
888                         new->flags |= F_MTFLAG;
889                         new->mt_data = MNT_RDONLY;
890                         return new;
891                 }
892                 break;
893         }
894
895         /*
896          * We need to make filesystem checks for filesystems
897          * that exists but aren't in the kernel work.
898          */
899         fprintf(stderr, "Warning: Unknown filesystem type %s\n", fsname);
900         new->flags |= F_MTUNKNOWN;
901         return new;
902 }
903
904 /*
905  * -group gname functions --
906  *
907  *      True if the file belongs to the group gname.  If gname is numeric and
908  *      an equivalent of the getgrnam() function does not return a valid group
909  *      name, gname is taken as a group ID.
910  */
911 int
912 f_group(PLAN *plan, FTSENT *entry)
913 {
914         COMPARE(entry->fts_statp->st_gid, plan->g_data);
915 }
916
917 PLAN *
918 c_group(OPTION *option, char ***argvp)
919 {
920         char *gname;
921         PLAN *new;
922         struct group *g;
923         gid_t gid;
924
925         gname = nextarg(option, argvp);
926         ftsoptions &= ~FTS_NOSTAT;
927
928         new = palloc(option);
929         g = getgrnam(gname);
930         if (g == NULL) {
931                 char* cp = gname;
932                 if (gname[0] == '-' || gname[0] == '+')
933                         gname++;
934                 gid = atoi(gname);
935                 if (gid == 0 && gname[0] != '0')
936                         errx(1, "%s: %s: no such group", option->name, gname);
937                 gid = find_parsenum(new, option->name, cp, NULL);
938         } else
939                 gid = g->gr_gid;
940
941         new->g_data = gid;
942         return new;
943 }
944
945 /*
946  * -ignore_readdir_race functions --
947  *
948  *      Always true. Ignore errors which occur if a file or a directory
949  *      in a starting point gets deleted between reading the name and calling
950  *      stat on it while find is traversing the starting point.
951  */
952
953 PLAN *
954 c_ignore_readdir_race(OPTION *option, char ***argvp __unused)
955 {
956         if (strcmp(option->name, "-ignore_readdir_race") == 0)
957                 ignore_readdir_race = 1;
958         else
959                 ignore_readdir_race = 0;
960
961         return palloc(option);
962 }
963
964 /*
965  * -inum n functions --
966  *
967  *      True if the file has inode # n.
968  */
969 int
970 f_inum(PLAN *plan, FTSENT *entry)
971 {
972         COMPARE(entry->fts_statp->st_ino, plan->i_data);
973 }
974
975 PLAN *
976 c_inum(OPTION *option, char ***argvp)
977 {
978         char *inum_str;
979         PLAN *new;
980
981         inum_str = nextarg(option, argvp);
982         ftsoptions &= ~FTS_NOSTAT;
983
984         new = palloc(option);
985         new->i_data = find_parsenum(new, option->name, inum_str, NULL);
986         return new;
987 }
988
989 /*
990  * -samefile FN
991  *
992  *      True if the file has the same inode (eg hard link) FN
993  */
994
995 /* f_samefile is just f_inum */
996 PLAN *
997 c_samefile(OPTION *option, char ***argvp)
998 {
999         char *fn;
1000         PLAN *new;
1001         struct stat sb;
1002
1003         fn = nextarg(option, argvp);
1004         ftsoptions &= ~FTS_NOSTAT;
1005
1006         new = palloc(option);
1007         if (stat(fn, &sb))
1008                 err(1, "%s", fn);
1009         new->i_data = sb.st_ino;
1010         return new;
1011 }
1012
1013 /*
1014  * -links n functions --
1015  *
1016  *      True if the file has n links.
1017  */
1018 int
1019 f_links(PLAN *plan, FTSENT *entry)
1020 {
1021         COMPARE(entry->fts_statp->st_nlink, plan->l_data);
1022 }
1023
1024 PLAN *
1025 c_links(OPTION *option, char ***argvp)
1026 {
1027         char *nlinks;
1028         PLAN *new;
1029
1030         nlinks = nextarg(option, argvp);
1031         ftsoptions &= ~FTS_NOSTAT;
1032
1033         new = palloc(option);
1034         new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL);
1035         return new;
1036 }
1037
1038 /*
1039  * -ls functions --
1040  *
1041  *      Always true - prints the current entry to stdout in "ls" format.
1042  */
1043 int
1044 f_ls(PLAN *plan __unused, FTSENT *entry)
1045 {
1046         printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
1047         return 1;
1048 }
1049
1050 PLAN *
1051 c_ls(OPTION *option, char ***argvp __unused)
1052 {
1053         ftsoptions &= ~FTS_NOSTAT;
1054         isoutput = 1;
1055
1056         return palloc(option);
1057 }
1058
1059 /*
1060  * -name functions --
1061  *
1062  *      True if the basename of the filename being examined
1063  *      matches pattern using Pattern Matching Notation S3.14
1064  */
1065 int
1066 f_name(PLAN *plan, FTSENT *entry)
1067 {
1068         char fn[PATH_MAX];
1069         const char *name;
1070
1071         if (plan->flags & F_LINK) {
1072                 name = fn;
1073                 if (readlink(entry->fts_path, fn, sizeof(fn)) == -1)
1074                         return 0;
1075         } else
1076                 name = entry->fts_name;
1077         return !fnmatch(plan->c_data, name,
1078             plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0);
1079 }
1080
1081 PLAN *
1082 c_name(OPTION *option, char ***argvp)
1083 {
1084         char *pattern;
1085         PLAN *new;
1086
1087         pattern = nextarg(option, argvp);
1088         new = palloc(option);
1089         new->c_data = pattern;
1090         return new;
1091 }
1092
1093 /*
1094  * -newer file functions --
1095  *
1096  *      True if the current file has been modified more recently
1097  *      then the modification time of the file named by the pathname
1098  *      file.
1099  */
1100 int
1101 f_newer(PLAN *plan, FTSENT *entry)
1102 {
1103         struct timespec ft;
1104
1105         if (plan->flags & F_TIME_C)
1106                 ft = entry->fts_statp->st_ctim;
1107         else if (plan->flags & F_TIME_A)
1108                 ft = entry->fts_statp->st_atim;
1109         else
1110                 ft = entry->fts_statp->st_mtim;
1111         return (ft.tv_sec > plan->t_data.tv_sec ||
1112                 (ft.tv_sec == plan->t_data.tv_sec &&
1113                  ft.tv_nsec > plan->t_data.tv_nsec));
1114 }
1115
1116 PLAN *
1117 c_newer(OPTION *option, char ***argvp)
1118 {
1119         char *fn_or_tspec;
1120         PLAN *new;
1121         struct stat sb;
1122
1123         fn_or_tspec = nextarg(option, argvp);
1124         ftsoptions &= ~FTS_NOSTAT;
1125
1126         new = palloc(option);
1127         /* compare against what */
1128         if (option->flags & F_TIME2_T) {
1129                 new->t_data.tv_sec = get_date(fn_or_tspec);
1130                 if (new->t_data.tv_sec == (time_t) -1)
1131                         errx(1, "Can't parse date/time: %s", fn_or_tspec);
1132                 /* Use the seconds only in the comparison. */
1133                 new->t_data.tv_nsec = 999999999;
1134         } else {
1135                 if (stat(fn_or_tspec, &sb))
1136                         err(1, "%s", fn_or_tspec);
1137                 if (option->flags & F_TIME2_C)
1138                         new->t_data = sb.st_ctim;
1139                 else if (option->flags & F_TIME2_A)
1140                         new->t_data = sb.st_atim;
1141                 else
1142                         new->t_data = sb.st_mtim;
1143         }
1144         return new;
1145 }
1146
1147 /*
1148  * -nogroup functions --
1149  *
1150  *      True if file belongs to a user ID for which the equivalent
1151  *      of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
1152  */
1153 int
1154 f_nogroup(PLAN *plan __unused, FTSENT *entry)
1155 {
1156         return group_from_gid(entry->fts_statp->st_gid, 1) == NULL;
1157 }
1158
1159 PLAN *
1160 c_nogroup(OPTION *option, char ***argvp __unused)
1161 {
1162         ftsoptions &= ~FTS_NOSTAT;
1163
1164         return palloc(option);
1165 }
1166
1167 /*
1168  * -nouser functions --
1169  *
1170  *      True if file belongs to a user ID for which the equivalent
1171  *      of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
1172  */
1173 int
1174 f_nouser(PLAN *plan __unused, FTSENT *entry)
1175 {
1176         return user_from_uid(entry->fts_statp->st_uid, 1) == NULL;
1177 }
1178
1179 PLAN *
1180 c_nouser(OPTION *option, char ***argvp __unused)
1181 {
1182         ftsoptions &= ~FTS_NOSTAT;
1183
1184         return palloc(option);
1185 }
1186
1187 /*
1188  * -path functions --
1189  *
1190  *      True if the path of the filename being examined
1191  *      matches pattern using Pattern Matching Notation S3.14
1192  */
1193 int
1194 f_path(PLAN *plan, FTSENT *entry)
1195 {
1196         return !fnmatch(plan->c_data, entry->fts_path,
1197             plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0);
1198 }
1199
1200 /* c_path is the same as c_name */
1201
1202 /*
1203  * -perm functions --
1204  *
1205  *      The mode argument is used to represent file mode bits.  If it starts
1206  *      with a leading digit, it's treated as an octal mode, otherwise as a
1207  *      symbolic mode.
1208  */
1209 int
1210 f_perm(PLAN *plan, FTSENT *entry)
1211 {
1212         mode_t mode;
1213
1214         mode = entry->fts_statp->st_mode &
1215             (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1216         if (plan->flags & F_ATLEAST)
1217                 return (plan->m_data | mode) == mode;
1218         else if (plan->flags & F_ANY)
1219                 return (mode & plan->m_data);
1220         else
1221                 return mode == plan->m_data;
1222         /* NOTREACHED */
1223 }
1224
1225 PLAN *
1226 c_perm(OPTION *option, char ***argvp)
1227 {
1228         char *perm;
1229         PLAN *new;
1230         mode_t *set;
1231
1232         perm = nextarg(option, argvp);
1233         ftsoptions &= ~FTS_NOSTAT;
1234
1235         new = palloc(option);
1236
1237         if (*perm == '-') {
1238                 new->flags |= F_ATLEAST;
1239                 ++perm;
1240         } else if (*perm == '+') {
1241                 new->flags |= F_ANY;
1242                 ++perm;
1243         }
1244
1245         if ((set = setmode(perm)) == NULL)
1246                 errx(1, "%s: %s: illegal mode string", option->name, perm);
1247
1248         new->m_data = getmode(set, 0);
1249         free(set);
1250         return new;
1251 }
1252
1253 /*
1254  * -print functions --
1255  *
1256  *      Always true, causes the current pathname to be written to
1257  *      standard output.
1258  */
1259 int
1260 f_print(PLAN *plan __unused, FTSENT *entry)
1261 {
1262         (void)puts(entry->fts_path);
1263         return 1;
1264 }
1265
1266 PLAN *
1267 c_print(OPTION *option, char ***argvp __unused)
1268 {
1269         isoutput = 1;
1270
1271         return palloc(option);
1272 }
1273
1274 /*
1275  * -print0 functions --
1276  *
1277  *      Always true, causes the current pathname to be written to
1278  *      standard output followed by a NUL character
1279  */
1280 int
1281 f_print0(PLAN *plan __unused, FTSENT *entry)
1282 {
1283         fputs(entry->fts_path, stdout);
1284         fputc('\0', stdout);
1285         return 1;
1286 }
1287
1288 /* c_print0 is the same as c_print */
1289
1290 /*
1291  * -prune functions --
1292  *
1293  *      Prune a portion of the hierarchy.
1294  */
1295 int
1296 f_prune(PLAN *plan __unused, FTSENT *entry)
1297 {
1298         if (fts_set(tree, entry, FTS_SKIP))
1299                 err(1, "%s", entry->fts_path);
1300         return 1;
1301 }
1302
1303 /* c_prune == c_simple */
1304
1305 /*
1306  * -regex functions --
1307  *
1308  *      True if the whole path of the file matches pattern using
1309  *      regular expression.
1310  */
1311 int
1312 f_regex(PLAN *plan, FTSENT *entry)
1313 {
1314         char *str;
1315         int len;
1316         regex_t *pre;
1317         regmatch_t pmatch;
1318         int errcode;
1319         char errbuf[LINE_MAX];
1320         int matched;
1321
1322         pre = plan->re_data;
1323         str = entry->fts_path;
1324         len = strlen(str);
1325         matched = 0;
1326
1327         pmatch.rm_so = 0;
1328         pmatch.rm_eo = len;
1329
1330         errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND);
1331
1332         if (errcode != 0 && errcode != REG_NOMATCH) {
1333                 regerror(errcode, pre, errbuf, sizeof errbuf);
1334                 errx(1, "%s: %s",
1335                      plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf);
1336         }
1337
1338         if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len)
1339                 matched = 1;
1340
1341         return matched;
1342 }
1343
1344 PLAN *
1345 c_regex(OPTION *option, char ***argvp)
1346 {
1347         PLAN *new;
1348         char *pattern;
1349         regex_t *pre;
1350         int errcode;
1351         char errbuf[LINE_MAX];
1352
1353         if ((pre = malloc(sizeof(regex_t))) == NULL)
1354                 err(1, NULL);
1355
1356         pattern = nextarg(option, argvp);
1357
1358         if ((errcode = regcomp(pre, pattern,
1359             regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) {
1360                 regerror(errcode, pre, errbuf, sizeof errbuf);
1361                 errx(1, "%s: %s: %s",
1362                      option->flags & F_IGNCASE ? "-iregex" : "-regex",
1363                      pattern, errbuf);
1364         }
1365
1366         new = palloc(option);
1367         new->re_data = pre;
1368
1369         return new;
1370 }
1371
1372 /* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */
1373
1374 PLAN *
1375 c_simple(OPTION *option, char ***argvp __unused)
1376 {
1377         return palloc(option);
1378 }
1379
1380 /*
1381  * -size n[c] functions --
1382  *
1383  *      True if the file size in bytes, divided by an implementation defined
1384  *      value and rounded up to the next integer, is n.  If n is followed by
1385  *      one of c k M G T P, the size is in bytes, kilobytes,
1386  *      megabytes, gigabytes, terabytes or petabytes respectively.
1387  */
1388 #define FIND_SIZE       512
1389 static int divsize = 1;
1390
1391 int
1392 f_size(PLAN *plan, FTSENT *entry)
1393 {
1394         off_t size;
1395
1396         size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1397             FIND_SIZE : entry->fts_statp->st_size;
1398         COMPARE(size, plan->o_data);
1399 }
1400
1401 PLAN *
1402 c_size(OPTION *option, char ***argvp)
1403 {
1404         char *size_str;
1405         PLAN *new;
1406         char endch;
1407         off_t scale;
1408
1409         size_str = nextarg(option, argvp);
1410         ftsoptions &= ~FTS_NOSTAT;
1411
1412         new = palloc(option);
1413         endch = 'c';
1414         new->o_data = find_parsenum(new, option->name, size_str, &endch);
1415         if (endch != '\0') {
1416                 divsize = 0;
1417
1418                 switch (endch) {
1419                 case 'c':                       /* characters */
1420                         scale = 0x1LL;
1421                         break;
1422                 case 'k':                       /* kilobytes 1<<10 */
1423                         scale = 0x400LL;
1424                         break;
1425                 case 'M':                       /* megabytes 1<<20 */
1426                         scale = 0x100000LL;
1427                         break;
1428                 case 'G':                       /* gigabytes 1<<30 */
1429                         scale = 0x40000000LL;
1430                         break;
1431                 case 'T':                       /* terabytes 1<<40 */
1432                         scale = 0x1000000000LL;
1433                         break;
1434                 case 'P':                       /* petabytes 1<<50 */
1435                         scale = 0x4000000000000LL;
1436                         break;
1437                 default:
1438                         errx(1, "%s: %s: illegal trailing character",
1439                                 option->name, size_str);
1440                         break;
1441                 }
1442                 if (new->o_data > QUAD_MAX / scale)
1443                         errx(1, "%s: %s: value too large",
1444                                 option->name, size_str);
1445                 new->o_data *= scale;
1446         }
1447         return new;
1448 }
1449
1450 /*
1451  * -sparse functions --
1452  *
1453  *      Check if a file is sparse by finding if it occupies fewer blocks
1454  *      than we expect based on its size.
1455  */
1456 int
1457 f_sparse(PLAN *plan __unused, FTSENT *entry)
1458 {
1459         off_t expected_blocks;
1460
1461         expected_blocks = (entry->fts_statp->st_size + 511) / 512;
1462         return entry->fts_statp->st_blocks < expected_blocks;
1463 }
1464
1465 PLAN *
1466 c_sparse(OPTION *option, char ***argvp __unused)
1467 {
1468         ftsoptions &= ~FTS_NOSTAT;
1469
1470         return palloc(option);
1471 }
1472
1473 /*
1474  * -type c functions --
1475  *
1476  *      True if the type of the file is c, where c is b, c, d, p, f or w
1477  *      for block special file, character special file, directory, FIFO,
1478  *      regular file or whiteout respectively.
1479  */
1480 int
1481 f_type(PLAN *plan, FTSENT *entry)
1482 {
1483         return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data;
1484 }
1485
1486 PLAN *
1487 c_type(OPTION *option, char ***argvp)
1488 {
1489         char *typestring;
1490         PLAN *new;
1491         mode_t  mask;
1492
1493         typestring = nextarg(option, argvp);
1494         ftsoptions &= ~FTS_NOSTAT;
1495
1496         switch (typestring[0]) {
1497         case 'b':
1498                 mask = S_IFBLK;
1499                 break;
1500         case 'c':
1501                 mask = S_IFCHR;
1502                 break;
1503         case 'd':
1504                 mask = S_IFDIR;
1505                 break;
1506         case 'f':
1507                 mask = S_IFREG;
1508                 break;
1509         case 'l':
1510                 mask = S_IFLNK;
1511                 break;
1512         case 'p':
1513                 mask = S_IFIFO;
1514                 break;
1515         case 's':
1516                 mask = S_IFSOCK;
1517                 break;
1518 #ifdef FTS_WHITEOUT
1519         case 'w':
1520                 mask = S_IFWHT;
1521                 ftsoptions |= FTS_WHITEOUT;
1522                 break;
1523 #endif /* FTS_WHITEOUT */
1524         default:
1525                 errx(1, "%s: %s: unknown type", option->name, typestring);
1526         }
1527
1528         new = palloc(option);
1529         new->m_data = mask;
1530         return new;
1531 }
1532
1533 /*
1534  * -user uname functions --
1535  *
1536  *      True if the file belongs to the user uname.  If uname is numeric and
1537  *      an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1538  *      return a valid user name, uname is taken as a user ID.
1539  */
1540 int
1541 f_user(PLAN *plan, FTSENT *entry)
1542 {
1543         COMPARE(entry->fts_statp->st_uid, plan->u_data);
1544 }
1545
1546 PLAN *
1547 c_user(OPTION *option, char ***argvp)
1548 {
1549         char *username;
1550         PLAN *new;
1551         struct passwd *p;
1552         uid_t uid;
1553
1554         username = nextarg(option, argvp);
1555         ftsoptions &= ~FTS_NOSTAT;
1556
1557         new = palloc(option);
1558         p = getpwnam(username);
1559         if (p == NULL) {
1560                 char* cp = username;
1561                 if( username[0] == '-' || username[0] == '+' )
1562                         username++;
1563                 uid = atoi(username);
1564                 if (uid == 0 && username[0] != '0')
1565                         errx(1, "%s: %s: no such user", option->name, username);
1566                 uid = find_parsenum(new, option->name, cp, NULL);
1567         } else
1568                 uid = p->pw_uid;
1569
1570         new->u_data = uid;
1571         return new;
1572 }
1573
1574 /*
1575  * -xdev functions --
1576  *
1577  *      Always true, causes find not to descend past directories that have a
1578  *      different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1579  */
1580 PLAN *
1581 c_xdev(OPTION *option, char ***argvp __unused)
1582 {
1583         ftsoptions |= FTS_XDEV;
1584
1585         return palloc(option);
1586 }
1587
1588 /*
1589  * ( expression ) functions --
1590  *
1591  *      True if expression is true.
1592  */
1593 int
1594 f_expr(PLAN *plan, FTSENT *entry)
1595 {
1596         PLAN *p;
1597         int state = 0;
1598
1599         for (p = plan->p_data[0];
1600             p && (state = (p->execute)(p, entry)); p = p->next);
1601         return state;
1602 }
1603
1604 /*
1605  * f_openparen and f_closeparen nodes are temporary place markers.  They are
1606  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1607  * to a f_expr node containing the expression and the ')' node is discarded.
1608  * The functions themselves are only used as constants.
1609  */
1610
1611 int
1612 f_openparen(PLAN *plan __unused, FTSENT *entry __unused)
1613 {
1614         abort();
1615 }
1616
1617 int
1618 f_closeparen(PLAN *plan __unused, FTSENT *entry __unused)
1619 {
1620         abort();
1621 }
1622
1623 /* c_openparen == c_simple */
1624 /* c_closeparen == c_simple */
1625
1626 /*
1627  * AND operator. Since AND is implicit, no node is allocated.
1628  */
1629 PLAN *
1630 c_and(OPTION *option __unused, char ***argvp __unused)
1631 {
1632         return NULL;
1633 }
1634
1635 /*
1636  * ! expression functions --
1637  *
1638  *      Negation of a primary; the unary NOT operator.
1639  */
1640 int
1641 f_not(PLAN *plan, FTSENT *entry)
1642 {
1643         PLAN *p;
1644         int state = 0;
1645
1646         for (p = plan->p_data[0];
1647             p && (state = (p->execute)(p, entry)); p = p->next);
1648         return !state;
1649 }
1650
1651 /* c_not == c_simple */
1652
1653 /*
1654  * expression -o expression functions --
1655  *
1656  *      Alternation of primaries; the OR operator.  The second expression is
1657  * not evaluated if the first expression is true.
1658  */
1659 int
1660 f_or(PLAN *plan, FTSENT *entry)
1661 {
1662         PLAN *p;
1663         int state = 0;
1664
1665         for (p = plan->p_data[0];
1666             p && (state = (p->execute)(p, entry)); p = p->next);
1667
1668         if (state)
1669                 return 1;
1670
1671         for (p = plan->p_data[1];
1672             p && (state = (p->execute)(p, entry)); p = p->next);
1673         return state;
1674 }
1675
1676 /* c_or == c_simple */
1677
1678 /*
1679  * -false
1680  *
1681  *      Always false.
1682  */
1683 int
1684 f_false(PLAN *plan __unused, FTSENT *entry __unused)
1685 {
1686         return 0;
1687 }
1688
1689 /* c_false == c_simple */
1690
1691 /*
1692  * -quit
1693  *
1694  *      Exits the program
1695  */
1696 int
1697 f_quit(PLAN *plan __unused, FTSENT *entry __unused)
1698 {
1699         exit(0);
1700 }
1701
1702 /* c_quit == c_simple */