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

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