[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.37

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