[BACK]Return to function.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / find

Annotation of src/usr.bin/find/function.c, Revision 1.42

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