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

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