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

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