11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1990, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * This code is derived from software contributed to Berkeley by 61590Srgrimes * Cimarron D. Taylor of the University of California, Berkeley. 71590Srgrimes * 81590Srgrimes * Redistribution and use in source and binary forms, with or without 91590Srgrimes * modification, are permitted provided that the following conditions 101590Srgrimes * are met: 111590Srgrimes * 1. Redistributions of source code must retain the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer. 131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer in the 151590Srgrimes * documentation and/or other materials provided with the distribution. 161590Srgrimes * 4. Neither the name of the University nor the names of its contributors 171590Srgrimes * may be used to endorse or promote products derived from this software 181590Srgrimes * without specific prior written permission. 191590Srgrimes * 201590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301590Srgrimes * SUCH DAMAGE. 311590Srgrimes */ 321590Srgrimes 33207705Sdelphij#ifndef lint 34207705Sdelphij#if 0 35207705Sdelphijstatic const char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95"; 36207705Sdelphij#endif 37207705Sdelphij#endif /* not lint */ 38207705Sdelphij 3993604Sobrien#include <sys/cdefs.h> 4093604Sobrien__FBSDID("$FreeBSD: releng/10.3/usr.bin/find/function.c 272922 2014-10-11 00:30:57Z emaste $"); 411590Srgrimes 421590Srgrimes#include <sys/param.h> 431590Srgrimes#include <sys/ucred.h> 441590Srgrimes#include <sys/stat.h> 45127796Sbmilekic#include <sys/types.h> 46127796Sbmilekic#include <sys/acl.h> 471590Srgrimes#include <sys/wait.h> 481590Srgrimes#include <sys/mount.h> 491590Srgrimes 5071422Speter#include <dirent.h> 511590Srgrimes#include <err.h> 521590Srgrimes#include <errno.h> 531590Srgrimes#include <fnmatch.h> 541590Srgrimes#include <fts.h> 551590Srgrimes#include <grp.h> 5697736Stjr#include <limits.h> 571590Srgrimes#include <pwd.h> 5872945Sknu#include <regex.h> 591590Srgrimes#include <stdio.h> 601590Srgrimes#include <stdlib.h> 611590Srgrimes#include <string.h> 621590Srgrimes#include <unistd.h> 63129812Seik#include <ctype.h> 641590Srgrimes 651590Srgrimes#include "find.h" 661590Srgrimes 6792786Smarkmstatic PLAN *palloc(OPTION *); 6892786Smarkmstatic long long find_parsenum(PLAN *, const char *, char *, char *); 6992786Smarkmstatic long long find_parsetime(PLAN *, const char *, char *); 7092786Smarkmstatic char *nextarg(OPTION *, char ***); 7176250Sphk 7299905Stjrextern char **environ; 7399905Stjr 74158572Skrionstatic PLAN *lastexecplus = NULL; 75158572Skrion 7683450Sru#define COMPARE(a, b) do { \ 7776250Sphk switch (plan->flags & F_ELG_MASK) { \ 781590Srgrimes case F_EQUAL: \ 791590Srgrimes return (a == b); \ 801590Srgrimes case F_LESSTHAN: \ 811590Srgrimes return (a < b); \ 821590Srgrimes case F_GREATER: \ 831590Srgrimes return (a > b); \ 841590Srgrimes default: \ 851590Srgrimes abort(); \ 861590Srgrimes } \ 8783450Sru} while(0) 881590Srgrimes 8976250Sphkstatic PLAN * 90116333Smarkmpalloc(OPTION *option) 9176250Sphk{ 9276250Sphk PLAN *new; 931590Srgrimes 9476250Sphk if ((new = malloc(sizeof(PLAN))) == NULL) 9576250Sphk err(1, NULL); 9676250Sphk new->execute = option->execute; 9776250Sphk new->flags = option->flags; 9876250Sphk new->next = NULL; 9976250Sphk return new; 10076250Sphk} 10176250Sphk 1021590Srgrimes/* 1031590Srgrimes * find_parsenum -- 1041590Srgrimes * Parse a string of the form [+-]# and return the value. 1051590Srgrimes */ 10615102Sbdestatic long long 107116333Smarkmfind_parsenum(PLAN *plan, const char *option, char *vp, char *endch) 1081590Srgrimes{ 10915102Sbde long long value; 1101590Srgrimes char *endchar, *str; /* Pointer to character ending conversion. */ 1118874Srgrimes 1121590Srgrimes /* Determine comparison from leading + or -. */ 1131590Srgrimes str = vp; 1141590Srgrimes switch (*str) { 1151590Srgrimes case '+': 1161590Srgrimes ++str; 11776250Sphk plan->flags |= F_GREATER; 1181590Srgrimes break; 1191590Srgrimes case '-': 1201590Srgrimes ++str; 12176250Sphk plan->flags |= F_LESSTHAN; 1221590Srgrimes break; 1231590Srgrimes default: 12476250Sphk plan->flags |= F_EQUAL; 1251590Srgrimes break; 1261590Srgrimes } 1278874Srgrimes 1281590Srgrimes /* 12915102Sbde * Convert the string with strtoq(). Note, if strtoq() returns zero 1301590Srgrimes * and endchar points to the beginning of the string we know we have 1311590Srgrimes * a syntax error. 1321590Srgrimes */ 13315102Sbde value = strtoq(str, &endchar, 10); 1341590Srgrimes if (value == 0 && endchar == str) 1351590Srgrimes errx(1, "%s: %s: illegal numeric value", option, vp); 136158986Skrion if (endchar[0] && endch == NULL) 1371590Srgrimes errx(1, "%s: %s: illegal trailing character", option, vp); 1381590Srgrimes if (endch) 1391590Srgrimes *endch = endchar[0]; 14076250Sphk return value; 1411590Srgrimes} 1421590Srgrimes 1431590Srgrimes/* 14483450Sru * find_parsetime -- 14583450Sru * Parse a string of the form [+-]([0-9]+[smhdw]?)+ and return the value. 14683450Sru */ 14783450Srustatic long long 148116333Smarkmfind_parsetime(PLAN *plan, const char *option, char *vp) 14983450Sru{ 15083450Sru long long secs, value; 15183450Sru char *str, *unit; /* Pointer to character ending conversion. */ 15283450Sru 15383450Sru /* Determine comparison from leading + or -. */ 15483450Sru str = vp; 15583450Sru switch (*str) { 15683450Sru case '+': 15783450Sru ++str; 15883450Sru plan->flags |= F_GREATER; 15983450Sru break; 16083450Sru case '-': 16183450Sru ++str; 16283450Sru plan->flags |= F_LESSTHAN; 16383450Sru break; 16483450Sru default: 16583450Sru plan->flags |= F_EQUAL; 16683450Sru break; 16783450Sru } 16883450Sru 16983450Sru value = strtoq(str, &unit, 10); 17083450Sru if (value == 0 && unit == str) { 17183450Sru errx(1, "%s: %s: illegal time value", option, vp); 17283450Sru /* NOTREACHED */ 17383450Sru } 17483450Sru if (*unit == '\0') 17583450Sru return value; 17683450Sru 17783450Sru /* Units syntax. */ 17883450Sru secs = 0; 17983450Sru for (;;) { 18083450Sru switch(*unit) { 18183450Sru case 's': /* seconds */ 18283450Sru secs += value; 18383450Sru break; 18483450Sru case 'm': /* minutes */ 18583450Sru secs += value * 60; 18683450Sru break; 18783450Sru case 'h': /* hours */ 18883450Sru secs += value * 3600; 18983450Sru break; 19083450Sru case 'd': /* days */ 19183450Sru secs += value * 86400; 19283450Sru break; 19383450Sru case 'w': /* weeks */ 19483450Sru secs += value * 604800; 19583450Sru break; 19683450Sru default: 19783450Sru errx(1, "%s: %s: bad unit '%c'", option, vp, *unit); 19883450Sru /* NOTREACHED */ 19983450Sru } 20083450Sru str = unit + 1; 20183450Sru if (*str == '\0') /* EOS */ 20283450Sru break; 20383450Sru value = strtoq(str, &unit, 10); 20483450Sru if (value == 0 && unit == str) { 20583450Sru errx(1, "%s: %s: illegal time value", option, vp); 20683450Sru /* NOTREACHED */ 20783450Sru } 20883450Sru if (*unit == '\0') { 20983450Sru errx(1, "%s: %s: missing trailing unit", option, vp); 21083450Sru /* NOTREACHED */ 21183450Sru } 21283450Sru } 21383450Sru plan->flags |= F_EXACTTIME; 21483450Sru return secs; 21583450Sru} 21683450Sru 21783450Sru/* 21876250Sphk * nextarg -- 21976250Sphk * Check that another argument still exists, return a pointer to it, 22076250Sphk * and increment the argument vector pointer. 22176250Sphk */ 22276250Sphkstatic char * 223116333Smarkmnextarg(OPTION *option, char ***argvp) 22476250Sphk{ 22576250Sphk char *arg; 22676250Sphk 22776250Sphk if ((arg = **argvp) == 0) 22876250Sphk errx(1, "%s: requires additional arguments", option->name); 22976250Sphk (*argvp)++; 23076250Sphk return arg; 23176250Sphk} /* nextarg() */ 23276250Sphk 23376250Sphk/* 234157440Sceri * The value of n for the inode times (atime, birthtime, ctime, mtime) is a 235157440Sceri * range, i.e. n matches from (n - 1) to n 24 hour periods. This interacts 236157440Sceri * with -n, such that "-mtime -1" would be less than 0 days, which isn't what 237157440Sceri * the user wanted. Correct so that -1 is "less than 1". 2381590Srgrimes */ 23976250Sphk#define TIME_CORRECT(p) \ 24076250Sphk if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \ 241248446Sjilles ++((p)->t_data.tv_sec); 2421590Srgrimes 2431590Srgrimes/* 24476250Sphk * -[acm]min n functions -- 24530395Swosch * 24676250Sphk * True if the difference between the 24776250Sphk * file access time (-amin) 248157440Sceri * file birth time (-Bmin) 24976250Sphk * last change of file status information (-cmin) 25076250Sphk * file modification time (-mmin) 25176250Sphk * and the current time is n min periods. 25230395Swosch */ 25330395Swoschint 254116333Smarkmf_Xmin(PLAN *plan, FTSENT *entry) 25530395Swosch{ 25676250Sphk if (plan->flags & F_TIME_C) { 25776250Sphk COMPARE((now - entry->fts_statp->st_ctime + 258248446Sjilles 60 - 1) / 60, plan->t_data.tv_sec); 25976250Sphk } else if (plan->flags & F_TIME_A) { 26076250Sphk COMPARE((now - entry->fts_statp->st_atime + 261248446Sjilles 60 - 1) / 60, plan->t_data.tv_sec); 262157440Sceri } else if (plan->flags & F_TIME_B) { 263157440Sceri COMPARE((now - entry->fts_statp->st_birthtime + 264248446Sjilles 60 - 1) / 60, plan->t_data.tv_sec); 26576250Sphk } else { 26676250Sphk COMPARE((now - entry->fts_statp->st_mtime + 267248446Sjilles 60 - 1) / 60, plan->t_data.tv_sec); 26876250Sphk } 26930395Swosch} 27030395Swosch 27130395SwoschPLAN * 272116333Smarkmc_Xmin(OPTION *option, char ***argvp) 27330395Swosch{ 27476250Sphk char *nmins; 27530395Swosch PLAN *new; 27630395Swosch 27776250Sphk nmins = nextarg(option, argvp); 27830395Swosch ftsoptions &= ~FTS_NOSTAT; 27930395Swosch 28076250Sphk new = palloc(option); 281248446Sjilles new->t_data.tv_sec = find_parsenum(new, option->name, nmins, NULL); 282248446Sjilles new->t_data.tv_nsec = 0; 28376250Sphk TIME_CORRECT(new); 28476250Sphk return new; 28530395Swosch} 28630395Swosch 28730395Swosch/* 28876250Sphk * -[acm]time n functions -- 2891590Srgrimes * 29076250Sphk * True if the difference between the 29176250Sphk * file access time (-atime) 292157440Sceri * file birth time (-Btime) 29376250Sphk * last change of file status information (-ctime) 29476250Sphk * file modification time (-mtime) 29576250Sphk * and the current time is n 24 hour periods. 2961590Srgrimes */ 29776250Sphk 2981590Srgrimesint 299116333Smarkmf_Xtime(PLAN *plan, FTSENT *entry) 3001590Srgrimes{ 30183451Sru time_t xtime; 3021590Srgrimes 30383451Sru if (plan->flags & F_TIME_A) 30483451Sru xtime = entry->fts_statp->st_atime; 305157440Sceri else if (plan->flags & F_TIME_B) 306157440Sceri xtime = entry->fts_statp->st_birthtime; 30783451Sru else if (plan->flags & F_TIME_C) 30883451Sru xtime = entry->fts_statp->st_ctime; 30983451Sru else 31083451Sru xtime = entry->fts_statp->st_mtime; 31183450Sru 31283451Sru if (plan->flags & F_EXACTTIME) 313248446Sjilles COMPARE(now - xtime, plan->t_data.tv_sec); 31483451Sru else 315248446Sjilles COMPARE((now - xtime + 86400 - 1) / 86400, plan->t_data.tv_sec); 3161590Srgrimes} 3178874Srgrimes 3181590SrgrimesPLAN * 319116333Smarkmc_Xtime(OPTION *option, char ***argvp) 3201590Srgrimes{ 32183450Sru char *value; 3221590Srgrimes PLAN *new; 3231590Srgrimes 32483450Sru value = nextarg(option, argvp); 3251590Srgrimes ftsoptions &= ~FTS_NOSTAT; 3261590Srgrimes 32776250Sphk new = palloc(option); 328248446Sjilles new->t_data.tv_sec = find_parsetime(new, option->name, value); 329248446Sjilles new->t_data.tv_nsec = 0; 33083450Sru if (!(new->flags & F_EXACTTIME)) 33183450Sru TIME_CORRECT(new); 33276250Sphk return new; 3331590Srgrimes} 33430395Swosch 3351590Srgrimes/* 33676250Sphk * -maxdepth/-mindepth n functions -- 33730395Swosch * 33876250Sphk * Does the same as -prune if the level of the current file is 33976250Sphk * greater/less than the specified maximum/minimum depth. 34076250Sphk * 34176250Sphk * Note that -maxdepth and -mindepth are handled specially in 34276250Sphk * find_execute() so their f_* functions are set to f_always_true(). 34330395Swosch */ 34430395SwoschPLAN * 345116333Smarkmc_mXXdepth(OPTION *option, char ***argvp) 34630395Swosch{ 34776250Sphk char *dstr; 34830395Swosch PLAN *new; 34930395Swosch 35076250Sphk dstr = nextarg(option, argvp); 35176250Sphk if (dstr[0] == '-') 35276250Sphk /* all other errors handled by find_parsenum() */ 35376250Sphk errx(1, "%s: %s: value must be positive", option->name, dstr); 35430395Swosch 35576250Sphk new = palloc(option); 35676250Sphk if (option->flags & F_MAXDEPTH) 35776250Sphk maxdepth = find_parsenum(new, option->name, dstr, NULL); 35876250Sphk else 35976250Sphk mindepth = find_parsenum(new, option->name, dstr, NULL); 36076250Sphk return new; 36130395Swosch} 36230395Swosch 36330395Swosch/* 364127796Sbmilekic * -acl function -- 365127796Sbmilekic * 366127796Sbmilekic * Show files with EXTENDED ACL attributes. 367127796Sbmilekic */ 368127796Sbmilekicint 369140810Sssouhlalf_acl(PLAN *plan __unused, FTSENT *entry) 370127796Sbmilekic{ 371127796Sbmilekic acl_t facl; 372196839Strasz acl_type_t acl_type; 373196839Strasz int acl_supported = 0, ret, trivial; 374127796Sbmilekic 375127796Sbmilekic if (S_ISLNK(entry->fts_statp->st_mode)) 376127796Sbmilekic return 0; 377196839Strasz ret = pathconf(entry->fts_accpath, _PC_ACL_NFS4); 378196839Strasz if (ret > 0) { 379196839Strasz acl_supported = 1; 380196839Strasz acl_type = ACL_TYPE_NFS4; 381196839Strasz } else if (ret < 0 && errno != EINVAL) { 382196839Strasz warn("%s", entry->fts_accpath); 383196839Strasz return (0); 384196839Strasz } 385196839Strasz if (acl_supported == 0) { 386196839Strasz ret = pathconf(entry->fts_accpath, _PC_ACL_EXTENDED); 387196839Strasz if (ret > 0) { 388196839Strasz acl_supported = 1; 389196839Strasz acl_type = ACL_TYPE_ACCESS; 390196839Strasz } else if (ret < 0 && errno != EINVAL) { 391127796Sbmilekic warn("%s", entry->fts_accpath); 392196839Strasz return (0); 393196839Strasz } 394127796Sbmilekic } 395196839Strasz if (acl_supported == 0) 396196839Strasz return (0); 397196839Strasz 398196839Strasz facl = acl_get_file(entry->fts_accpath, acl_type); 399196839Strasz if (facl == NULL) { 400196839Strasz warn("%s", entry->fts_accpath); 401196839Strasz return (0); 402196839Strasz } 403196839Strasz ret = acl_is_trivial_np(facl, &trivial); 404196839Strasz acl_free(facl); 405196839Strasz if (ret) { 406196839Strasz warn("%s", entry->fts_accpath); 407196839Strasz return (0); 408196839Strasz } 409196839Strasz if (trivial) 410196839Strasz return (0); 411196839Strasz return (1); 412127796Sbmilekic} 413127796Sbmilekic 414127796SbmilekicPLAN * 415140810Sssouhlalc_acl(OPTION *option, char ***argvp __unused) 416127796Sbmilekic{ 417127796Sbmilekic ftsoptions &= ~FTS_NOSTAT; 418127796Sbmilekic return (palloc(option)); 419127796Sbmilekic} 420127796Sbmilekic 421127796Sbmilekic/* 42276250Sphk * -delete functions -- 4231590Srgrimes * 42476250Sphk * True always. Makes its best shot and continues on regardless. 4251590Srgrimes */ 4261590Srgrimesint 427116333Smarkmf_delete(PLAN *plan __unused, FTSENT *entry) 4281590Srgrimes{ 42976250Sphk /* ignore these from fts */ 43076250Sphk if (strcmp(entry->fts_accpath, ".") == 0 || 43176250Sphk strcmp(entry->fts_accpath, "..") == 0) 43276250Sphk return 1; 4331590Srgrimes 43476250Sphk /* sanity check */ 43576250Sphk if (isdepth == 0 || /* depth off */ 436192381Savg (ftsoptions & FTS_NOSTAT)) /* not stat()ing */ 43776250Sphk errx(1, "-delete: insecure options got turned on"); 43876250Sphk 439192381Savg if (!(ftsoptions & FTS_PHYSICAL) || /* physical off */ 440192381Savg (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ 441192381Savg errx(1, "-delete: forbidden when symlinks are followed"); 442192381Savg 44376250Sphk /* Potentially unsafe - do not accept relative paths whatsoever */ 444253886Sjilles if (entry->fts_level > FTS_ROOTLEVEL && 445253886Sjilles strchr(entry->fts_accpath, '/') != NULL) 44676250Sphk errx(1, "-delete: %s: relative path potentially not safe", 44776250Sphk entry->fts_accpath); 44876250Sphk 44976250Sphk /* Turn off user immutable bits if running as root */ 45076250Sphk if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 45176250Sphk !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 45276250Sphk geteuid() == 0) 453193087Sjilles lchflags(entry->fts_accpath, 45476250Sphk entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 45576250Sphk 45676250Sphk /* rmdir directories, unlink everything else */ 45776250Sphk if (S_ISDIR(entry->fts_statp->st_mode)) { 45876250Sphk if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) 45976250Sphk warn("-delete: rmdir(%s)", entry->fts_path); 46076250Sphk } else { 46176250Sphk if (unlink(entry->fts_accpath) < 0) 46276250Sphk warn("-delete: unlink(%s)", entry->fts_path); 46376250Sphk } 46476250Sphk 46576250Sphk /* "succeed" */ 46676250Sphk return 1; 4671590Srgrimes} 4688874Srgrimes 4691590SrgrimesPLAN * 470116333Smarkmc_delete(OPTION *option, char ***argvp __unused) 4711590Srgrimes{ 4721590Srgrimes 47376250Sphk ftsoptions &= ~FTS_NOSTAT; /* no optimise */ 47476250Sphk isoutput = 1; /* possible output */ 47576250Sphk isdepth = 1; /* -depth implied */ 4761590Srgrimes 477246628Sjilles /* 478246628Sjilles * Try to avoid the confusing error message about relative paths 479246628Sjilles * being potentially not safe. 480246628Sjilles */ 481246628Sjilles if (ftsoptions & FTS_NOCHDIR) 482246628Sjilles errx(1, "%s: forbidden when the current directory cannot be opened", 483246628Sjilles "-delete"); 484246628Sjilles 48576250Sphk return palloc(option); 4861590Srgrimes} 4871590Srgrimes 48830395Swosch 4891590Srgrimes/* 490129812Seik * always_true -- 4911590Srgrimes * 492176478Simp * Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true 4931590Srgrimes */ 4941590Srgrimesint 495116333Smarkmf_always_true(PLAN *plan __unused, FTSENT *entry __unused) 4961590Srgrimes{ 49776250Sphk return 1; 4981590Srgrimes} 4998874Srgrimes 500129812Seik/* 501129812Seik * -depth functions -- 502129812Seik * 503129812Seik * With argument: True if the file is at level n. 504129812Seik * Without argument: Always true, causes descent of the directory hierarchy 505129812Seik * to be done so that all entries in a directory are acted on before the 506129812Seik * directory itself. 507129812Seik */ 508129812Seikint 509129812Seikf_depth(PLAN *plan, FTSENT *entry) 510129812Seik{ 511129812Seik if (plan->flags & F_DEPTH) 512129812Seik COMPARE(entry->fts_level, plan->d_data); 513129812Seik else 514129812Seik return 1; 515129812Seik} 516129812Seik 5171590SrgrimesPLAN * 518129812Seikc_depth(OPTION *option, char ***argvp) 5191590Srgrimes{ 520129812Seik PLAN *new; 521129812Seik char *str; 5221590Srgrimes 523129812Seik new = palloc(option); 524129812Seik 525129812Seik str = **argvp; 526129812Seik if (str && !(new->flags & F_DEPTH)) { 527129812Seik /* skip leading + or - */ 528129812Seik if (*str == '+' || *str == '-') 529129812Seik str++; 530129812Seik /* skip sign */ 531129812Seik if (*str == '+' || *str == '-') 532129812Seik str++; 533129812Seik if (isdigit(*str)) 534129812Seik new->flags |= F_DEPTH; 535129812Seik } 536129812Seik 537129812Seik if (new->flags & F_DEPTH) { /* -depth n */ 538129812Seik char *ndepth; 539129812Seik 540129812Seik ndepth = nextarg(option, argvp); 541129812Seik new->d_data = find_parsenum(new, option->name, ndepth, NULL); 542129812Seik } else { /* -d */ 543129812Seik isdepth = 1; 544129812Seik } 545129812Seik 546129812Seik return new; 5471590Srgrimes} 54828914Simp 54928914Simp/* 55071422Speter * -empty functions -- 55171422Speter * 55271422Speter * True if the file or directory is empty 55371422Speter */ 55471422Speterint 555116333Smarkmf_empty(PLAN *plan __unused, FTSENT *entry) 55671422Speter{ 55776250Sphk if (S_ISREG(entry->fts_statp->st_mode) && 55876250Sphk entry->fts_statp->st_size == 0) 55976250Sphk return 1; 56071422Speter if (S_ISDIR(entry->fts_statp->st_mode)) { 56171422Speter struct dirent *dp; 56271422Speter int empty; 56371422Speter DIR *dir; 56471422Speter 56571422Speter empty = 1; 56671422Speter dir = opendir(entry->fts_accpath); 56771422Speter if (dir == NULL) 568216106Skevlo return 0; 56971422Speter for (dp = readdir(dir); dp; dp = readdir(dir)) 57071422Speter if (dp->d_name[0] != '.' || 57171422Speter (dp->d_name[1] != '\0' && 57271422Speter (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { 57371422Speter empty = 0; 57471422Speter break; 57571422Speter } 57671422Speter closedir(dir); 57776250Sphk return empty; 57871422Speter } 57976250Sphk return 0; 58071422Speter} 58171422Speter 58271422SpeterPLAN * 583116333Smarkmc_empty(OPTION *option, char ***argvp __unused) 58471422Speter{ 58571422Speter ftsoptions &= ~FTS_NOSTAT; 58671422Speter 58776250Sphk return palloc(option); 58871422Speter} 58971422Speter 59071422Speter/* 59176250Sphk * [-exec | -execdir | -ok] utility [arg ... ] ; functions -- 59228914Simp * 59328914Simp * True if the executed utility returns a zero value as exit status. 59428914Simp * The end of the primary expression is delimited by a semicolon. If 59576250Sphk * "{}" occurs anywhere, it gets replaced by the current pathname, 59676250Sphk * or, in the case of -execdir, the current basename (filename 59776250Sphk * without leading directory prefix). For -exec and -ok, 59876250Sphk * the current directory for the execution of utility is the same as 59976250Sphk * the current directory when the find utility was started, whereas 60076250Sphk * for -execdir, it is the directory the file resides in. 60176250Sphk * 60276250Sphk * The primary -ok differs from -exec in that it requests affirmation 60376250Sphk * of the user before executing the utility. 60428914Simp */ 60528914Simpint 606116333Smarkmf_exec(PLAN *plan, FTSENT *entry) 60728914Simp{ 60891400Sdwmalone int cnt; 60928914Simp pid_t pid; 61028914Simp int status; 61128914Simp char *file; 6128874Srgrimes 61397736Stjr if (entry == NULL && plan->flags & F_EXECPLUS) { 61497736Stjr if (plan->e_ppos == plan->e_pbnum) 61597736Stjr return (1); 61697736Stjr plan->e_argv[plan->e_ppos] = NULL; 61797736Stjr goto doexec; 61897736Stjr } 61997736Stjr 62028914Simp /* XXX - if file/dir ends in '/' this will not work -- can it? */ 62176250Sphk if ((plan->flags & F_EXECDIR) && \ 62276250Sphk (file = strrchr(entry->fts_path, '/'))) 62376250Sphk file++; 62428914Simp else 62576250Sphk file = entry->fts_path; 62628914Simp 62797736Stjr if (plan->flags & F_EXECPLUS) { 62897736Stjr if ((plan->e_argv[plan->e_ppos] = strdup(file)) == NULL) 62997736Stjr err(1, NULL); 63097736Stjr plan->e_len[plan->e_ppos] = strlen(file); 63197736Stjr plan->e_psize += plan->e_len[plan->e_ppos]; 63297736Stjr if (++plan->e_ppos < plan->e_pnummax && 63397736Stjr plan->e_psize < plan->e_psizemax) 63497736Stjr return (1); 63597736Stjr plan->e_argv[plan->e_ppos] = NULL; 63697736Stjr } else { 63797736Stjr for (cnt = 0; plan->e_argv[cnt]; ++cnt) 63897736Stjr if (plan->e_len[cnt]) 63997736Stjr brace_subst(plan->e_orig[cnt], 64097736Stjr &plan->e_argv[cnt], file, 64197736Stjr plan->e_len[cnt]); 64297736Stjr } 64328914Simp 64497736Stjrdoexec: if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv)) 64576250Sphk return 0; 64676250Sphk 64776250Sphk /* make sure find output is interspersed correctly with subprocesses */ 64828914Simp fflush(stdout); 64928914Simp fflush(stderr); 65028914Simp 65140301Sdes switch (pid = fork()) { 65228914Simp case -1: 65328914Simp err(1, "fork"); 65428914Simp /* NOTREACHED */ 65528914Simp case 0: 65676250Sphk /* change dir back from where we started */ 657246628Sjilles if (!(plan->flags & F_EXECDIR) && 658246628Sjilles !(ftsoptions & FTS_NOCHDIR) && fchdir(dotfd)) { 65976250Sphk warn("chdir"); 66076250Sphk _exit(1); 66176250Sphk } 66228914Simp execvp(plan->e_argv[0], plan->e_argv); 66328914Simp warn("%s", plan->e_argv[0]); 66428914Simp _exit(1); 66528914Simp } 66697736Stjr if (plan->flags & F_EXECPLUS) { 66797736Stjr while (--plan->e_ppos >= plan->e_pbnum) 66897736Stjr free(plan->e_argv[plan->e_ppos]); 66997736Stjr plan->e_ppos = plan->e_pbnum; 67097736Stjr plan->e_psize = plan->e_pbsize; 67197736Stjr } 67228914Simp pid = waitpid(pid, &status, 0); 67328914Simp return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 67428914Simp} 67576250Sphk 6761590Srgrimes/* 67776250Sphk * c_exec, c_execdir, c_ok -- 67828914Simp * build three parallel arrays, one with pointers to the strings passed 67928914Simp * on the command line, one with (possibly duplicated) pointers to the 68028914Simp * argv array, and one with integer values that are lengths of the 68128914Simp * strings, but also flags meaning that the string has to be massaged. 68228914Simp */ 68328914SimpPLAN * 684116333Smarkmc_exec(OPTION *option, char ***argvp) 68528914Simp{ 68628914Simp PLAN *new; /* node returned */ 68797736Stjr long argmax; 68897736Stjr int cnt, i; 68999905Stjr char **argv, **ap, **ep, *p; 69028914Simp 691246628Sjilles /* This would defeat -execdir's intended security. */ 692246628Sjilles if (option->flags & F_EXECDIR && ftsoptions & FTS_NOCHDIR) 693246628Sjilles errx(1, "%s: forbidden when the current directory cannot be opened", 694246628Sjilles "-execdir"); 695246628Sjilles 69676250Sphk /* XXX - was in c_execdir, but seems unnecessary!? 69728914Simp ftsoptions &= ~FTS_NOSTAT; 69876250Sphk */ 69928914Simp isoutput = 1; 70028914Simp 70176250Sphk /* XXX - this is a change from the previous coding */ 70276250Sphk new = palloc(option); 70376250Sphk 70428914Simp for (ap = argv = *argvp;; ++ap) { 70528914Simp if (!*ap) 70628914Simp errx(1, 707132815Stjr "%s: no terminating \";\" or \"+\"", option->name); 70828914Simp if (**ap == ';') 70928914Simp break; 71097736Stjr if (**ap == '+' && ap != argv && strcmp(*(ap - 1), "{}") == 0) { 71197736Stjr new->flags |= F_EXECPLUS; 71297736Stjr break; 71397736Stjr } 71428914Simp } 71528914Simp 71693628Sjmallett if (ap == argv) 71793628Sjmallett errx(1, "%s: no command specified", option->name); 71893628Sjmallett 71928914Simp cnt = ap - *argvp + 1; 72097736Stjr if (new->flags & F_EXECPLUS) { 72197736Stjr new->e_ppos = new->e_pbnum = cnt - 2; 72297736Stjr if ((argmax = sysconf(_SC_ARG_MAX)) == -1) { 72397736Stjr warn("sysconf(_SC_ARG_MAX)"); 72497736Stjr argmax = _POSIX_ARG_MAX; 72597736Stjr } 72699905Stjr argmax -= 1024; 72799905Stjr for (ep = environ; *ep != NULL; ep++) 72899905Stjr argmax -= strlen(*ep) + 1 + sizeof(*ep); 72999905Stjr argmax -= 1 + sizeof(*ep); 730246618Sjilles /* 731246618Sjilles * Ensure that -execdir ... {} + does not mix files 732246618Sjilles * from different directories in one invocation. 733246618Sjilles * Files from the same directory should be handled 734246618Sjilles * in one invocation but there is no code for it. 735246618Sjilles */ 736246618Sjilles new->e_pnummax = new->flags & F_EXECDIR ? 1 : argmax / 16; 73799905Stjr argmax -= sizeof(char *) * new->e_pnummax; 73899905Stjr if (argmax <= 0) 73999905Stjr errx(1, "no space for arguments"); 74099905Stjr new->e_psizemax = argmax; 74197736Stjr new->e_pbsize = 0; 74297736Stjr cnt += new->e_pnummax + 1; 743158572Skrion new->e_next = lastexecplus; 744158572Skrion lastexecplus = new; 74597736Stjr } 74696785Sjmallett if ((new->e_argv = malloc(cnt * sizeof(char *))) == NULL) 74796785Sjmallett err(1, NULL); 74896785Sjmallett if ((new->e_orig = malloc(cnt * sizeof(char *))) == NULL) 74996785Sjmallett err(1, NULL); 75096785Sjmallett if ((new->e_len = malloc(cnt * sizeof(int))) == NULL) 75196785Sjmallett err(1, NULL); 75228914Simp 75328914Simp for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 75428914Simp new->e_orig[cnt] = *argv; 75597736Stjr if (new->flags & F_EXECPLUS) 75697736Stjr new->e_pbsize += strlen(*argv) + 1; 75728914Simp for (p = *argv; *p; ++p) 75897736Stjr if (!(new->flags & F_EXECPLUS) && p[0] == '{' && 75997736Stjr p[1] == '}') { 76080291Sobrien if ((new->e_argv[cnt] = 76196785Sjmallett malloc(MAXPATHLEN)) == NULL) 76296785Sjmallett err(1, NULL); 76328914Simp new->e_len[cnt] = MAXPATHLEN; 76428914Simp break; 76528914Simp } 76628914Simp if (!*p) { 76728914Simp new->e_argv[cnt] = *argv; 76828914Simp new->e_len[cnt] = 0; 76928914Simp } 77028914Simp } 77197736Stjr if (new->flags & F_EXECPLUS) { 77297736Stjr new->e_psize = new->e_pbsize; 77397736Stjr cnt--; 77497736Stjr for (i = 0; i < new->e_pnummax; i++) { 77597736Stjr new->e_argv[cnt] = NULL; 77697736Stjr new->e_len[cnt] = 0; 77797736Stjr cnt++; 77897736Stjr } 77997736Stjr argv = ap; 78097736Stjr goto done; 78197736Stjr } 78228914Simp new->e_argv[cnt] = new->e_orig[cnt] = NULL; 78328914Simp 78497736Stjrdone: *argvp = argv + 1; 78576250Sphk return new; 78628914Simp} 78728914Simp 788158572Skrion/* Finish any pending -exec ... {} + functions. */ 789158572Skrionvoid 790201227Sedfinish_execplus(void) 791158572Skrion{ 792158572Skrion PLAN *p; 793158572Skrion 794158572Skrion p = lastexecplus; 795158572Skrion while (p != NULL) { 796158572Skrion (p->execute)(p, NULL); 797158572Skrion p = p->e_next; 798158572Skrion } 799158572Skrion} 800158572Skrion 80176250Sphkint 802116333Smarkmf_flags(PLAN *plan, FTSENT *entry) 80376250Sphk{ 80476250Sphk u_long flags; 80576250Sphk 80682972Sru flags = entry->fts_statp->st_flags; 80776250Sphk if (plan->flags & F_ATLEAST) 80882972Sru return (flags | plan->fl_flags) == flags && 80982972Sru !(flags & plan->fl_notflags); 81082972Sru else if (plan->flags & F_ANY) 81182972Sru return (flags & plan->fl_flags) || 81282972Sru (flags | plan->fl_notflags) != flags; 81376250Sphk else 81482972Sru return flags == plan->fl_flags && 81582972Sru !(plan->fl_flags & plan->fl_notflags); 81676250Sphk} 81776250Sphk 81876250SphkPLAN * 819116333Smarkmc_flags(OPTION *option, char ***argvp) 82076250Sphk{ 82176250Sphk char *flags_str; 82276250Sphk PLAN *new; 82376250Sphk u_long flags, notflags; 82476250Sphk 82576250Sphk flags_str = nextarg(option, argvp); 82676250Sphk ftsoptions &= ~FTS_NOSTAT; 82776250Sphk 82876250Sphk new = palloc(option); 82976250Sphk 83076250Sphk if (*flags_str == '-') { 83176250Sphk new->flags |= F_ATLEAST; 83276250Sphk flags_str++; 83382972Sru } else if (*flags_str == '+') { 83482972Sru new->flags |= F_ANY; 83582972Sru flags_str++; 83676250Sphk } 83776250Sphk if (strtofflags(&flags_str, &flags, ¬flags) == 1) 83876250Sphk errx(1, "%s: %s: illegal flags string", option->name, flags_str); 83976250Sphk 84076250Sphk new->fl_flags = flags; 84182972Sru new->fl_notflags = notflags; 84276250Sphk return new; 84376250Sphk} 84476250Sphk 84528914Simp/* 8461590Srgrimes * -follow functions -- 8471590Srgrimes * 8481590Srgrimes * Always true, causes symbolic links to be followed on a global 8491590Srgrimes * basis. 8501590Srgrimes */ 8511590SrgrimesPLAN * 852116333Smarkmc_follow(OPTION *option, char ***argvp __unused) 8531590Srgrimes{ 8541590Srgrimes ftsoptions &= ~FTS_PHYSICAL; 8551590Srgrimes ftsoptions |= FTS_LOGICAL; 8561590Srgrimes 85776250Sphk return palloc(option); 8581590Srgrimes} 8598874Srgrimes 8601590Srgrimes/* 8611590Srgrimes * -fstype functions -- 8621590Srgrimes * 8631590Srgrimes * True if the file is of a certain type. 8641590Srgrimes */ 8651590Srgrimesint 866116333Smarkmf_fstype(PLAN *plan, FTSENT *entry) 8671590Srgrimes{ 8681590Srgrimes static dev_t curdev; /* need a guaranteed illegal dev value */ 8691590Srgrimes static int first = 1; 8701590Srgrimes struct statfs sb; 871223035Savatar static int val_flags; 872223035Savatar static char fstype[sizeof(sb.f_fstypename)]; 873149453Sroberto char *p, save[2] = {0,0}; 8741590Srgrimes 87576250Sphk if ((plan->flags & F_MTMASK) == F_MTUNKNOWN) 87676250Sphk return 0; 87776250Sphk 8781590Srgrimes /* Only check when we cross mount point. */ 8791590Srgrimes if (first || curdev != entry->fts_statp->st_dev) { 8801590Srgrimes curdev = entry->fts_statp->st_dev; 8811590Srgrimes 8821590Srgrimes /* 88396704Strhodes * Statfs follows symlinks; find wants the link's filesystem, 8841590Srgrimes * not where it points. 8851590Srgrimes */ 8861590Srgrimes if (entry->fts_info == FTS_SL || 8871590Srgrimes entry->fts_info == FTS_SLNONE) { 8881590Srgrimes if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 8891590Srgrimes ++p; 8901590Srgrimes else 8911590Srgrimes p = entry->fts_accpath; 8921590Srgrimes save[0] = p[0]; 8931590Srgrimes p[0] = '.'; 8941590Srgrimes save[1] = p[1]; 8951590Srgrimes p[1] = '\0'; 8968874Srgrimes } else 8971590Srgrimes p = NULL; 8981590Srgrimes 8991590Srgrimes if (statfs(entry->fts_accpath, &sb)) 9001590Srgrimes err(1, "%s", entry->fts_accpath); 9011590Srgrimes 9021590Srgrimes if (p) { 9031590Srgrimes p[0] = save[0]; 9041590Srgrimes p[1] = save[1]; 9051590Srgrimes } 9061590Srgrimes 9071590Srgrimes first = 0; 90823695Speter 90923695Speter /* 91023695Speter * Further tests may need both of these values, so 91123695Speter * always copy both of them. 91223695Speter */ 91324313Speter val_flags = sb.f_flags; 914223035Savatar strlcpy(fstype, sb.f_fstypename, sizeof(fstype)); 9151590Srgrimes } 91676250Sphk switch (plan->flags & F_MTMASK) { 9171590Srgrimes case F_MTFLAG: 91876250Sphk return val_flags & plan->mt_data; 9191590Srgrimes case F_MTTYPE: 920223035Savatar return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0); 9211590Srgrimes default: 9221590Srgrimes abort(); 9231590Srgrimes } 9241590Srgrimes} 9258874Srgrimes 9261590SrgrimesPLAN * 927116333Smarkmc_fstype(OPTION *option, char ***argvp) 9281590Srgrimes{ 92976250Sphk char *fsname; 93091400Sdwmalone PLAN *new; 93176250Sphk 93276250Sphk fsname = nextarg(option, argvp); 9331590Srgrimes ftsoptions &= ~FTS_NOSTAT; 9348874Srgrimes 93576250Sphk new = palloc(option); 93676250Sphk switch (*fsname) { 9371590Srgrimes case 'l': 93876250Sphk if (!strcmp(fsname, "local")) { 93976250Sphk new->flags |= F_MTFLAG; 9401590Srgrimes new->mt_data = MNT_LOCAL; 94176250Sphk return new; 9421590Srgrimes } 9431590Srgrimes break; 9441590Srgrimes case 'r': 94576250Sphk if (!strcmp(fsname, "rdonly")) { 94676250Sphk new->flags |= F_MTFLAG; 9471590Srgrimes new->mt_data = MNT_RDONLY; 94876250Sphk return new; 9491590Srgrimes } 9501590Srgrimes break; 9511590Srgrimes } 95276250Sphk 953223035Savatar new->flags |= F_MTTYPE; 954223035Savatar new->c_data = fsname; 95576250Sphk return new; 9561590Srgrimes} 9578874Srgrimes 9581590Srgrimes/* 9591590Srgrimes * -group gname functions -- 9601590Srgrimes * 9611590Srgrimes * True if the file belongs to the group gname. If gname is numeric and 9621590Srgrimes * an equivalent of the getgrnam() function does not return a valid group 9631590Srgrimes * name, gname is taken as a group ID. 9641590Srgrimes */ 9651590Srgrimesint 966116333Smarkmf_group(PLAN *plan, FTSENT *entry) 9671590Srgrimes{ 968158919Skrion COMPARE(entry->fts_statp->st_gid, plan->g_data); 9691590Srgrimes} 9708874Srgrimes 9711590SrgrimesPLAN * 972116333Smarkmc_group(OPTION *option, char ***argvp) 97376250Sphk{ 9741590Srgrimes char *gname; 9751590Srgrimes PLAN *new; 9761590Srgrimes struct group *g; 9771590Srgrimes gid_t gid; 9788874Srgrimes 97976250Sphk gname = nextarg(option, argvp); 9801590Srgrimes ftsoptions &= ~FTS_NOSTAT; 9811590Srgrimes 982158919Skrion new = palloc(option); 9831590Srgrimes g = getgrnam(gname); 9841590Srgrimes if (g == NULL) { 985158919Skrion char* cp = gname; 986176478Simp if (gname[0] == '-' || gname[0] == '+') 987158919Skrion gname++; 9881590Srgrimes gid = atoi(gname); 9891590Srgrimes if (gid == 0 && gname[0] != '0') 99076250Sphk errx(1, "%s: %s: no such group", option->name, gname); 991158919Skrion gid = find_parsenum(new, option->name, cp, NULL); 9921590Srgrimes } else 9931590Srgrimes gid = g->gr_gid; 9948874Srgrimes 9951590Srgrimes new->g_data = gid; 99676250Sphk return new; 9971590Srgrimes} 9981590Srgrimes 9991590Srgrimes/* 1000238780Sjilles * -ignore_readdir_race functions -- 1001238780Sjilles * 1002238780Sjilles * Always true. Ignore errors which occur if a file or a directory 1003238780Sjilles * in a starting point gets deleted between reading the name and calling 1004238780Sjilles * stat on it while find is traversing the starting point. 1005238780Sjilles */ 1006238780Sjilles 1007238780SjillesPLAN * 1008238780Sjillesc_ignore_readdir_race(OPTION *option, char ***argvp __unused) 1009238780Sjilles{ 1010238780Sjilles if (strcmp(option->name, "-ignore_readdir_race") == 0) 1011238780Sjilles ignore_readdir_race = 1; 1012238780Sjilles else 1013238780Sjilles ignore_readdir_race = 0; 1014238780Sjilles 1015238780Sjilles return palloc(option); 1016238780Sjilles} 1017238780Sjilles 1018238780Sjilles/* 10191590Srgrimes * -inum n functions -- 10201590Srgrimes * 10211590Srgrimes * True if the file has inode # n. 10221590Srgrimes */ 10231590Srgrimesint 1024116333Smarkmf_inum(PLAN *plan, FTSENT *entry) 10251590Srgrimes{ 10261590Srgrimes COMPARE(entry->fts_statp->st_ino, plan->i_data); 10271590Srgrimes} 10288874Srgrimes 10291590SrgrimesPLAN * 1030116333Smarkmc_inum(OPTION *option, char ***argvp) 10311590Srgrimes{ 103276250Sphk char *inum_str; 10331590Srgrimes PLAN *new; 10348874Srgrimes 103576250Sphk inum_str = nextarg(option, argvp); 10361590Srgrimes ftsoptions &= ~FTS_NOSTAT; 10378874Srgrimes 103876250Sphk new = palloc(option); 103976250Sphk new->i_data = find_parsenum(new, option->name, inum_str, NULL); 104076250Sphk return new; 10411590Srgrimes} 10428874Srgrimes 10431590Srgrimes/* 1044176478Simp * -samefile FN 1045176478Simp * 1046176478Simp * True if the file has the same inode (eg hard link) FN 1047176478Simp */ 1048176478Simp 1049176478Simp/* f_samefile is just f_inum */ 1050176478SimpPLAN * 1051176478Simpc_samefile(OPTION *option, char ***argvp) 1052176478Simp{ 1053176478Simp char *fn; 1054176478Simp PLAN *new; 1055176478Simp struct stat sb; 1056176478Simp 1057176478Simp fn = nextarg(option, argvp); 1058176478Simp ftsoptions &= ~FTS_NOSTAT; 1059176478Simp 1060176478Simp new = palloc(option); 1061176478Simp if (stat(fn, &sb)) 1062176478Simp err(1, "%s", fn); 1063176478Simp new->i_data = sb.st_ino; 1064176478Simp return new; 1065176478Simp} 1066176478Simp 1067176478Simp/* 10681590Srgrimes * -links n functions -- 10691590Srgrimes * 10701590Srgrimes * True if the file has n links. 10711590Srgrimes */ 10721590Srgrimesint 1073116333Smarkmf_links(PLAN *plan, FTSENT *entry) 10741590Srgrimes{ 10751590Srgrimes COMPARE(entry->fts_statp->st_nlink, plan->l_data); 10761590Srgrimes} 10778874Srgrimes 10781590SrgrimesPLAN * 1079116333Smarkmc_links(OPTION *option, char ***argvp) 10801590Srgrimes{ 108176250Sphk char *nlinks; 10821590Srgrimes PLAN *new; 10838874Srgrimes 108476250Sphk nlinks = nextarg(option, argvp); 10851590Srgrimes ftsoptions &= ~FTS_NOSTAT; 10868874Srgrimes 108776250Sphk new = palloc(option); 108876250Sphk new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL); 108976250Sphk return new; 10901590Srgrimes} 10918874Srgrimes 10921590Srgrimes/* 10931590Srgrimes * -ls functions -- 10941590Srgrimes * 10951590Srgrimes * Always true - prints the current entry to stdout in "ls" format. 10961590Srgrimes */ 10971590Srgrimesint 1098116333Smarkmf_ls(PLAN *plan __unused, FTSENT *entry) 10991590Srgrimes{ 11001590Srgrimes printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 110176250Sphk return 1; 11021590Srgrimes} 11038874Srgrimes 11041590SrgrimesPLAN * 1105116333Smarkmc_ls(OPTION *option, char ***argvp __unused) 11061590Srgrimes{ 11071590Srgrimes ftsoptions &= ~FTS_NOSTAT; 11081590Srgrimes isoutput = 1; 11098874Srgrimes 111076250Sphk return palloc(option); 11111590Srgrimes} 11121590Srgrimes 11131590Srgrimes/* 11141590Srgrimes * -name functions -- 11151590Srgrimes * 11161590Srgrimes * True if the basename of the filename being examined 11171590Srgrimes * matches pattern using Pattern Matching Notation S3.14 11181590Srgrimes */ 11191590Srgrimesint 1120116333Smarkmf_name(PLAN *plan, FTSENT *entry) 11211590Srgrimes{ 1122176497Simp char fn[PATH_MAX]; 1123176497Simp const char *name; 1124260579Sjilles ssize_t len; 1125176497Simp 1126176497Simp if (plan->flags & F_LINK) { 1127260579Sjilles /* 1128260579Sjilles * The below test both avoids obviously useless readlink() 1129260579Sjilles * calls and ensures that symlinks with existent target do 1130260579Sjilles * not match if symlinks are being followed. 1131260579Sjilles * Assumption: fts will stat all symlinks that are to be 1132260579Sjilles * followed and will return the stat information. 1133260579Sjilles */ 1134260579Sjilles if (entry->fts_info != FTS_NSOK && entry->fts_info != FTS_SL && 1135260579Sjilles entry->fts_info != FTS_SLNONE) 1136260579Sjilles return 0; 1137260579Sjilles len = readlink(entry->fts_accpath, fn, sizeof(fn) - 1); 1138260579Sjilles if (len == -1) 1139260579Sjilles return 0; 1140260579Sjilles fn[len] = '\0'; 1141176497Simp name = fn; 1142176497Simp } else 1143176497Simp name = entry->fts_name; 1144176497Simp return !fnmatch(plan->c_data, name, 114576250Sphk plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); 11461590Srgrimes} 11478874Srgrimes 11481590SrgrimesPLAN * 1149116333Smarkmc_name(OPTION *option, char ***argvp) 11501590Srgrimes{ 115172945Sknu char *pattern; 115272945Sknu PLAN *new; 115372945Sknu 115476250Sphk pattern = nextarg(option, argvp); 115576250Sphk new = palloc(option); 115672945Sknu new->c_data = pattern; 115776250Sphk return new; 115872945Sknu} 115972945Sknu 116072945Sknu/* 11611590Srgrimes * -newer file functions -- 11621590Srgrimes * 11631590Srgrimes * True if the current file has been modified more recently 11641590Srgrimes * then the modification time of the file named by the pathname 11651590Srgrimes * file. 11661590Srgrimes */ 11671590Srgrimesint 1168116333Smarkmf_newer(PLAN *plan, FTSENT *entry) 11691590Srgrimes{ 1170248446Sjilles struct timespec ft; 1171248446Sjilles 117276250Sphk if (plan->flags & F_TIME_C) 1173248446Sjilles ft = entry->fts_statp->st_ctim; 117476250Sphk else if (plan->flags & F_TIME_A) 1175248446Sjilles ft = entry->fts_statp->st_atim; 1176157440Sceri else if (plan->flags & F_TIME_B) 1177248446Sjilles ft = entry->fts_statp->st_birthtim; 117876250Sphk else 1179248446Sjilles ft = entry->fts_statp->st_mtim; 1180248446Sjilles return (ft.tv_sec > plan->t_data.tv_sec || 1181248446Sjilles (ft.tv_sec == plan->t_data.tv_sec && 1182248446Sjilles ft.tv_nsec > plan->t_data.tv_nsec)); 11831590Srgrimes} 11848874Srgrimes 11851590SrgrimesPLAN * 1186116333Smarkmc_newer(OPTION *option, char ***argvp) 11871590Srgrimes{ 118876250Sphk char *fn_or_tspec; 11891590Srgrimes PLAN *new; 11901590Srgrimes struct stat sb; 11918874Srgrimes 119276250Sphk fn_or_tspec = nextarg(option, argvp); 11931590Srgrimes ftsoptions &= ~FTS_NOSTAT; 11941590Srgrimes 119576250Sphk new = palloc(option); 119676250Sphk /* compare against what */ 119776250Sphk if (option->flags & F_TIME2_T) { 1198248446Sjilles new->t_data.tv_sec = get_date(fn_or_tspec); 1199248446Sjilles if (new->t_data.tv_sec == (time_t) -1) 120076250Sphk errx(1, "Can't parse date/time: %s", fn_or_tspec); 1201248446Sjilles /* Use the seconds only in the comparison. */ 1202248446Sjilles new->t_data.tv_nsec = 999999999; 120376250Sphk } else { 120476250Sphk if (stat(fn_or_tspec, &sb)) 120576250Sphk err(1, "%s", fn_or_tspec); 120676250Sphk if (option->flags & F_TIME2_C) 1207248446Sjilles new->t_data = sb.st_ctim; 120876250Sphk else if (option->flags & F_TIME2_A) 1209248446Sjilles new->t_data = sb.st_atim; 1210203865Sgavin else if (option->flags & F_TIME2_B) 1211248446Sjilles new->t_data = sb.st_birthtim; 121276250Sphk else 1213248446Sjilles new->t_data = sb.st_mtim; 121476250Sphk } 121576250Sphk return new; 12161590Srgrimes} 12178874Srgrimes 12181590Srgrimes/* 12191590Srgrimes * -nogroup functions -- 12201590Srgrimes * 12211590Srgrimes * True if file belongs to a user ID for which the equivalent 12221590Srgrimes * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 12231590Srgrimes */ 12241590Srgrimesint 1225116333Smarkmf_nogroup(PLAN *plan __unused, FTSENT *entry) 12261590Srgrimes{ 122776250Sphk return group_from_gid(entry->fts_statp->st_gid, 1) == NULL; 12281590Srgrimes} 12298874Srgrimes 12301590SrgrimesPLAN * 1231116333Smarkmc_nogroup(OPTION *option, char ***argvp __unused) 12321590Srgrimes{ 12331590Srgrimes ftsoptions &= ~FTS_NOSTAT; 12341590Srgrimes 123576250Sphk return palloc(option); 12361590Srgrimes} 12378874Srgrimes 12381590Srgrimes/* 12391590Srgrimes * -nouser functions -- 12401590Srgrimes * 12411590Srgrimes * True if file belongs to a user ID for which the equivalent 12421590Srgrimes * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 12431590Srgrimes */ 12441590Srgrimesint 1245116333Smarkmf_nouser(PLAN *plan __unused, FTSENT *entry) 12461590Srgrimes{ 124776250Sphk return user_from_uid(entry->fts_statp->st_uid, 1) == NULL; 12481590Srgrimes} 12498874Srgrimes 12501590SrgrimesPLAN * 1251116333Smarkmc_nouser(OPTION *option, char ***argvp __unused) 12521590Srgrimes{ 12531590Srgrimes ftsoptions &= ~FTS_NOSTAT; 12541590Srgrimes 125576250Sphk return palloc(option); 12561590Srgrimes} 12578874Srgrimes 12581590Srgrimes/* 12591590Srgrimes * -path functions -- 12601590Srgrimes * 12611590Srgrimes * True if the path of the filename being examined 12621590Srgrimes * matches pattern using Pattern Matching Notation S3.14 12631590Srgrimes */ 12641590Srgrimesint 1265116333Smarkmf_path(PLAN *plan, FTSENT *entry) 12661590Srgrimes{ 126776250Sphk return !fnmatch(plan->c_data, entry->fts_path, 126876250Sphk plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); 12691590Srgrimes} 12708874Srgrimes 127176250Sphk/* c_path is the same as c_name */ 12721590Srgrimes 12731590Srgrimes/* 12741590Srgrimes * -perm functions -- 12751590Srgrimes * 12761590Srgrimes * The mode argument is used to represent file mode bits. If it starts 12771590Srgrimes * with a leading digit, it's treated as an octal mode, otherwise as a 12781590Srgrimes * symbolic mode. 12791590Srgrimes */ 12801590Srgrimesint 1281116333Smarkmf_perm(PLAN *plan, FTSENT *entry) 12821590Srgrimes{ 12831590Srgrimes mode_t mode; 12841590Srgrimes 12851590Srgrimes mode = entry->fts_statp->st_mode & 12861590Srgrimes (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 128776250Sphk if (plan->flags & F_ATLEAST) 128876250Sphk return (plan->m_data | mode) == mode; 128982569Sru else if (plan->flags & F_ANY) 129082569Sru return (mode & plan->m_data); 12911590Srgrimes else 129276250Sphk return mode == plan->m_data; 12931590Srgrimes /* NOTREACHED */ 12941590Srgrimes} 12958874Srgrimes 12961590SrgrimesPLAN * 1297116333Smarkmc_perm(OPTION *option, char ***argvp) 129876250Sphk{ 12991590Srgrimes char *perm; 13001590Srgrimes PLAN *new; 13011590Srgrimes mode_t *set; 13021590Srgrimes 130376250Sphk perm = nextarg(option, argvp); 13041590Srgrimes ftsoptions &= ~FTS_NOSTAT; 13051590Srgrimes 130676250Sphk new = palloc(option); 13071590Srgrimes 13081590Srgrimes if (*perm == '-') { 130976250Sphk new->flags |= F_ATLEAST; 13101590Srgrimes ++perm; 131161573Sroberto } else if (*perm == '+') { 131276250Sphk new->flags |= F_ANY; 131361573Sroberto ++perm; 13141590Srgrimes } 13151590Srgrimes 13161590Srgrimes if ((set = setmode(perm)) == NULL) 131776250Sphk errx(1, "%s: %s: illegal mode string", option->name, perm); 13181590Srgrimes 13191590Srgrimes new->m_data = getmode(set, 0); 132041846Simp free(set); 132154828Sroberto return new; 132254828Sroberto} 132354828Sroberto 132454828Sroberto/* 13251590Srgrimes * -print functions -- 13261590Srgrimes * 132793212Scharnier * Always true, causes the current pathname to be written to 13281590Srgrimes * standard output. 13291590Srgrimes */ 13301590Srgrimesint 1331116333Smarkmf_print(PLAN *plan __unused, FTSENT *entry) 13321590Srgrimes{ 133311538Swollman (void)puts(entry->fts_path); 133476250Sphk return 1; 13351590Srgrimes} 13368874Srgrimes 13371590SrgrimesPLAN * 1338116333Smarkmc_print(OPTION *option, char ***argvp __unused) 13391590Srgrimes{ 13401590Srgrimes isoutput = 1; 13411590Srgrimes 134276250Sphk return palloc(option); 13431590Srgrimes} 13448874Srgrimes 13451590Srgrimes/* 13468389Swollman * -print0 functions -- 13478389Swollman * 134893212Scharnier * Always true, causes the current pathname to be written to 13498389Swollman * standard output followed by a NUL character 13508389Swollman */ 13518389Swollmanint 1352116333Smarkmf_print0(PLAN *plan __unused, FTSENT *entry) 13538389Swollman{ 13548389Swollman fputs(entry->fts_path, stdout); 13558389Swollman fputc('\0', stdout); 135676250Sphk return 1; 13578389Swollman} 13588874Srgrimes 135976250Sphk/* c_print0 is the same as c_print */ 13608389Swollman 13618389Swollman/* 13621590Srgrimes * -prune functions -- 13631590Srgrimes * 13641590Srgrimes * Prune a portion of the hierarchy. 13651590Srgrimes */ 13661590Srgrimesint 1367116333Smarkmf_prune(PLAN *plan __unused, FTSENT *entry) 13681590Srgrimes{ 13691590Srgrimes if (fts_set(tree, entry, FTS_SKIP)) 13701590Srgrimes err(1, "%s", entry->fts_path); 137176250Sphk return 1; 13721590Srgrimes} 13738874Srgrimes 137476250Sphk/* c_prune == c_simple */ 137576250Sphk 137676250Sphk/* 137776250Sphk * -regex functions -- 137876250Sphk * 137976250Sphk * True if the whole path of the file matches pattern using 138076250Sphk * regular expression. 138176250Sphk */ 138276250Sphkint 1383116333Smarkmf_regex(PLAN *plan, FTSENT *entry) 138476250Sphk{ 138576250Sphk char *str; 138696785Sjmallett int len; 138776250Sphk regex_t *pre; 138876250Sphk regmatch_t pmatch; 138976250Sphk int errcode; 139076250Sphk char errbuf[LINE_MAX]; 139176250Sphk int matched; 139276250Sphk 139376250Sphk pre = plan->re_data; 139476250Sphk str = entry->fts_path; 139576250Sphk len = strlen(str); 139676250Sphk matched = 0; 139776250Sphk 139876250Sphk pmatch.rm_so = 0; 139976250Sphk pmatch.rm_eo = len; 140076250Sphk 140176250Sphk errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); 140276250Sphk 140376250Sphk if (errcode != 0 && errcode != REG_NOMATCH) { 140476250Sphk regerror(errcode, pre, errbuf, sizeof errbuf); 140576250Sphk errx(1, "%s: %s", 140676250Sphk plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf); 140776250Sphk } 140876250Sphk 140976250Sphk if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) 141076250Sphk matched = 1; 141176250Sphk 141276250Sphk return matched; 141376250Sphk} 141476250Sphk 14151590SrgrimesPLAN * 1416116333Smarkmc_regex(OPTION *option, char ***argvp) 14171590Srgrimes{ 141876250Sphk PLAN *new; 141976250Sphk char *pattern; 142076250Sphk regex_t *pre; 142176250Sphk int errcode; 142276250Sphk char errbuf[LINE_MAX]; 142376250Sphk 142476250Sphk if ((pre = malloc(sizeof(regex_t))) == NULL) 142576250Sphk err(1, NULL); 142676250Sphk 142776250Sphk pattern = nextarg(option, argvp); 142876250Sphk 142976250Sphk if ((errcode = regcomp(pre, pattern, 143076250Sphk regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) { 143176250Sphk regerror(errcode, pre, errbuf, sizeof errbuf); 143276250Sphk errx(1, "%s: %s: %s", 143376250Sphk option->flags & F_IGNCASE ? "-iregex" : "-regex", 143476250Sphk pattern, errbuf); 143576250Sphk } 143676250Sphk 143776250Sphk new = palloc(option); 143876250Sphk new->re_data = pre; 143976250Sphk 144076250Sphk return new; 14411590Srgrimes} 14428874Srgrimes 1443176478Simp/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */ 144476250Sphk 144576250SphkPLAN * 1446116333Smarkmc_simple(OPTION *option, char ***argvp __unused) 144776250Sphk{ 144876250Sphk return palloc(option); 144976250Sphk} 145076250Sphk 14511590Srgrimes/* 14521590Srgrimes * -size n[c] functions -- 14531590Srgrimes * 14541590Srgrimes * True if the file size in bytes, divided by an implementation defined 14551590Srgrimes * value and rounded up to the next integer, is n. If n is followed by 1456158986Skrion * one of c k M G T P, the size is in bytes, kilobytes, 1457158986Skrion * megabytes, gigabytes, terabytes or petabytes respectively. 14581590Srgrimes */ 14591590Srgrimes#define FIND_SIZE 512 14601590Srgrimesstatic int divsize = 1; 14611590Srgrimes 14621590Srgrimesint 1463116333Smarkmf_size(PLAN *plan, FTSENT *entry) 14641590Srgrimes{ 14651590Srgrimes off_t size; 14661590Srgrimes 14671590Srgrimes size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 14681590Srgrimes FIND_SIZE : entry->fts_statp->st_size; 14691590Srgrimes COMPARE(size, plan->o_data); 14701590Srgrimes} 14718874Srgrimes 14721590SrgrimesPLAN * 1473116333Smarkmc_size(OPTION *option, char ***argvp) 14741590Srgrimes{ 147576250Sphk char *size_str; 14761590Srgrimes PLAN *new; 14771590Srgrimes char endch; 1478158986Skrion off_t scale; 14798874Srgrimes 148076250Sphk size_str = nextarg(option, argvp); 14811590Srgrimes ftsoptions &= ~FTS_NOSTAT; 14821590Srgrimes 148376250Sphk new = palloc(option); 14841590Srgrimes endch = 'c'; 148576250Sphk new->o_data = find_parsenum(new, option->name, size_str, &endch); 1486158986Skrion if (endch != '\0') { 14871590Srgrimes divsize = 0; 1488158986Skrion 1489158986Skrion switch (endch) { 1490158986Skrion case 'c': /* characters */ 1491158986Skrion scale = 0x1LL; 1492158986Skrion break; 1493158986Skrion case 'k': /* kilobytes 1<<10 */ 1494158986Skrion scale = 0x400LL; 1495158986Skrion break; 1496158986Skrion case 'M': /* megabytes 1<<20 */ 1497158986Skrion scale = 0x100000LL; 1498158986Skrion break; 1499158986Skrion case 'G': /* gigabytes 1<<30 */ 1500158986Skrion scale = 0x40000000LL; 1501158986Skrion break; 1502158986Skrion case 'T': /* terabytes 1<<40 */ 1503272922Semaste scale = 0x10000000000LL; 1504158986Skrion break; 1505158986Skrion case 'P': /* petabytes 1<<50 */ 1506158986Skrion scale = 0x4000000000000LL; 1507158986Skrion break; 1508158986Skrion default: 1509158986Skrion errx(1, "%s: %s: illegal trailing character", 1510158986Skrion option->name, size_str); 1511158986Skrion break; 1512158986Skrion } 1513158986Skrion if (new->o_data > QUAD_MAX / scale) 1514158986Skrion errx(1, "%s: %s: value too large", 1515158986Skrion option->name, size_str); 1516158986Skrion new->o_data *= scale; 1517158986Skrion } 151876250Sphk return new; 15191590Srgrimes} 15208874Srgrimes 15211590Srgrimes/* 1522247730Sdwmalone * -sparse functions -- 1523247730Sdwmalone * 1524247730Sdwmalone * Check if a file is sparse by finding if it occupies fewer blocks 1525247730Sdwmalone * than we expect based on its size. 1526247730Sdwmalone */ 1527247730Sdwmaloneint 1528247730Sdwmalonef_sparse(PLAN *plan __unused, FTSENT *entry) 1529247730Sdwmalone{ 1530247730Sdwmalone off_t expected_blocks; 1531247730Sdwmalone 1532247730Sdwmalone expected_blocks = (entry->fts_statp->st_size + 511) / 512; 1533247730Sdwmalone return entry->fts_statp->st_blocks < expected_blocks; 1534247730Sdwmalone} 1535247730Sdwmalone 1536247730SdwmalonePLAN * 1537247730Sdwmalonec_sparse(OPTION *option, char ***argvp __unused) 1538247730Sdwmalone{ 1539247730Sdwmalone ftsoptions &= ~FTS_NOSTAT; 1540247730Sdwmalone 1541247730Sdwmalone return palloc(option); 1542247730Sdwmalone} 1543247730Sdwmalone 1544247730Sdwmalone/* 15451590Srgrimes * -type c functions -- 15461590Srgrimes * 154723695Speter * True if the type of the file is c, where c is b, c, d, p, f or w 154823695Speter * for block special file, character special file, directory, FIFO, 154923695Speter * regular file or whiteout respectively. 15501590Srgrimes */ 15511590Srgrimesint 1552116333Smarkmf_type(PLAN *plan, FTSENT *entry) 15531590Srgrimes{ 155476250Sphk return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data; 15551590Srgrimes} 15568874Srgrimes 15571590SrgrimesPLAN * 1558116333Smarkmc_type(OPTION *option, char ***argvp) 155976250Sphk{ 15601590Srgrimes char *typestring; 15611590Srgrimes PLAN *new; 15621590Srgrimes mode_t mask; 15638874Srgrimes 156476250Sphk typestring = nextarg(option, argvp); 15651590Srgrimes ftsoptions &= ~FTS_NOSTAT; 15661590Srgrimes 15671590Srgrimes switch (typestring[0]) { 15681590Srgrimes case 'b': 15691590Srgrimes mask = S_IFBLK; 15701590Srgrimes break; 15711590Srgrimes case 'c': 15721590Srgrimes mask = S_IFCHR; 15731590Srgrimes break; 15741590Srgrimes case 'd': 15751590Srgrimes mask = S_IFDIR; 15761590Srgrimes break; 15771590Srgrimes case 'f': 15781590Srgrimes mask = S_IFREG; 15791590Srgrimes break; 15801590Srgrimes case 'l': 15811590Srgrimes mask = S_IFLNK; 15821590Srgrimes break; 15831590Srgrimes case 'p': 15841590Srgrimes mask = S_IFIFO; 15851590Srgrimes break; 15861590Srgrimes case 's': 15871590Srgrimes mask = S_IFSOCK; 15881590Srgrimes break; 158923695Speter#ifdef FTS_WHITEOUT 159023695Speter case 'w': 159123695Speter mask = S_IFWHT; 159223695Speter ftsoptions |= FTS_WHITEOUT; 159323695Speter break; 159423695Speter#endif /* FTS_WHITEOUT */ 15951590Srgrimes default: 159676250Sphk errx(1, "%s: %s: unknown type", option->name, typestring); 15971590Srgrimes } 15988874Srgrimes 159976250Sphk new = palloc(option); 16001590Srgrimes new->m_data = mask; 160176250Sphk return new; 16021590Srgrimes} 16038874Srgrimes 16041590Srgrimes/* 16051590Srgrimes * -user uname functions -- 16061590Srgrimes * 16071590Srgrimes * True if the file belongs to the user uname. If uname is numeric and 16081590Srgrimes * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 16091590Srgrimes * return a valid user name, uname is taken as a user ID. 16101590Srgrimes */ 16111590Srgrimesint 1612116333Smarkmf_user(PLAN *plan, FTSENT *entry) 16131590Srgrimes{ 1614158919Skrion COMPARE(entry->fts_statp->st_uid, plan->u_data); 16151590Srgrimes} 16168874Srgrimes 16171590SrgrimesPLAN * 1618116333Smarkmc_user(OPTION *option, char ***argvp) 161976250Sphk{ 16201590Srgrimes char *username; 16211590Srgrimes PLAN *new; 16221590Srgrimes struct passwd *p; 16231590Srgrimes uid_t uid; 16248874Srgrimes 162576250Sphk username = nextarg(option, argvp); 16261590Srgrimes ftsoptions &= ~FTS_NOSTAT; 16271590Srgrimes 1628158919Skrion new = palloc(option); 16291590Srgrimes p = getpwnam(username); 16301590Srgrimes if (p == NULL) { 1631158919Skrion char* cp = username; 1632158919Skrion if( username[0] == '-' || username[0] == '+' ) 1633158919Skrion username++; 16341590Srgrimes uid = atoi(username); 16351590Srgrimes if (uid == 0 && username[0] != '0') 163676250Sphk errx(1, "%s: %s: no such user", option->name, username); 1637158919Skrion uid = find_parsenum(new, option->name, cp, NULL); 16381590Srgrimes } else 16391590Srgrimes uid = p->pw_uid; 16401590Srgrimes 16411590Srgrimes new->u_data = uid; 164276250Sphk return new; 16431590Srgrimes} 16448874Srgrimes 16451590Srgrimes/* 16461590Srgrimes * -xdev functions -- 16471590Srgrimes * 164893212Scharnier * Always true, causes find not to descend past directories that have a 16491590Srgrimes * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 16501590Srgrimes */ 16511590SrgrimesPLAN * 1652116333Smarkmc_xdev(OPTION *option, char ***argvp __unused) 16531590Srgrimes{ 16541590Srgrimes ftsoptions |= FTS_XDEV; 16551590Srgrimes 165676250Sphk return palloc(option); 16571590Srgrimes} 16581590Srgrimes 16591590Srgrimes/* 16601590Srgrimes * ( expression ) functions -- 16611590Srgrimes * 16621590Srgrimes * True if expression is true. 16631590Srgrimes */ 16641590Srgrimesint 1665116333Smarkmf_expr(PLAN *plan, FTSENT *entry) 16661590Srgrimes{ 166791400Sdwmalone PLAN *p; 166891400Sdwmalone int state = 0; 16691590Srgrimes 16701590Srgrimes for (p = plan->p_data[0]; 167176250Sphk p && (state = (p->execute)(p, entry)); p = p->next); 167276250Sphk return state; 16731590Srgrimes} 16748874Srgrimes 16751590Srgrimes/* 167676250Sphk * f_openparen and f_closeparen nodes are temporary place markers. They are 16771590Srgrimes * eliminated during phase 2 of find_formplan() --- the '(' node is converted 167876250Sphk * to a f_expr node containing the expression and the ')' node is discarded. 167976250Sphk * The functions themselves are only used as constants. 16801590Srgrimes */ 168176250Sphk 168276250Sphkint 1683116333Smarkmf_openparen(PLAN *plan __unused, FTSENT *entry __unused) 16841590Srgrimes{ 168576250Sphk abort(); 16861590Srgrimes} 16878874Srgrimes 168876250Sphkint 1689116333Smarkmf_closeparen(PLAN *plan __unused, FTSENT *entry __unused) 169076250Sphk{ 169176250Sphk abort(); 169276250Sphk} 169376250Sphk 169476250Sphk/* c_openparen == c_simple */ 169576250Sphk/* c_closeparen == c_simple */ 169676250Sphk 169776250Sphk/* 169876250Sphk * AND operator. Since AND is implicit, no node is allocated. 169976250Sphk */ 17001590SrgrimesPLAN * 1701116333Smarkmc_and(OPTION *option __unused, char ***argvp __unused) 17021590Srgrimes{ 170376250Sphk return NULL; 17041590Srgrimes} 17058874Srgrimes 17061590Srgrimes/* 17071590Srgrimes * ! expression functions -- 17081590Srgrimes * 17091590Srgrimes * Negation of a primary; the unary NOT operator. 17101590Srgrimes */ 17111590Srgrimesint 1712116333Smarkmf_not(PLAN *plan, FTSENT *entry) 17131590Srgrimes{ 171491400Sdwmalone PLAN *p; 171591400Sdwmalone int state = 0; 17161590Srgrimes 17171590Srgrimes for (p = plan->p_data[0]; 171876250Sphk p && (state = (p->execute)(p, entry)); p = p->next); 171976250Sphk return !state; 17201590Srgrimes} 17218874Srgrimes 172276250Sphk/* c_not == c_simple */ 17238874Srgrimes 17241590Srgrimes/* 17251590Srgrimes * expression -o expression functions -- 17261590Srgrimes * 17271590Srgrimes * Alternation of primaries; the OR operator. The second expression is 17281590Srgrimes * not evaluated if the first expression is true. 17291590Srgrimes */ 17301590Srgrimesint 1731116333Smarkmf_or(PLAN *plan, FTSENT *entry) 17321590Srgrimes{ 173391400Sdwmalone PLAN *p; 173491400Sdwmalone int state = 0; 17351590Srgrimes 17361590Srgrimes for (p = plan->p_data[0]; 173776250Sphk p && (state = (p->execute)(p, entry)); p = p->next); 17381590Srgrimes 17391590Srgrimes if (state) 174076250Sphk return 1; 17411590Srgrimes 17421590Srgrimes for (p = plan->p_data[1]; 174376250Sphk p && (state = (p->execute)(p, entry)); p = p->next); 174476250Sphk return state; 17451590Srgrimes} 17461590Srgrimes 174776250Sphk/* c_or == c_simple */ 1748176478Simp 1749176478Simp/* 1750176478Simp * -false 1751176478Simp * 1752176478Simp * Always false. 1753176478Simp */ 1754176478Simpint 1755176478Simpf_false(PLAN *plan __unused, FTSENT *entry __unused) 1756176478Simp{ 1757176478Simp return 0; 1758176478Simp} 1759176478Simp 1760176478Simp/* c_false == c_simple */ 1761176478Simp 1762176478Simp/* 1763176478Simp * -quit 1764176478Simp * 1765176478Simp * Exits the program 1766176478Simp */ 1767176478Simpint 1768176478Simpf_quit(PLAN *plan __unused, FTSENT *entry __unused) 1769176478Simp{ 1770264169Sjilles finish_execplus(); 1771176478Simp exit(0); 1772176478Simp} 1773176478Simp 1774176478Simp/* c_quit == c_simple */ 1775