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

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