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

1.16    ! millert     1: /*     $OpenBSD: function.c,v 1.15 1999/10/04 21:17:32 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.16    ! millert    41: static char rcsid[] = "$OpenBSD: function.c,v 1.15 1999/10/04 21:17:32 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)) {
1.16    ! millert   456:                        (void) close(fd);
1.15      millert   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");
1.16    ! millert   487:                        (void) close(fd);
1.15      millert   488:                        return (0);
                    489:                }
1.16    ! millert   490:                (void) close(fd);
1.15      millert   491:        }
                    492:
1.8       millert   493:        pid = waitpid(pid, &status, 0);
                    494:        return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
                    495: }
                    496:
                    497: /*
                    498:  * c_execdir --
                    499:  *     build three parallel arrays, one with pointers to the strings passed
                    500:  *     on the command line, one with (possibly duplicated) pointers to the
                    501:  *     argv array, and one with integer values that are lengths of the
                    502:  *     strings, but also flags meaning that the string has to be massaged.
                    503:  */
                    504: PLAN *
                    505: c_execdir(argvp)
                    506:        char ***argvp;
                    507: {
                    508:        PLAN *new;                      /* node returned */
                    509:        register int cnt;
                    510:        register char **argv, **ap, *p;
                    511:
                    512:        ftsoptions &= ~FTS_NOSTAT;
                    513:        isoutput = 1;
                    514:
                    515:        new = palloc(N_EXECDIR, f_execdir);
                    516:
                    517:        for (ap = argv = *argvp;; ++ap) {
                    518:                if (!*ap)
                    519:                        errx(1,
                    520:                            "-execdir: no terminating \";\"");
1.1       deraadt   521:                if (**ap == ';')
                    522:                        break;
                    523:        }
                    524:
                    525:        cnt = ap - *argvp + 1;
                    526:        new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
                    527:        new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
                    528:        new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
                    529:
                    530:        for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
                    531:                new->e_orig[cnt] = *argv;
                    532:                for (p = *argv; *p; ++p)
                    533:                        if (p[0] == '{' && p[1] == '}') {
                    534:                                new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
                    535:                                new->e_len[cnt] = MAXPATHLEN;
                    536:                                break;
                    537:                        }
                    538:                if (!*p) {
                    539:                        new->e_argv[cnt] = *argv;
                    540:                        new->e_len[cnt] = 0;
                    541:                }
                    542:        }
                    543:        new->e_argv[cnt] = new->e_orig[cnt] = NULL;
                    544:
                    545:        *argvp = argv + 1;
                    546:        return (new);
                    547: }
                    548:
                    549: /*
                    550:  * -follow functions --
                    551:  *
                    552:  *     Always true, causes symbolic links to be followed on a global
                    553:  *     basis.
                    554:  */
                    555: PLAN *
                    556: c_follow()
                    557: {
                    558:        ftsoptions &= ~FTS_PHYSICAL;
                    559:        ftsoptions |= FTS_LOGICAL;
                    560:
                    561:        return (palloc(N_FOLLOW, f_always_true));
                    562: }
                    563:
                    564: /*
                    565:  * -fstype functions --
                    566:  *
                    567:  *     True if the file is of a certain type.
                    568:  */
                    569: int
                    570: f_fstype(plan, entry)
                    571:        PLAN *plan;
                    572:        FTSENT *entry;
                    573: {
                    574:        static dev_t curdev;    /* need a guaranteed illegal dev value */
                    575:        static int first = 1;
                    576:        struct statfs sb;
                    577:        static short val;
                    578:        static char fstype[MFSNAMELEN];
                    579:        char *p, save[2];
                    580:
                    581:        /* Only check when we cross mount point. */
                    582:        if (first || curdev != entry->fts_statp->st_dev) {
                    583:                curdev = entry->fts_statp->st_dev;
                    584:
                    585:                /*
                    586:                 * Statfs follows symlinks; find wants the link's file system,
                    587:                 * not where it points.
                    588:                 */
                    589:                if (entry->fts_info == FTS_SL ||
                    590:                    entry->fts_info == FTS_SLNONE) {
1.9       millert   591:                        if ((p = strrchr(entry->fts_accpath, '/')))
1.1       deraadt   592:                                ++p;
                    593:                        else
                    594:                                p = entry->fts_accpath;
                    595:                        save[0] = p[0];
                    596:                        p[0] = '.';
                    597:                        save[1] = p[1];
                    598:                        p[1] = '\0';
                    599:
                    600:                } else
                    601:                        p = NULL;
                    602:
                    603:                if (statfs(entry->fts_accpath, &sb))
                    604:                        err(1, "%s", entry->fts_accpath);
                    605:
                    606:                if (p) {
                    607:                        p[0] = save[0];
                    608:                        p[1] = save[1];
                    609:                }
                    610:
                    611:                first = 0;
                    612:
                    613:                /*
                    614:                 * Further tests may need both of these values, so
                    615:                 * always copy both of them.
                    616:                 */
                    617:                val = sb.f_flags;
                    618:                strncpy(fstype, sb.f_fstypename, MFSNAMELEN);
                    619:        }
                    620:        switch (plan->flags) {
                    621:        case F_MTFLAG:
                    622:                return (val & plan->mt_data);
                    623:        case F_MTTYPE:
                    624:                return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
                    625:        default:
                    626:                abort();
                    627:        }
                    628: }
                    629:
                    630: PLAN *
                    631: c_fstype(arg)
                    632:        char *arg;
                    633: {
                    634:        register PLAN *new;
                    635:
                    636:        ftsoptions &= ~FTS_NOSTAT;
                    637:
                    638:        new = palloc(N_FSTYPE, f_fstype);
                    639:        switch (*arg) {
                    640:        case 'l':
                    641:                if (!strcmp(arg, "local")) {
                    642:                        new->flags = F_MTFLAG;
                    643:                        new->mt_data = MNT_LOCAL;
                    644:                        return (new);
                    645:                }
                    646:                break;
                    647:        case 'r':
                    648:                if (!strcmp(arg, "rdonly")) {
                    649:                        new->flags = F_MTFLAG;
                    650:                        new->mt_data = MNT_RDONLY;
                    651:                        return (new);
                    652:                }
                    653:                break;
                    654:        }
                    655:
                    656:        new->flags = F_MTTYPE;
                    657:        new->c_data = arg;
                    658:        return (new);
                    659: }
                    660:
                    661: /*
                    662:  * -group gname functions --
                    663:  *
                    664:  *     True if the file belongs to the group gname.  If gname is numeric and
                    665:  *     an equivalent of the getgrnam() function does not return a valid group
                    666:  *     name, gname is taken as a group ID.
                    667:  */
                    668: int
                    669: f_group(plan, entry)
                    670:        PLAN *plan;
                    671:        FTSENT *entry;
                    672: {
                    673:        return (entry->fts_statp->st_gid == plan->g_data);
                    674: }
                    675:
                    676: PLAN *
                    677: c_group(gname)
                    678:        char *gname;
                    679: {
                    680:        PLAN *new;
                    681:        struct group *g;
                    682:        gid_t gid;
                    683:
                    684:        ftsoptions &= ~FTS_NOSTAT;
                    685:
                    686:        g = getgrnam(gname);
                    687:        if (g == NULL) {
                    688:                gid = atoi(gname);
                    689:                if (gid == 0 && gname[0] != '0')
                    690:                        errx(1, "-group: %s: no such group", gname);
                    691:        } else
                    692:                gid = g->gr_gid;
                    693:
                    694:        new = palloc(N_GROUP, f_group);
                    695:        new->g_data = gid;
                    696:        return (new);
                    697: }
                    698:
                    699: /*
                    700:  * -inum n functions --
                    701:  *
                    702:  *     True if the file has inode # n.
                    703:  */
                    704: int
                    705: f_inum(plan, entry)
                    706:        PLAN *plan;
                    707:        FTSENT *entry;
                    708: {
                    709:        COMPARE(entry->fts_statp->st_ino, plan->i_data);
                    710: }
                    711:
                    712: PLAN *
                    713: c_inum(arg)
                    714:        char *arg;
                    715: {
                    716:        PLAN *new;
                    717:
                    718:        ftsoptions &= ~FTS_NOSTAT;
                    719:
                    720:        new = palloc(N_INUM, f_inum);
                    721:        new->i_data = find_parsenum(new, "-inum", arg, NULL);
                    722:        return (new);
                    723: }
                    724:
                    725: /*
                    726:  * -links n functions --
                    727:  *
                    728:  *     True if the file has n links.
                    729:  */
                    730: int
                    731: f_links(plan, entry)
                    732:        PLAN *plan;
                    733:        FTSENT *entry;
                    734: {
                    735:        COMPARE(entry->fts_statp->st_nlink, plan->l_data);
                    736: }
                    737:
                    738: PLAN *
                    739: c_links(arg)
                    740:        char *arg;
                    741: {
                    742:        PLAN *new;
                    743:
                    744:        ftsoptions &= ~FTS_NOSTAT;
                    745:
                    746:        new = palloc(N_LINKS, f_links);
                    747:        new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
                    748:        return (new);
                    749: }
                    750:
                    751: /*
                    752:  * -ls functions --
                    753:  *
                    754:  *     Always true - prints the current entry to stdout in "ls" format.
                    755:  */
                    756: int
                    757: f_ls(plan, entry)
                    758:        PLAN *plan;
                    759:        FTSENT *entry;
                    760: {
                    761:        printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
                    762:        return (1);
                    763: }
                    764:
                    765: PLAN *
                    766: c_ls()
                    767: {
                    768:        ftsoptions &= ~FTS_NOSTAT;
                    769:        isoutput = 1;
                    770:
                    771:        return (palloc(N_LS, f_ls));
1.5       tholo     772: }
                    773:
                    774: /*
                    775:  * - maxdepth n functions --
                    776:  *
                    777:  *     True if the current search depth is less than or equal to the
                    778:  *     maximum depth specified
                    779:  */
                    780: int
1.6       tholo     781: f_maxdepth(plan, entry)
1.5       tholo     782:        PLAN *plan;
                    783:        FTSENT *entry;
                    784: {
                    785:
1.6       tholo     786:        if (entry->fts_level >= plan->max_data)
1.5       tholo     787:                fts_set(tree, entry, FTS_SKIP);
1.6       tholo     788:        return (entry->fts_level <= plan->max_data);
1.5       tholo     789: }
                    790:
                    791: PLAN *
1.6       tholo     792: c_maxdepth(arg)
1.5       tholo     793:        char *arg;
                    794: {
                    795:        PLAN *new;
                    796:
1.6       tholo     797:        new = palloc(N_MAXDEPTH, f_maxdepth);
                    798:        new->max_data = atoi(arg);
                    799:        return (new);
                    800: }
                    801:
                    802: /*
                    803:  * - mindepth n functions --
                    804:  *
                    805:  *     True if the current search depth is greater than or equal to the
                    806:  *     minimum depth specified
                    807:  */
                    808: int
                    809: f_mindepth(plan, entry)
                    810:        PLAN *plan;
                    811:        FTSENT *entry;
                    812: {
                    813:
                    814:        return (entry->fts_level >= plan->min_data);
                    815: }
                    816:
                    817: PLAN *
                    818: c_mindepth(arg)
                    819:        char *arg;
                    820: {
                    821:        PLAN *new;
                    822:
                    823:        new = palloc(N_MINDEPTH, f_mindepth);
                    824:        new->min_data = atoi(arg);
1.5       tholo     825:        return (new);
1.1       deraadt   826: }
                    827:
                    828: /*
                    829:  * -mtime n functions --
                    830:  *
                    831:  *     True if the difference between the file modification time and the
                    832:  *     current time is n 24 hour periods.
                    833:  */
                    834: int
                    835: f_mtime(plan, entry)
                    836:        PLAN *plan;
                    837:        FTSENT *entry;
                    838: {
                    839:
                    840:        COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
                    841:            SECSPERDAY, plan->t_data);
                    842: }
                    843:
                    844: PLAN *
                    845: c_mtime(arg)
                    846:        char *arg;
                    847: {
                    848:        PLAN *new;
                    849:
                    850:        ftsoptions &= ~FTS_NOSTAT;
                    851:
                    852:        new = palloc(N_MTIME, f_mtime);
                    853:        new->t_data = find_parsenum(new, "-mtime", arg, NULL);
                    854:        TIME_CORRECT(new, N_MTIME);
1.11      deraadt   855:        return (new);
                    856: }
                    857:
                    858: /*
                    859:  * -mmin n functions --
                    860:  *
                    861:  *     True if the difference between the file modification time and the
                    862:  *     current time is n min periods.
                    863:  */
                    864: int
                    865: f_mmin(plan, entry)
                    866:        PLAN *plan;
                    867:        FTSENT *entry;
                    868: {
                    869:        extern time_t now;
                    870:
                    871:        COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /
                    872:            60, plan->t_data);
                    873: }
                    874:
                    875: PLAN *
                    876: c_mmin(arg)
                    877:        char *arg;
                    878: {
                    879:        PLAN *new;
                    880:
                    881:        ftsoptions &= ~FTS_NOSTAT;
                    882:
                    883:        new = palloc(N_MMIN, f_mmin);
                    884:        new->t_data = find_parsenum(new, "-mmin", arg, NULL);
                    885:        TIME_CORRECT(new, N_MMIN);
1.1       deraadt   886:        return (new);
                    887: }
                    888:
                    889: /*
                    890:  * -name functions --
                    891:  *
                    892:  *     True if the basename of the filename being examined
                    893:  *     matches pattern using Pattern Matching Notation S3.14
                    894:  */
                    895: int
                    896: f_name(plan, entry)
                    897:        PLAN *plan;
                    898:        FTSENT *entry;
                    899: {
                    900:        return (!fnmatch(plan->c_data, entry->fts_name, 0));
                    901: }
                    902:
                    903: PLAN *
                    904: c_name(pattern)
                    905:        char *pattern;
                    906: {
                    907:        PLAN *new;
                    908:
                    909:        new = palloc(N_NAME, f_name);
                    910:        new->c_data = pattern;
                    911:        return (new);
                    912: }
                    913:
                    914: /*
                    915:  * -newer file functions --
                    916:  *
                    917:  *     True if the current file has been modified more recently
                    918:  *     then the modification time of the file named by the pathname
                    919:  *     file.
                    920:  */
                    921: int
                    922: f_newer(plan, entry)
                    923:        PLAN *plan;
                    924:        FTSENT *entry;
                    925: {
                    926:        return (entry->fts_statp->st_mtime > plan->t_data);
                    927: }
                    928:
                    929: PLAN *
                    930: c_newer(filename)
                    931:        char *filename;
                    932: {
                    933:        PLAN *new;
                    934:        struct stat sb;
                    935:
                    936:        ftsoptions &= ~FTS_NOSTAT;
                    937:
                    938:        if (stat(filename, &sb))
                    939:                err(1, "%s", filename);
                    940:        new = palloc(N_NEWER, f_newer);
                    941:        new->t_data = sb.st_mtime;
                    942:        return (new);
                    943: }
                    944:
                    945: /*
                    946:  * -nogroup functions --
                    947:  *
                    948:  *     True if file belongs to a user ID for which the equivalent
                    949:  *     of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
                    950:  */
                    951: int
                    952: f_nogroup(plan, entry)
                    953:        PLAN *plan;
                    954:        FTSENT *entry;
                    955: {
                    956:        char *group_from_gid();
                    957:
                    958:        return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
                    959: }
                    960:
                    961: PLAN *
                    962: c_nogroup()
                    963: {
                    964:        ftsoptions &= ~FTS_NOSTAT;
                    965:
                    966:        return (palloc(N_NOGROUP, f_nogroup));
                    967: }
                    968:
                    969: /*
                    970:  * -nouser functions --
                    971:  *
                    972:  *     True if file belongs to a user ID for which the equivalent
                    973:  *     of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
                    974:  */
                    975: int
                    976: f_nouser(plan, entry)
                    977:        PLAN *plan;
                    978:        FTSENT *entry;
                    979: {
                    980:        char *user_from_uid();
                    981:
                    982:        return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
                    983: }
                    984:
                    985: PLAN *
                    986: c_nouser()
                    987: {
                    988:        ftsoptions &= ~FTS_NOSTAT;
                    989:
                    990:        return (palloc(N_NOUSER, f_nouser));
                    991: }
                    992:
                    993: /*
                    994:  * -path functions --
                    995:  *
                    996:  *     True if the path of the filename being examined
                    997:  *     matches pattern using Pattern Matching Notation S3.14
                    998:  */
                    999: int
                   1000: f_path(plan, entry)
                   1001:        PLAN *plan;
                   1002:        FTSENT *entry;
                   1003: {
                   1004:        return (!fnmatch(plan->c_data, entry->fts_path, 0));
                   1005: }
                   1006:
                   1007: PLAN *
                   1008: c_path(pattern)
                   1009:        char *pattern;
                   1010: {
                   1011:        PLAN *new;
                   1012:
                   1013:        new = palloc(N_NAME, f_path);
                   1014:        new->c_data = pattern;
                   1015:        return (new);
                   1016: }
                   1017:
                   1018: /*
                   1019:  * -perm functions --
                   1020:  *
                   1021:  *     The mode argument is used to represent file mode bits.  If it starts
                   1022:  *     with a leading digit, it's treated as an octal mode, otherwise as a
                   1023:  *     symbolic mode.
                   1024:  */
                   1025: int
                   1026: f_perm(plan, entry)
                   1027:        PLAN *plan;
                   1028:        FTSENT *entry;
                   1029: {
                   1030:        mode_t mode;
                   1031:
                   1032:        mode = entry->fts_statp->st_mode &
                   1033:            (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
                   1034:        if (plan->flags == F_ATLEAST)
                   1035:                return ((plan->m_data | mode) == mode);
                   1036:        else
                   1037:                return (mode == plan->m_data);
                   1038:        /* NOTREACHED */
                   1039: }
                   1040:
                   1041: PLAN *
                   1042: c_perm(perm)
                   1043:        char *perm;
                   1044: {
                   1045:        PLAN *new;
                   1046:        mode_t *set;
                   1047:
                   1048:        ftsoptions &= ~FTS_NOSTAT;
                   1049:
                   1050:        new = palloc(N_PERM, f_perm);
                   1051:
                   1052:        if (*perm == '-') {
                   1053:                new->flags = F_ATLEAST;
                   1054:                ++perm;
                   1055:        }
                   1056:
                   1057:        if ((set = setmode(perm)) == NULL)
                   1058:                err(1, "-perm: %s: illegal mode string", perm);
                   1059:
                   1060:        new->m_data = getmode(set, 0);
1.12      deraadt  1061:        free(set);
1.1       deraadt  1062:        return (new);
                   1063: }
                   1064:
                   1065: /*
                   1066:  * -print functions --
                   1067:  *
                   1068:  *     Always true, causes the current pathame to be written to
                   1069:  *     standard output.
                   1070:  */
                   1071: int
                   1072: f_print(plan, entry)
                   1073:        PLAN *plan;
                   1074:        FTSENT *entry;
                   1075: {
                   1076:        (void)printf("%s\n", entry->fts_path);
                   1077:        return(1);
                   1078: }
                   1079:
                   1080: /* ARGSUSED */
1.9       millert  1081: int
1.1       deraadt  1082: f_print0(plan, entry)
                   1083:        PLAN *plan;
                   1084:        FTSENT *entry;
                   1085: {
                   1086:        (void)fputs(entry->fts_path, stdout);
                   1087:        (void)fputc('\0', stdout);
                   1088:        return(1);
                   1089: }
                   1090:
                   1091: PLAN *
                   1092: c_print()
                   1093: {
                   1094:        isoutput = 1;
                   1095:
                   1096:        return(palloc(N_PRINT, f_print));
                   1097: }
                   1098:
                   1099: PLAN *
                   1100: c_print0()
                   1101: {
                   1102:        isoutput = 1;
                   1103:
                   1104:        return(palloc(N_PRINT0, f_print0));
                   1105: }
                   1106:
                   1107: /*
                   1108:  * -prune functions --
                   1109:  *
                   1110:  *     Prune a portion of the hierarchy.
                   1111:  */
                   1112: int
                   1113: f_prune(plan, entry)
                   1114:        PLAN *plan;
                   1115:        FTSENT *entry;
                   1116: {
                   1117:
                   1118:        if (fts_set(tree, entry, FTS_SKIP))
                   1119:                err(1, "%s", entry->fts_path);
                   1120:        return (1);
                   1121: }
                   1122:
                   1123: PLAN *
                   1124: c_prune()
                   1125: {
                   1126:        return (palloc(N_PRUNE, f_prune));
                   1127: }
                   1128:
                   1129: /*
                   1130:  * -size n[c] functions --
                   1131:  *
                   1132:  *     True if the file size in bytes, divided by an implementation defined
                   1133:  *     value and rounded up to the next integer, is n.  If n is followed by
                   1134:  *     a c, the size is in bytes.
                   1135:  */
                   1136: #define        FIND_SIZE       512
                   1137: static int divsize = 1;
                   1138:
                   1139: int
                   1140: f_size(plan, entry)
                   1141:        PLAN *plan;
                   1142:        FTSENT *entry;
                   1143: {
                   1144:        off_t size;
                   1145:
                   1146:        size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
                   1147:            FIND_SIZE : entry->fts_statp->st_size;
                   1148:        COMPARE(size, plan->o_data);
                   1149: }
                   1150:
                   1151: PLAN *
                   1152: c_size(arg)
                   1153:        char *arg;
                   1154: {
                   1155:        PLAN *new;
                   1156:        char endch;
                   1157:
                   1158:        ftsoptions &= ~FTS_NOSTAT;
                   1159:
                   1160:        new = palloc(N_SIZE, f_size);
                   1161:        endch = 'c';
                   1162:        new->o_data = find_parsenum(new, "-size", arg, &endch);
                   1163:        if (endch == 'c')
                   1164:                divsize = 0;
                   1165:        return (new);
                   1166: }
                   1167:
                   1168: /*
                   1169:  * -type c functions --
                   1170:  *
                   1171:  *     True if the type of the file is c, where c is b, c, d, p, or f for
                   1172:  *     block special file, character special file, directory, FIFO, or
                   1173:  *     regular file, respectively.
                   1174:  */
                   1175: int
                   1176: f_type(plan, entry)
                   1177:        PLAN *plan;
                   1178:        FTSENT *entry;
                   1179: {
                   1180:        return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
                   1181: }
                   1182:
                   1183: PLAN *
                   1184: c_type(typestring)
                   1185:        char *typestring;
                   1186: {
                   1187:        PLAN *new;
                   1188:        mode_t  mask;
                   1189:
                   1190:        ftsoptions &= ~FTS_NOSTAT;
                   1191:
                   1192:        switch (typestring[0]) {
1.2       deraadt  1193: #ifdef S_IFWHT
1.3       deraadt  1194:        case 'W':
                   1195:                mask = S_IFWHT;
1.13      espie    1196:                if ((ftsoptions & FTS_WHITEOUT) == 0)
                   1197:                        warnx("-type W without -W is a no-op");
1.3       deraadt  1198:                break;
1.2       deraadt  1199: #endif
1.1       deraadt  1200:        case 'b':
                   1201:                mask = S_IFBLK;
                   1202:                break;
                   1203:        case 'c':
                   1204:                mask = S_IFCHR;
                   1205:                break;
                   1206:        case 'd':
                   1207:                mask = S_IFDIR;
                   1208:                break;
                   1209:        case 'f':
                   1210:                mask = S_IFREG;
                   1211:                break;
                   1212:        case 'l':
                   1213:                mask = S_IFLNK;
                   1214:                break;
                   1215:        case 'p':
                   1216:                mask = S_IFIFO;
                   1217:                break;
                   1218:        case 's':
                   1219:                mask = S_IFSOCK;
                   1220:                break;
                   1221:        default:
                   1222:                errx(1, "-type: %s: unknown type", typestring);
                   1223:        }
                   1224:
                   1225:        new = palloc(N_TYPE, f_type);
                   1226:        new->m_data = mask;
                   1227:        return (new);
                   1228: }
                   1229:
                   1230: /*
                   1231:  * -user uname functions --
                   1232:  *
                   1233:  *     True if the file belongs to the user uname.  If uname is numeric and
                   1234:  *     an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
                   1235:  *     return a valid user name, uname is taken as a user ID.
                   1236:  */
                   1237: int
                   1238: f_user(plan, entry)
                   1239:        PLAN *plan;
                   1240:        FTSENT *entry;
                   1241: {
                   1242:        return (entry->fts_statp->st_uid == plan->u_data);
                   1243: }
                   1244:
                   1245: PLAN *
                   1246: c_user(username)
                   1247:        char *username;
                   1248: {
                   1249:        PLAN *new;
                   1250:        struct passwd *p;
                   1251:        uid_t uid;
                   1252:
                   1253:        ftsoptions &= ~FTS_NOSTAT;
                   1254:
                   1255:        p = getpwnam(username);
                   1256:        if (p == NULL) {
                   1257:                uid = atoi(username);
                   1258:                if (uid == 0 && username[0] != '0')
                   1259:                        errx(1, "-user: %s: no such user", username);
                   1260:        } else
                   1261:                uid = p->pw_uid;
                   1262:
                   1263:        new = palloc(N_USER, f_user);
                   1264:        new->u_data = uid;
                   1265:        return (new);
                   1266: }
                   1267:
                   1268: /*
                   1269:  * -xdev functions --
                   1270:  *
                   1271:  *     Always true, causes find not to decend past directories that have a
                   1272:  *     different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
                   1273:  */
                   1274: PLAN *
                   1275: c_xdev()
                   1276: {
                   1277:        ftsoptions |= FTS_XDEV;
                   1278:
                   1279:        return (palloc(N_XDEV, f_always_true));
                   1280: }
                   1281:
                   1282: /*
                   1283:  * ( expression ) functions --
                   1284:  *
                   1285:  *     True if expression is true.
                   1286:  */
                   1287: int
                   1288: f_expr(plan, entry)
                   1289:        PLAN *plan;
                   1290:        FTSENT *entry;
                   1291: {
                   1292:        register PLAN *p;
                   1293:        register int state;
                   1294:
                   1295:        for (p = plan->p_data[0];
                   1296:            p && (state = (p->eval)(p, entry)); p = p->next);
                   1297:        return (state);
                   1298: }
                   1299:
                   1300: /*
                   1301:  * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
                   1302:  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
                   1303:  * to a N_EXPR node containing the expression and the ')' node is discarded.
                   1304:  */
                   1305: PLAN *
                   1306: c_openparen()
                   1307: {
                   1308:        return (palloc(N_OPENPAREN, (int (*)())-1));
                   1309: }
                   1310:
                   1311: PLAN *
                   1312: c_closeparen()
                   1313: {
                   1314:        return (palloc(N_CLOSEPAREN, (int (*)())-1));
                   1315: }
                   1316:
                   1317: /*
                   1318:  * ! expression functions --
                   1319:  *
                   1320:  *     Negation of a primary; the unary NOT operator.
                   1321:  */
                   1322: int
                   1323: f_not(plan, entry)
                   1324:        PLAN *plan;
                   1325:        FTSENT *entry;
                   1326: {
                   1327:        register PLAN *p;
                   1328:        register int state;
                   1329:
                   1330:        for (p = plan->p_data[0];
                   1331:            p && (state = (p->eval)(p, entry)); p = p->next);
                   1332:        return (!state);
                   1333: }
                   1334:
                   1335: PLAN *
                   1336: c_not()
                   1337: {
                   1338:        return (palloc(N_NOT, f_not));
                   1339: }
                   1340:
                   1341: /*
                   1342:  * expression -o expression functions --
                   1343:  *
                   1344:  *     Alternation of primaries; the OR operator.  The second expression is
                   1345:  * not evaluated if the first expression is true.
                   1346:  */
                   1347: int
                   1348: f_or(plan, entry)
                   1349:        PLAN *plan;
                   1350:        FTSENT *entry;
                   1351: {
                   1352:        register PLAN *p;
                   1353:        register int state;
                   1354:
                   1355:        for (p = plan->p_data[0];
                   1356:            p && (state = (p->eval)(p, entry)); p = p->next);
                   1357:
                   1358:        if (state)
                   1359:                return (1);
                   1360:
                   1361:        for (p = plan->p_data[1];
                   1362:            p && (state = (p->eval)(p, entry)); p = p->next);
                   1363:        return (state);
                   1364: }
                   1365:
                   1366: PLAN *
                   1367: c_or()
                   1368: {
                   1369:        return (palloc(N_OR, f_or));
                   1370: }
                   1371:
                   1372: static PLAN *
                   1373: palloc(t, f)
                   1374:        enum ntype t;
                   1375:        int (*f) __P((PLAN *, FTSENT *));
                   1376: {
                   1377:        PLAN *new;
                   1378:
1.9       millert  1379:        if ((new = malloc(sizeof(PLAN)))) {
1.1       deraadt  1380:                new->type = t;
                   1381:                new->eval = f;
                   1382:                new->flags = 0;
                   1383:                new->next = NULL;
                   1384:                return (new);
                   1385:        }
                   1386:        err(1, NULL);
                   1387:        /* NOTREACHED */
                   1388: }