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

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