function.c revision 207677
174462Salfred/*- 274462Salfred * Copyright (c) 1990, 1993 374462Salfred * The Regents of the University of California. All rights reserved. 487096Salfred * 574462Salfred * This code is derived from software contributed to Berkeley by 674462Salfred * Cimarron D. Taylor of the University of California, Berkeley. 774462Salfred * 874462Salfred * Redistribution and use in source and binary forms, with or without 974462Salfred * modification, are permitted provided that the following conditions 1074462Salfred * are met: 1174462Salfred * 1. Redistributions of source code must retain the above copyright 1274462Salfred * notice, this list of conditions and the following disclaimer. 1374462Salfred * 2. Redistributions in binary form must reproduce the above copyright 1474462Salfred * notice, this list of conditions and the following disclaimer in the 1574462Salfred * documentation and/or other materials provided with the distribution. 1674462Salfred * 3. All advertising materials mentioning features or use of this software 1774462Salfred * must display the following acknowledgement: 1874462Salfred * This product includes software developed by the University of 1974462Salfred * California, Berkeley and its contributors. 2074462Salfred * 4. Neither the name of the University nor the names of its contributors 2174462Salfred * may be used to endorse or promote products derived from this software 2274462Salfred * without specific prior written permission. 2374462Salfred * 2474462Salfred * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2574462Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2674462Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2774462Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2874462Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2974462Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3074462Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3174462Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3274462Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3374462Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3474462Salfred * SUCH DAMAGE. 3574462Salfred * 3674462Salfred * @(#)function.c 8.10 (Berkeley) 5/4/95 37146445Scharnier */ 38146445Scharnier 39146445Scharnier#include <sys/cdefs.h> 4087096Salfred__FBSDID("$FreeBSD: head/usr.bin/find/function.c 207677 2010-05-05 21:24:18Z delphij $"); 4187096Salfred 4274462Salfred#include <sys/param.h> 4387096Salfred#include <sys/ucred.h> 4487096Salfred#include <sys/stat.h> 4587096Salfred#include <sys/types.h> 4674462Salfred#include <sys/acl.h> 4774462Salfred#include <sys/wait.h> 4874462Salfred#include <sys/mount.h> 4974462Salfred 5074462Salfred#include <dirent.h> 5174462Salfred#include <err.h> 5274462Salfred#include <errno.h> 5374462Salfred#include <fnmatch.h> 5474462Salfred#include <fts.h> 5574462Salfred#include <grp.h> 5674462Salfred#include <limits.h> 5774462Salfred#include <pwd.h> 5874462Salfred#include <regex.h> 5974462Salfred#include <stdio.h> 6074462Salfred#include <stdlib.h> 6174462Salfred#include <string.h> 6274462Salfred#include <unistd.h> 6374462Salfred#include <ctype.h> 6474462Salfred 6587096Salfred#include "find.h" 6687096Salfred 6787096Salfredstatic PLAN *palloc(OPTION *); 6884923Salfredstatic long long find_parsenum(PLAN *, const char *, char *, char *); 6984923Salfredstatic long long find_parsetime(PLAN *, const char *, char *); 7084923Salfredstatic char *nextarg(OPTION *, char ***); 7184923Salfred 72228990Suqsextern char **environ; 7384923Salfred 7474462Salfredstatic PLAN *lastexecplus = NULL; 7574462Salfred 7674462Salfred#define COMPARE(a, b) do { \ 7787096Salfred switch (plan->flags & F_ELG_MASK) { \ 7874462Salfred case F_EQUAL: \ 7974462Salfred return (a == b); \ 8074462Salfred case F_LESSTHAN: \ 81165776Smjacob return (a < b); \ 8274462Salfred case F_GREATER: \ 8374462Salfred return (a > b); \ 8474462Salfred default: \ 8574462Salfred abort(); \ 8687096Salfred } \ 87132254Smr} while(0) 88132254Smr 8974462Salfredstatic PLAN * 9074462Salfredpalloc(OPTION *option) 9187096Salfred{ 9287096Salfred PLAN *new; 9387096Salfred 9487096Salfred if ((new = malloc(sizeof(PLAN))) == NULL) 9587096Salfred err(1, NULL); 9687096Salfred new->execute = option->execute; 9774462Salfred new->flags = option->flags; 9874462Salfred new->next = NULL; 9984923Salfred return new; 10074462Salfred} 101228990Suqs 10274462Salfred/* 10374462Salfred * find_parsenum -- 10487096Salfred * Parse a string of the form [+-]# and return the value. 10574462Salfred */ 10674462Salfredstatic long long 10774462Salfredfind_parsenum(PLAN *plan, const char *option, char *vp, char *endch) 108132254Smr{ 109132254Smr long long value; 11074462Salfred char *endchar, *str; /* Pointer to character ending conversion. */ 11187096Salfred 11287096Salfred /* Determine comparison from leading + or -. */ 11387096Salfred str = vp; 11474462Salfred switch (*str) { 11587096Salfred case '+': 11687096Salfred ++str; 11787096Salfred plan->flags |= F_GREATER; 11887096Salfred break; 11987096Salfred case '-': 12087096Salfred ++str; 12174462Salfred plan->flags |= F_LESSTHAN; 12287096Salfred break; 12387096Salfred default: 12487096Salfred plan->flags |= F_EQUAL; 12587096Salfred break; 12687096Salfred } 12787096Salfred 12887096Salfred /* 12987096Salfred * Convert the string with strtoq(). Note, if strtoq() returns zero 13087096Salfred * and endchar points to the beginning of the string we know we have 13187096Salfred * a syntax error. 13287096Salfred */ 13387096Salfred value = strtoq(str, &endchar, 10); 13487096Salfred if (value == 0 && endchar == str) 13587096Salfred errx(1, "%s: %s: illegal numeric value", option, vp); 13687096Salfred if (endchar[0] && endch == NULL) 13787096Salfred errx(1, "%s: %s: illegal trailing character", option, vp); 13887096Salfred if (endch) 13987096Salfred *endch = endchar[0]; 14087096Salfred return value; 14187096Salfred} 142165776Smjacob 14387096Salfred/* 14487096Salfred * find_parsetime -- 14587096Salfred * Parse a string of the form [+-]([0-9]+[smhdw]?)+ and return the value. 146165776Smjacob */ 14787096Salfredstatic long long 14887096Salfredfind_parsetime(PLAN *plan, const char *option, char *vp) 14987096Salfred{ 15087096Salfred long long secs, value; 15187096Salfred char *str, *unit; /* Pointer to character ending conversion. */ 15287096Salfred 15387096Salfred /* Determine comparison from leading + or -. */ 15487096Salfred str = vp; 15587096Salfred switch (*str) { 15687096Salfred case '+': 15787096Salfred ++str; 15887096Salfred plan->flags |= F_GREATER; 15987096Salfred break; 160121558Speter case '-': 16187096Salfred ++str; 16287096Salfred plan->flags |= F_LESSTHAN; 16387096Salfred break; 16487199Salfred default: 165132254Smr plan->flags |= F_EQUAL; 166132254Smr break; 167132254Smr } 16887096Salfred 16987199Salfred value = strtoq(str, &unit, 10); 170132254Smr if (value == 0 && unit == str) { 171132254Smr errx(1, "%s: %s: illegal time value", option, vp); 17287199Salfred /* NOTREACHED */ 17387096Salfred } 174129302Sstefanf if (*unit == '\0') 17587096Salfred return value; 17687096Salfred 17787096Salfred /* Units syntax. */ 17887096Salfred secs = 0; 17987096Salfred for (;;) { 18087096Salfred switch(*unit) { 18187096Salfred case 's': /* seconds */ 18287096Salfred secs += value; 18387096Salfred break; 18487096Salfred case 'm': /* minutes */ 18587096Salfred secs += value * 60; 18687096Salfred break; 18787096Salfred case 'h': /* hours */ 18887096Salfred secs += value * 3600; 18987096Salfred break; 19087096Salfred case 'd': /* days */ 19187096Salfred secs += value * 86400; 19287096Salfred break; 19387096Salfred case 'w': /* weeks */ 19487096Salfred secs += value * 604800; 19587096Salfred break; 19687096Salfred default: 19787096Salfred errx(1, "%s: %s: bad unit '%c'", option, vp, *unit); 19887096Salfred /* NOTREACHED */ 19987096Salfred } 200216603Suqs str = unit + 1; 20187096Salfred if (*str == '\0') /* EOS */ 20287096Salfred break; 20387096Salfred value = strtoq(str, &unit, 10); 204165776Smjacob if (value == 0 && unit == str) { 20587096Salfred errx(1, "%s: %s: illegal time value", option, vp); 20687096Salfred /* NOTREACHED */ 20787096Salfred } 20887096Salfred if (*unit == '\0') { 20987096Salfred errx(1, "%s: %s: missing trailing unit", option, vp); 21087096Salfred /* NOTREACHED */ 21187096Salfred } 21287096Salfred } 21387096Salfred plan->flags |= F_EXACTTIME; 21487096Salfred return secs; 21587096Salfred} 21687096Salfred 21787096Salfred/* 218146445Scharnier * nextarg -- 21987096Salfred * Check that another argument still exists, return a pointer to it, 22087096Salfred * and increment the argument vector pointer. 22187096Salfred */ 22287096Salfredstatic char * 22387096Salfrednextarg(OPTION *option, char ***argvp) 22487096Salfred{ 22587096Salfred char *arg; 22687096Salfred 22787096Salfred if ((arg = **argvp) == 0) 22887096Salfred errx(1, "%s: requires additional arguments", option->name); 22987096Salfred (*argvp)++; 23087096Salfred return arg; 23187096Salfred} /* nextarg() */ 23287096Salfred 23387096Salfred/* 23487096Salfred * The value of n for the inode times (atime, birthtime, ctime, mtime) is a 23587096Salfred * range, i.e. n matches from (n - 1) to n 24 hour periods. This interacts 23687096Salfred * with -n, such that "-mtime -1" would be less than 0 days, which isn't what 23787096Salfred * the user wanted. Correct so that -1 is "less than 1". 23887096Salfred */ 23987096Salfred#define TIME_CORRECT(p) \ 24087096Salfred if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \ 24187096Salfred ++((p)->t_data); 24287096Salfred 24387096Salfred/* 244165776Smjacob * -[acm]min n functions -- 24587096Salfred * 24687096Salfred * True if the difference between the 24787096Salfred * file access time (-amin) 24887096Salfred * file birth time (-Bmin) 24987096Salfred * last change of file status information (-cmin) 25087096Salfred * file modification time (-mmin) 25187096Salfred * and the current time is n min periods. 25287096Salfred */ 25387096Salfredint 25487096Salfredf_Xmin(PLAN *plan, FTSENT *entry) 25587096Salfred{ 25687096Salfred if (plan->flags & F_TIME_C) { 25787096Salfred COMPARE((now - entry->fts_statp->st_ctime + 25887096Salfred 60 - 1) / 60, plan->t_data); 25987096Salfred } else if (plan->flags & F_TIME_A) { 26087096Salfred COMPARE((now - entry->fts_statp->st_atime + 261165776Smjacob 60 - 1) / 60, plan->t_data); 26287096Salfred } else if (plan->flags & F_TIME_B) { 26387096Salfred COMPARE((now - entry->fts_statp->st_birthtime + 26487096Salfred 60 - 1) / 60, plan->t_data); 26587096Salfred } else { 26687096Salfred COMPARE((now - entry->fts_statp->st_mtime + 26787096Salfred 60 - 1) / 60, plan->t_data); 26887096Salfred } 26987096Salfred} 27087096Salfred 27187096SalfredPLAN * 27287096Salfredc_Xmin(OPTION *option, char ***argvp) 273165776Smjacob{ 27487096Salfred char *nmins; 27587096Salfred PLAN *new; 27687096Salfred 27787096Salfred nmins = nextarg(option, argvp); 27887096Salfred ftsoptions &= ~FTS_NOSTAT; 279165776Smjacob 28087096Salfred new = palloc(option); 28187096Salfred new->t_data = find_parsenum(new, option->name, nmins, NULL); 28287096Salfred TIME_CORRECT(new); 28387096Salfred return new; 28487096Salfred} 28587096Salfred 28687096Salfred/* 28787096Salfred * -[acm]time n functions -- 28887096Salfred * 28987096Salfred * True if the difference between the 29087096Salfred * file access time (-atime) 29187096Salfred * file birth time (-Btime) 29287096Salfred * last change of file status information (-ctime) 29387096Salfred * file modification time (-mtime) 29487096Salfred * and the current time is n 24 hour periods. 29587096Salfred */ 29687096Salfred 29787096Salfredint 29887096Salfredf_Xtime(PLAN *plan, FTSENT *entry) 29987096Salfred{ 30087096Salfred time_t xtime; 30187096Salfred 30287096Salfred if (plan->flags & F_TIME_A) 30387096Salfred xtime = entry->fts_statp->st_atime; 30487096Salfred else if (plan->flags & F_TIME_B) 30587096Salfred xtime = entry->fts_statp->st_birthtime; 30687096Salfred else if (plan->flags & F_TIME_C) 30787096Salfred xtime = entry->fts_statp->st_ctime; 30887096Salfred else 30987096Salfred xtime = entry->fts_statp->st_mtime; 31087096Salfred 31187096Salfred if (plan->flags & F_EXACTTIME) 31287096Salfred COMPARE(now - xtime, plan->t_data); 31387096Salfred else 31487096Salfred COMPARE((now - xtime + 86400 - 1) / 86400, plan->t_data); 31592911Salfred} 31687096Salfred 31787096SalfredPLAN * 31887096Salfredc_Xtime(OPTION *option, char ***argvp) 31992911Salfred{ 32087096Salfred char *value; 32187096Salfred PLAN *new; 32292911Salfred 32387096Salfred value = nextarg(option, argvp); 32487096Salfred ftsoptions &= ~FTS_NOSTAT; 32587096Salfred 32687096Salfred new = palloc(option); 32787096Salfred new->t_data = find_parsetime(new, option->name, value); 32887096Salfred if (!(new->flags & F_EXACTTIME)) 32987096Salfred TIME_CORRECT(new); 33087096Salfred return new; 33192911Salfred} 33287096Salfred 33387096Salfred/* 33487096Salfred * -maxdepth/-mindepth n functions -- 33587096Salfred * 33692911Salfred * Does the same as -prune if the level of the current file is 337165776Smjacob * greater/less than the specified maximum/minimum depth. 33887096Salfred * 33987096Salfred * Note that -maxdepth and -mindepth are handled specially in 34087096Salfred * find_execute() so their f_* functions are set to f_always_true(). 34187096Salfred */ 34287096SalfredPLAN * 34392911Salfredc_mXXdepth(OPTION *option, char ***argvp) 34487096Salfred{ 34587096Salfred char *dstr; 346165776Smjacob PLAN *new; 34787096Salfred 34887096Salfred dstr = nextarg(option, argvp); 349165776Smjacob if (dstr[0] == '-') 350168640Skuriyama /* all other errors handled by find_parsenum() */ 351168640Skuriyama errx(1, "%s: %s: value must be positive", option->name, dstr); 352168640Skuriyama 35392911Salfred new = palloc(option); 35487096Salfred if (option->flags & F_MAXDEPTH) 35587096Salfred maxdepth = find_parsenum(new, option->name, dstr, NULL); 35687096Salfred else 35787096Salfred mindepth = find_parsenum(new, option->name, dstr, NULL); 35887096Salfred return new; 35987096Salfred} 36087096Salfred 36187096Salfred/* 36287096Salfred * -acl function -- 36387096Salfred * 36487096Salfred * Show files with EXTENDED ACL attributes. 36587096Salfred */ 36687096Salfredint 36787096Salfredf_acl(PLAN *plan __unused, FTSENT *entry) 36887096Salfred{ 36987096Salfred acl_t facl; 37087096Salfred acl_type_t acl_type; 37187096Salfred int acl_supported = 0, ret, trivial; 37287096Salfred 37387096Salfred if (S_ISLNK(entry->fts_statp->st_mode)) 37487096Salfred return 0; 375132254Smr ret = pathconf(entry->fts_accpath, _PC_ACL_NFS4); 376132254Smr if (ret > 0) { 377132254Smr acl_supported = 1; 378132254Smr acl_type = ACL_TYPE_NFS4; 379132254Smr } else if (ret < 0 && errno != EINVAL) { 380132254Smr warn("%s", entry->fts_accpath); 381132254Smr return (0); 382132254Smr } 383132254Smr if (acl_supported == 0) { 384132254Smr ret = pathconf(entry->fts_accpath, _PC_ACL_EXTENDED); 38574462Salfred if (ret > 0) { 38687096Salfred acl_supported = 1; 38784923Salfred acl_type = ACL_TYPE_ACCESS; 38887096Salfred } else if (ret < 0 && errno != EINVAL) { 38987096Salfred warn("%s", entry->fts_accpath); 390132254Smr return (0); 391132254Smr } 39287096Salfred } 39387096Salfred if (acl_supported == 0) 394132254Smr return (0); 39587096Salfred 396132254Smr facl = acl_get_file(entry->fts_accpath, acl_type); 397132254Smr if (facl == NULL) { 398132254Smr warn("%s", entry->fts_accpath); 399132254Smr return (0); 400132254Smr } 401132254Smr ret = acl_is_trivial_np(facl, &trivial); 402132254Smr acl_free(facl); 40387096Salfred if (ret) { 40487096Salfred warn("%s", entry->fts_accpath); 40587096Salfred acl_free(facl); 406132254Smr return (0); 407132254Smr } 408132254Smr if (trivial) 40987096Salfred return (0); 41087096Salfred return (1); 41187096Salfred} 41287096Salfred 41387096SalfredPLAN * 41487096Salfredc_acl(OPTION *option, char ***argvp __unused) 41587096Salfred{ 41687096Salfred ftsoptions &= ~FTS_NOSTAT; 41787096Salfred return (palloc(option)); 41887096Salfred} 41987096Salfred 42087096Salfred/* 42187096Salfred * -delete functions -- 42287096Salfred * 42387096Salfred * True always. Makes its best shot and continues on regardless. 42487096Salfred */ 42587096Salfredint 42687096Salfredf_delete(PLAN *plan __unused, FTSENT *entry) 427132254Smr{ 428132254Smr /* ignore these from fts */ 429132254Smr if (strcmp(entry->fts_accpath, ".") == 0 || 430132254Smr strcmp(entry->fts_accpath, "..") == 0) 431132254Smr return 1; 432132254Smr 433132254Smr /* sanity check */ 434132254Smr if (isdepth == 0 || /* depth off */ 435132254Smr (ftsoptions & FTS_NOSTAT)) /* not stat()ing */ 43687096Salfred errx(1, "-delete: insecure options got turned on"); 43787096Salfred 43887096Salfred if (!(ftsoptions & FTS_PHYSICAL) || /* physical off */ 43987096Salfred (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ 44087096Salfred errx(1, "-delete: forbidden when symlinks are followed"); 44187096Salfred 44287096Salfred /* Potentially unsafe - do not accept relative paths whatsoever */ 44387199Salfred if (strchr(entry->fts_accpath, '/') != NULL) 444132254Smr errx(1, "-delete: %s: relative path potentially not safe", 445132254Smr entry->fts_accpath); 44687199Salfred 44787096Salfred /* Turn off user immutable bits if running as root */ 44887096Salfred if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 44987096Salfred !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 45087096Salfred geteuid() == 0) 45187096Salfred lchflags(entry->fts_accpath, 45287096Salfred entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 45387096Salfred 45487096Salfred /* rmdir directories, unlink everything else */ 45587096Salfred if (S_ISDIR(entry->fts_statp->st_mode)) { 45687096Salfred if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) 45787096Salfred warn("-delete: rmdir(%s)", entry->fts_path); 45887096Salfred } else { 45987096Salfred if (unlink(entry->fts_accpath) < 0) 46087096Salfred warn("-delete: unlink(%s)", entry->fts_path); 46187096Salfred } 46287096Salfred 46387096Salfred /* "succeed" */ 46487096Salfred return 1; 46587096Salfred} 46687096Salfred 467132254SmrPLAN * 46887096Salfredc_delete(OPTION *option, char ***argvp __unused) 46987096Salfred{ 47087096Salfred 47187096Salfred ftsoptions &= ~FTS_NOSTAT; /* no optimise */ 47287096Salfred isoutput = 1; /* possible output */ 47387096Salfred isdepth = 1; /* -depth implied */ 47487096Salfred 47587096Salfred return palloc(option); 47687096Salfred} 47784923Salfred 47884923Salfred 47987096Salfred/* 48084923Salfred * always_true -- 48187096Salfred * 48287096Salfred * Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true 48384923Salfred */ 48487096Salfredint 48587096Salfredf_always_true(PLAN *plan __unused, FTSENT *entry __unused) 48687096Salfred{ 48787096Salfred return 1; 48887096Salfred} 48987096Salfred 49087096Salfred/* 49187096Salfred * -depth functions -- 49287096Salfred * 49387096Salfred * With argument: True if the file is at level n. 49484923Salfred * Without argument: Always true, causes descent of the directory hierarchy 49587096Salfred * to be done so that all entries in a directory are acted on before the 49684923Salfred * directory itself. 49787096Salfred */ 49884923Salfredint 49984923Salfredf_depth(PLAN *plan, FTSENT *entry) 50087096Salfred{ 50184923Salfred if (plan->flags & F_DEPTH) 50287096Salfred COMPARE(entry->fts_level, plan->d_data); 50387096Salfred else 50487096Salfred return 1; 505165776Smjacob} 50687096Salfred 50774462SalfredPLAN * 50887096Salfredc_depth(OPTION *option, char ***argvp) 50987096Salfred{ 51087096Salfred PLAN *new; 51187096Salfred char *str; 51287096Salfred 51387096Salfred new = palloc(option); 51487096Salfred 51587096Salfred str = **argvp; 51687096Salfred if (str && !(new->flags & F_DEPTH)) { 51787096Salfred /* skip leading + or - */ 51887096Salfred if (*str == '+' || *str == '-') 51987096Salfred str++; 52087096Salfred /* skip sign */ 52174462Salfred if (*str == '+' || *str == '-') 52287096Salfred str++; 52387096Salfred if (isdigit(*str)) 52487096Salfred new->flags |= F_DEPTH; 52587096Salfred } 526146445Scharnier 52787096Salfred if (new->flags & F_DEPTH) { /* -depth n */ 52887096Salfred char *ndepth; 52987096Salfred 53087096Salfred ndepth = nextarg(option, argvp); 53187096Salfred new->d_data = find_parsenum(new, option->name, ndepth, NULL); 53287096Salfred } else { /* -d */ 53387096Salfred isdepth = 1; 53487096Salfred } 53587096Salfred 53687096Salfred return new; 53787096Salfred} 53887096Salfred 53987096Salfred/* 54087096Salfred * -empty functions -- 54187096Salfred * 54287096Salfred * True if the file or directory is empty 54387096Salfred */ 54487096Salfredint 54587096Salfredf_empty(PLAN *plan __unused, FTSENT *entry) 54687096Salfred{ 54787096Salfred if (S_ISREG(entry->fts_statp->st_mode) && 548146445Scharnier entry->fts_statp->st_size == 0) 54987096Salfred return 1; 55087096Salfred if (S_ISDIR(entry->fts_statp->st_mode)) { 55187096Salfred struct dirent *dp; 55287096Salfred int empty; 55387096Salfred DIR *dir; 55487096Salfred 55587096Salfred empty = 1; 55687096Salfred dir = opendir(entry->fts_accpath); 55787096Salfred if (dir == NULL) 55887096Salfred err(1, "%s", entry->fts_accpath); 55987096Salfred for (dp = readdir(dir); dp; dp = readdir(dir)) 56087096Salfred if (dp->d_name[0] != '.' || 56187096Salfred (dp->d_name[1] != '\0' && 56287096Salfred (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { 56387096Salfred empty = 0; 56487096Salfred break; 56587096Salfred } 56687096Salfred closedir(dir); 56787096Salfred return empty; 56887096Salfred } 56987096Salfred return 0; 57087096Salfred} 57187096Salfred 57287096SalfredPLAN * 57387096Salfredc_empty(OPTION *option, char ***argvp __unused) 57487096Salfred{ 57587096Salfred ftsoptions &= ~FTS_NOSTAT; 57687096Salfred 57787096Salfred return palloc(option); 57887096Salfred} 57987096Salfred 58087096Salfred/* 58187096Salfred * [-exec | -execdir | -ok] utility [arg ... ] ; functions -- 58287096Salfred * 58387096Salfred * True if the executed utility returns a zero value as exit status. 584146445Scharnier * The end of the primary expression is delimited by a semicolon. If 58587096Salfred * "{}" occurs anywhere, it gets replaced by the current pathname, 58687096Salfred * or, in the case of -execdir, the current basename (filename 58787096Salfred * without leading directory prefix). For -exec and -ok, 58887096Salfred * the current directory for the execution of utility is the same as 58987096Salfred * the current directory when the find utility was started, whereas 59087096Salfred * for -execdir, it is the directory the file resides in. 59187096Salfred * 59287096Salfred * The primary -ok differs from -exec in that it requests affirmation 59387096Salfred * of the user before executing the utility. 59487096Salfred */ 59587096Salfredint 59687096Salfredf_exec(PLAN *plan, FTSENT *entry) 59787096Salfred{ 59887096Salfred int cnt; 59987096Salfred pid_t pid; 60087096Salfred int status; 60187096Salfred char *file; 60287096Salfred 60387096Salfred if (entry == NULL && plan->flags & F_EXECPLUS) { 60487096Salfred if (plan->e_ppos == plan->e_pbnum) 60587096Salfred return (1); 60687096Salfred plan->e_argv[plan->e_ppos] = NULL; 60787096Salfred goto doexec; 60887096Salfred } 60987096Salfred 61087096Salfred /* XXX - if file/dir ends in '/' this will not work -- can it? */ 61187096Salfred if ((plan->flags & F_EXECDIR) && \ 61287096Salfred (file = strrchr(entry->fts_path, '/'))) 61387096Salfred file++; 614146445Scharnier else 61587096Salfred file = entry->fts_path; 61687096Salfred 61787096Salfred if (plan->flags & F_EXECPLUS) { 61887096Salfred if ((plan->e_argv[plan->e_ppos] = strdup(file)) == NULL) 61987096Salfred err(1, NULL); 62087096Salfred plan->e_len[plan->e_ppos] = strlen(file); 62187096Salfred plan->e_psize += plan->e_len[plan->e_ppos]; 62287096Salfred if (++plan->e_ppos < plan->e_pnummax && 62387096Salfred plan->e_psize < plan->e_psizemax) 62487096Salfred return (1); 62587096Salfred plan->e_argv[plan->e_ppos] = NULL; 62687096Salfred } else { 62787096Salfred for (cnt = 0; plan->e_argv[cnt]; ++cnt) 62887096Salfred if (plan->e_len[cnt]) 62987096Salfred brace_subst(plan->e_orig[cnt], 63087096Salfred &plan->e_argv[cnt], file, 63187096Salfred plan->e_len[cnt]); 63287096Salfred } 63387096Salfred 63487096Salfreddoexec: if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv)) 63587096Salfred return 0; 63687096Salfred 63787096Salfred /* make sure find output is interspersed correctly with subprocesses */ 63887096Salfred fflush(stdout); 63987096Salfred fflush(stderr); 64087096Salfred 64187096Salfred switch (pid = fork()) { 64287096Salfred case -1: 64387096Salfred err(1, "fork"); 64487096Salfred /* NOTREACHED */ 64587096Salfred case 0: 64687096Salfred /* change dir back from where we started */ 64787096Salfred if (!(plan->flags & F_EXECDIR) && fchdir(dotfd)) { 64887096Salfred warn("chdir"); 64987096Salfred _exit(1); 65087096Salfred } 65187096Salfred execvp(plan->e_argv[0], plan->e_argv); 65287096Salfred warn("%s", plan->e_argv[0]); 65387096Salfred _exit(1); 65487096Salfred } 65587096Salfred if (plan->flags & F_EXECPLUS) { 65687096Salfred while (--plan->e_ppos >= plan->e_pbnum) 65787096Salfred free(plan->e_argv[plan->e_ppos]); 65887096Salfred plan->e_ppos = plan->e_pbnum; 65987096Salfred plan->e_psize = plan->e_pbsize; 66087096Salfred } 66187096Salfred pid = waitpid(pid, &status, 0); 66287096Salfred return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 66387096Salfred} 66487096Salfred 66587096Salfred/* 66687096Salfred * c_exec, c_execdir, c_ok -- 66787096Salfred * build three parallel arrays, one with pointers to the strings passed 66887096Salfred * on the command line, one with (possibly duplicated) pointers to the 66987096Salfred * argv array, and one with integer values that are lengths of the 67087096Salfred * strings, but also flags meaning that the string has to be massaged. 67187096Salfred */ 67287096SalfredPLAN * 67387096Salfredc_exec(OPTION *option, char ***argvp) 67487096Salfred{ 67574462Salfred PLAN *new; /* node returned */ 67687096Salfred long argmax; 67774462Salfred int cnt, i; 67887096Salfred char **argv, **ap, **ep, *p; 67974462Salfred 68087096Salfred /* XXX - was in c_execdir, but seems unnecessary!? 68187096Salfred ftsoptions &= ~FTS_NOSTAT; 68287096Salfred */ 68387096Salfred isoutput = 1; 68487096Salfred 68587096Salfred /* XXX - this is a change from the previous coding */ 68687096Salfred new = palloc(option); 687165776Smjacob 68887096Salfred for (ap = argv = *argvp;; ++ap) { 68987096Salfred if (!*ap) 69087096Salfred errx(1, 69187096Salfred "%s: no terminating \";\" or \"+\"", option->name); 69287096Salfred if (**ap == ';') 69387096Salfred break; 69487096Salfred if (**ap == '+' && ap != argv && strcmp(*(ap - 1), "{}") == 0) { 69587096Salfred new->flags |= F_EXECPLUS; 69687096Salfred break; 69787096Salfred } 69887096Salfred } 69987096Salfred 70087096Salfred if (ap == argv) 70187096Salfred errx(1, "%s: no command specified", option->name); 70287096Salfred 70387096Salfred cnt = ap - *argvp + 1; 70487096Salfred if (new->flags & F_EXECPLUS) { 70587096Salfred new->e_ppos = new->e_pbnum = cnt - 2; 70687096Salfred if ((argmax = sysconf(_SC_ARG_MAX)) == -1) { 70787096Salfred warn("sysconf(_SC_ARG_MAX)"); 70887096Salfred argmax = _POSIX_ARG_MAX; 70987096Salfred } 71087096Salfred argmax -= 1024; 71187096Salfred for (ep = environ; *ep != NULL; ep++) 71287096Salfred argmax -= strlen(*ep) + 1 + sizeof(*ep); 71387096Salfred argmax -= 1 + sizeof(*ep); 71487096Salfred new->e_pnummax = argmax / 16; 71587096Salfred argmax -= sizeof(char *) * new->e_pnummax; 71687096Salfred if (argmax <= 0) 71787096Salfred errx(1, "no space for arguments"); 71887096Salfred new->e_psizemax = argmax; 71987096Salfred new->e_pbsize = 0; 72087096Salfred cnt += new->e_pnummax + 1; 72187096Salfred new->e_next = lastexecplus; 72287096Salfred lastexecplus = new; 72387096Salfred } 72487096Salfred if ((new->e_argv = malloc(cnt * sizeof(char *))) == NULL) 72587096Salfred err(1, NULL); 72687096Salfred if ((new->e_orig = malloc(cnt * sizeof(char *))) == NULL) 72787096Salfred err(1, NULL); 72887096Salfred if ((new->e_len = malloc(cnt * sizeof(int))) == NULL) 72987096Salfred err(1, NULL); 730168640Skuriyama 73187096Salfred for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 73287096Salfred new->e_orig[cnt] = *argv; 73387096Salfred if (new->flags & F_EXECPLUS) 73487096Salfred new->e_pbsize += strlen(*argv) + 1; 73587096Salfred for (p = *argv; *p; ++p) 73687096Salfred if (!(new->flags & F_EXECPLUS) && p[0] == '{' && 73787096Salfred p[1] == '}') { 73887096Salfred if ((new->e_argv[cnt] = 73987096Salfred malloc(MAXPATHLEN)) == NULL) 74087096Salfred err(1, NULL); 74187096Salfred new->e_len[cnt] = MAXPATHLEN; 74287096Salfred break; 74387096Salfred } 74487096Salfred if (!*p) { 74587096Salfred new->e_argv[cnt] = *argv; 74687096Salfred new->e_len[cnt] = 0; 74787096Salfred } 74874462Salfred } 74987096Salfred if (new->flags & F_EXECPLUS) { 750168640Skuriyama new->e_psize = new->e_pbsize; 75187096Salfred cnt--; 75287096Salfred for (i = 0; i < new->e_pnummax; i++) { 75387096Salfred new->e_argv[cnt] = NULL; 75487096Salfred new->e_len[cnt] = 0; 75587096Salfred cnt++; 75687096Salfred } 757165776Smjacob argv = ap; 758168640Skuriyama goto done; 75987096Salfred } 76087096Salfred new->e_argv[cnt] = new->e_orig[cnt] = NULL; 76187096Salfred 76287096Salfreddone: *argvp = argv + 1; 76387096Salfred return new; 76487096Salfred} 76587096Salfred 76687096Salfred/* Finish any pending -exec ... {} + functions. */ 767168640Skuriyamavoid 76887096Salfredfinish_execplus(void) 76987096Salfred{ 77087096Salfred PLAN *p; 771168640Skuriyama 77287096Salfred p = lastexecplus; 77387096Salfred while (p != NULL) { 77487096Salfred (p->execute)(p, NULL); 77587096Salfred p = p->e_next; 77687096Salfred } 77787096Salfred} 77887096Salfred 77987096Salfredint 78087096Salfredf_flags(PLAN *plan, FTSENT *entry) 78187096Salfred{ 78287096Salfred u_long flags; 78387096Salfred 78487096Salfred flags = entry->fts_statp->st_flags; 78587096Salfred if (plan->flags & F_ATLEAST) 78687096Salfred return (flags | plan->fl_flags) == flags && 78787096Salfred !(flags & plan->fl_notflags); 78887096Salfred else if (plan->flags & F_ANY) 78987096Salfred return (flags & plan->fl_flags) || 79087096Salfred (flags | plan->fl_notflags) != flags; 79187096Salfred else 79287096Salfred return flags == plan->fl_flags && 79387096Salfred !(plan->fl_flags & plan->fl_notflags); 79487096Salfred} 79587096Salfred 79687096SalfredPLAN * 79787096Salfredc_flags(OPTION *option, char ***argvp) 79887096Salfred{ 79987096Salfred char *flags_str; 80087096Salfred PLAN *new; 80187096Salfred u_long flags, notflags; 80287096Salfred 80387096Salfred flags_str = nextarg(option, argvp); 80487096Salfred ftsoptions &= ~FTS_NOSTAT; 80587096Salfred 80687096Salfred new = palloc(option); 80787096Salfred 80887096Salfred if (*flags_str == '-') { 80987096Salfred new->flags |= F_ATLEAST; 81087096Salfred flags_str++; 81187096Salfred } else if (*flags_str == '+') { 81287096Salfred new->flags |= F_ANY; 81387096Salfred flags_str++; 814165776Smjacob } 81587096Salfred if (strtofflags(&flags_str, &flags, ¬flags) == 1) 81687096Salfred errx(1, "%s: %s: illegal flags string", option->name, flags_str); 81787096Salfred 81887096Salfred new->fl_flags = flags; 81987096Salfred new->fl_notflags = notflags; 82087096Salfred return new; 82184923Salfred} 82287096Salfred 82387096Salfred/* 82487096Salfred * -follow functions -- 82587096Salfred * 82684923Salfred * Always true, causes symbolic links to be followed on a global 82787096Salfred * basis. 82874462Salfred */ 82987096SalfredPLAN * 83087096Salfredc_follow(OPTION *option, char ***argvp __unused) 83187096Salfred{ 83287096Salfred ftsoptions &= ~FTS_PHYSICAL; 83387096Salfred ftsoptions |= FTS_LOGICAL; 83487096Salfred 83587096Salfred return palloc(option); 83687096Salfred} 83787096Salfred 83887096Salfred/* 83987096Salfred * -fstype functions -- 84087096Salfred * 84187096Salfred * True if the file is of a certain type. 84287096Salfred */ 84387096Salfredint 84487096Salfredf_fstype(PLAN *plan, FTSENT *entry) 845165776Smjacob{ 84687096Salfred static dev_t curdev; /* need a guaranteed illegal dev value */ 84787096Salfred static int first = 1; 84887096Salfred struct statfs sb; 84987096Salfred static int val_type, val_flags; 85087096Salfred char *p, save[2] = {0,0}; 85187096Salfred 85287096Salfred if ((plan->flags & F_MTMASK) == F_MTUNKNOWN) 85387096Salfred return 0; 85487096Salfred 85587096Salfred /* Only check when we cross mount point. */ 85687096Salfred if (first || curdev != entry->fts_statp->st_dev) { 85787096Salfred curdev = entry->fts_statp->st_dev; 85887096Salfred 85987096Salfred /* 86087096Salfred * Statfs follows symlinks; find wants the link's filesystem, 86187096Salfred * not where it points. 86287096Salfred */ 86384923Salfred if (entry->fts_info == FTS_SL || 86484923Salfred entry->fts_info == FTS_SLNONE) { 865165776Smjacob if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 86687096Salfred ++p; 86787096Salfred else 868165776Smjacob p = entry->fts_accpath; 86987096Salfred save[0] = p[0]; 87087096Salfred p[0] = '.'; 87187096Salfred save[1] = p[1]; 87287096Salfred p[1] = '\0'; 87387096Salfred } else 87487096Salfred p = NULL; 87587096Salfred 87687096Salfred if (statfs(entry->fts_accpath, &sb)) 877165776Smjacob err(1, "%s", entry->fts_accpath); 87887096Salfred 87987096Salfred if (p) { 88087096Salfred p[0] = save[0]; 88187096Salfred p[1] = save[1]; 88287096Salfred } 88387096Salfred 88487096Salfred first = 0; 88587096Salfred 88687096Salfred /* 88787096Salfred * Further tests may need both of these values, so 88887096Salfred * always copy both of them. 88987096Salfred */ 89087096Salfred val_flags = sb.f_flags; 89187096Salfred val_type = sb.f_type; 89287096Salfred } 89387096Salfred switch (plan->flags & F_MTMASK) { 89487096Salfred case F_MTFLAG: 89587096Salfred return val_flags & plan->mt_data; 89687096Salfred case F_MTTYPE: 89787096Salfred return val_type == plan->mt_data; 89887096Salfred default: 89987096Salfred abort(); 90087096Salfred } 90187096Salfred} 90287096Salfred 90387096SalfredPLAN * 90487096Salfredc_fstype(OPTION *option, char ***argvp) 90587096Salfred{ 90674462Salfred char *fsname; 90787096Salfred PLAN *new; 90887096Salfred struct xvfsconf vfc; 90987096Salfred 91087096Salfred fsname = nextarg(option, argvp); 91174462Salfred ftsoptions &= ~FTS_NOSTAT; 91274462Salfred 91374462Salfred new = palloc(option); 91487096Salfred 91587096Salfred /* 91687096Salfred * Check first for a filesystem name. 91787096Salfred */ 91887096Salfred if (getvfsbyname(fsname, &vfc) == 0) { 91987096Salfred new->flags |= F_MTTYPE; 92087096Salfred new->mt_data = vfc.vfc_typenum; 92187096Salfred return new; 92274462Salfred } 92387096Salfred 92487096Salfred switch (*fsname) { 92587096Salfred case 'l': 92674462Salfred if (!strcmp(fsname, "local")) { 92774462Salfred new->flags |= F_MTFLAG; 92887096Salfred new->mt_data = MNT_LOCAL; 92987096Salfred return new; 93087096Salfred } 93187096Salfred break; 93287096Salfred case 'r': 93387096Salfred if (!strcmp(fsname, "rdonly")) { 93487096Salfred new->flags |= F_MTFLAG; 93587096Salfred new->mt_data = MNT_RDONLY; 93687096Salfred return new; 93787096Salfred } 93887096Salfred break; 93987096Salfred } 94087096Salfred 94187096Salfred /* 94287096Salfred * We need to make filesystem checks for filesystems 94387096Salfred * that exists but aren't in the kernel work. 94487096Salfred */ 94587096Salfred fprintf(stderr, "Warning: Unknown filesystem type %s\n", fsname); 946132254Smr new->flags |= F_MTUNKNOWN; 94787096Salfred return new; 94887096Salfred} 94987096Salfred 95087096Salfred/* 95187096Salfred * -group gname functions -- 95287199Salfred * 95387096Salfred * True if the file belongs to the group gname. If gname is numeric and 95487096Salfred * an equivalent of the getgrnam() function does not return a valid group 955132254Smr * name, gname is taken as a group ID. 95687096Salfred */ 95774462Salfredint 95887096Salfredf_group(PLAN *plan, FTSENT *entry) 95987096Salfred{ 960132254Smr COMPARE(entry->fts_statp->st_gid, plan->g_data); 96187096Salfred} 96287096Salfred 96387096SalfredPLAN * 96487096Salfredc_group(OPTION *option, char ***argvp) 96587096Salfred{ 96687096Salfred char *gname; 96787096Salfred PLAN *new; 96887096Salfred struct group *g; 96987199Salfred gid_t gid; 97087096Salfred 97187096Salfred gname = nextarg(option, argvp); 972132254Smr ftsoptions &= ~FTS_NOSTAT; 97387096Salfred 97474462Salfred new = palloc(option); 97587096Salfred g = getgrnam(gname); 97687096Salfred if (g == NULL) { 97787096Salfred char* cp = gname; 97887096Salfred if (gname[0] == '-' || gname[0] == '+') 97987096Salfred gname++; 98087096Salfred gid = atoi(gname); 98187096Salfred if (gid == 0 && gname[0] != '0') 98287096Salfred errx(1, "%s: %s: no such group", option->name, gname); 98387096Salfred gid = find_parsenum(new, option->name, cp, NULL); 98487096Salfred } else 98587096Salfred gid = g->gr_gid; 98687096Salfred 98787096Salfred new->g_data = gid; 98887096Salfred return new; 98987096Salfred} 99087096Salfred 99187096Salfred/* 99287096Salfred * -inum n functions -- 99387096Salfred * 99487096Salfred * True if the file has inode # n. 99587096Salfred */ 99687096Salfredint 99787096Salfredf_inum(PLAN *plan, FTSENT *entry) 998132254Smr{ 99987096Salfred COMPARE(entry->fts_statp->st_ino, plan->i_data); 100087096Salfred} 100187096Salfred 100287096SalfredPLAN * 100387096Salfredc_inum(OPTION *option, char ***argvp) 100487096Salfred{ 100587096Salfred char *inum_str; 100687096Salfred PLAN *new; 100787096Salfred 100887096Salfred inum_str = nextarg(option, argvp); 100987096Salfred ftsoptions &= ~FTS_NOSTAT; 101087096Salfred 101187096Salfred new = palloc(option); 101287096Salfred new->i_data = find_parsenum(new, option->name, inum_str, NULL); 101387096Salfred return new; 101487096Salfred} 101587096Salfred 101687096Salfred/* 101787096Salfred * -samefile FN 101874462Salfred * 101987096Salfred * True if the file has the same inode (eg hard link) FN 102087096Salfred */ 102187096Salfred 102287096Salfred/* f_samefile is just f_inum */ 102374462SalfredPLAN * 102487096Salfredc_samefile(OPTION *option, char ***argvp) 102587096Salfred{ 102674462Salfred char *fn; 102787096Salfred PLAN *new; 102887096Salfred struct stat sb; 102987096Salfred 103087096Salfred fn = nextarg(option, argvp); 103187096Salfred ftsoptions &= ~FTS_NOSTAT; 103287096Salfred 103387096Salfred new = palloc(option); 103487096Salfred if (stat(fn, &sb)) 103587096Salfred err(1, "%s", fn); 103687096Salfred new->i_data = sb.st_ino; 103787096Salfred return new; 103887096Salfred} 103987096Salfred 104087096Salfred/* 104187096Salfred * -links n functions -- 104287096Salfred * 104374462Salfred * True if the file has n links. 104487096Salfred */ 104587096Salfredint 104687096Salfredf_links(PLAN *plan, FTSENT *entry) 104774462Salfred{ 104874462Salfred COMPARE(entry->fts_statp->st_nlink, plan->l_data); 104974462Salfred} 105087096Salfred 105187096SalfredPLAN * 105287096Salfredc_links(OPTION *option, char ***argvp) 105387096Salfred{ 105487096Salfred char *nlinks; 105587096Salfred PLAN *new; 105674462Salfred 105787096Salfred nlinks = nextarg(option, argvp); 105887096Salfred ftsoptions &= ~FTS_NOSTAT; 105974462Salfred 106087096Salfred new = palloc(option); 106187096Salfred new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL); 106287096Salfred return new; 106387096Salfred} 106487096Salfred 106574462Salfred/* 106687096Salfred * -ls functions -- 106787096Salfred * 106887096Salfred * Always true - prints the current entry to stdout in "ls" format. 106987096Salfred */ 107087096Salfredint 107187096Salfredf_ls(PLAN *plan __unused, FTSENT *entry) 107287096Salfred{ 107387096Salfred printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 107487096Salfred return 1; 107587096Salfred} 107687096Salfred 107787096SalfredPLAN * 107887096Salfredc_ls(OPTION *option, char ***argvp __unused) 107987096Salfred{ 108087096Salfred ftsoptions &= ~FTS_NOSTAT; 108187096Salfred isoutput = 1; 108287096Salfred 108387096Salfred return palloc(option); 108487096Salfred} 108587096Salfred 108687096Salfred/* 108787096Salfred * -name functions -- 108887096Salfred * 108987096Salfred * True if the basename of the filename being examined 109087096Salfred * matches pattern using Pattern Matching Notation S3.14 109187096Salfred */ 109287096Salfredint 109387096Salfredf_name(PLAN *plan, FTSENT *entry) 109487096Salfred{ 109587096Salfred char fn[PATH_MAX]; 109687096Salfred const char *name; 109787096Salfred 109887096Salfred if (plan->flags & F_LINK) { 109987096Salfred name = fn; 110087096Salfred if (readlink(entry->fts_path, fn, sizeof(fn)) == -1) 110187096Salfred return 0; 110287096Salfred } else 110387096Salfred name = entry->fts_name; 110487096Salfred return !fnmatch(plan->c_data, name, 110587096Salfred plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); 110687096Salfred} 110787096Salfred 110887096SalfredPLAN * 110987096Salfredc_name(OPTION *option, char ***argvp) 111087096Salfred{ 111187096Salfred char *pattern; 111287096Salfred PLAN *new; 111387096Salfred 111487096Salfred pattern = nextarg(option, argvp); 111587096Salfred new = palloc(option); 111687096Salfred new->c_data = pattern; 111787096Salfred return new; 111887096Salfred} 111987096Salfred 112087096Salfred/* 112187096Salfred * -newer file functions -- 112287096Salfred * 112387096Salfred * True if the current file has been modified more recently 112487096Salfred * then the modification time of the file named by the pathname 112587096Salfred * file. 112674462Salfred */ 112787096Salfredint 112887096Salfredf_newer(PLAN *plan, FTSENT *entry) 112987096Salfred{ 113087096Salfred if (plan->flags & F_TIME_C) 113187096Salfred return entry->fts_statp->st_ctime > plan->t_data; 113287096Salfred else if (plan->flags & F_TIME_A) 113387096Salfred return entry->fts_statp->st_atime > plan->t_data; 113487096Salfred else if (plan->flags & F_TIME_B) 113587096Salfred return entry->fts_statp->st_birthtime > plan->t_data; 1136165776Smjacob else 113787096Salfred return entry->fts_statp->st_mtime > plan->t_data; 113887096Salfred} 113987096Salfred 114087096SalfredPLAN * 114187096Salfredc_newer(OPTION *option, char ***argvp) 114287096Salfred{ 114387096Salfred char *fn_or_tspec; 114487096Salfred PLAN *new; 114587096Salfred struct stat sb; 114687096Salfred 114787096Salfred fn_or_tspec = nextarg(option, argvp); 114887096Salfred ftsoptions &= ~FTS_NOSTAT; 114987096Salfred 115074462Salfred new = palloc(option); 115174462Salfred /* compare against what */ 115274462Salfred if (option->flags & F_TIME2_T) { 115387096Salfred new->t_data = get_date(fn_or_tspec); 115487096Salfred if (new->t_data == (time_t) -1) 115587096Salfred errx(1, "Can't parse date/time: %s", fn_or_tspec); 115687096Salfred } else { 115787096Salfred if (stat(fn_or_tspec, &sb)) 115887096Salfred err(1, "%s", fn_or_tspec); 115987096Salfred if (option->flags & F_TIME2_C) 116087096Salfred new->t_data = sb.st_ctime; 116187096Salfred else if (option->flags & F_TIME2_A) 116287096Salfred new->t_data = sb.st_atime; 116387096Salfred else if (option->flags & F_TIME2_B) 116487096Salfred new->t_data = sb.st_birthtime; 116587096Salfred else 116687096Salfred new->t_data = sb.st_mtime; 116787096Salfred } 116887096Salfred return new; 116987096Salfred} 117087096Salfred 117187096Salfred/* 117287096Salfred * -nogroup functions -- 117387096Salfred * 117487096Salfred * True if file belongs to a user ID for which the equivalent 117587096Salfred * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 117674462Salfred */ 117774462Salfredint 117887096Salfredf_nogroup(PLAN *plan __unused, FTSENT *entry) 117992911Salfred{ 118092911Salfred return group_from_gid(entry->fts_statp->st_gid, 1) == NULL; 118192911Salfred} 118287096Salfred 118387096SalfredPLAN * 118487096Salfredc_nogroup(OPTION *option, char ***argvp __unused) 118587096Salfred{ 118687096Salfred ftsoptions &= ~FTS_NOSTAT; 118787096Salfred 118887096Salfred return palloc(option); 118987096Salfred} 119087096Salfred 119187096Salfred/* 119287096Salfred * -nouser functions -- 119387096Salfred * 119487096Salfred * True if file belongs to a user ID for which the equivalent 119587096Salfred * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 119687096Salfred */ 119787096Salfredint 119887096Salfredf_nouser(PLAN *plan __unused, FTSENT *entry) 1199165775Smjacob{ 1200165775Smjacob return user_from_uid(entry->fts_statp->st_uid, 1) == NULL; 1201165775Smjacob} 1202216603Suqs 1203165775SmjacobPLAN * 1204165775Smjacobc_nouser(OPTION *option, char ***argvp __unused) 1205165775Smjacob{ 1206165775Smjacob ftsoptions &= ~FTS_NOSTAT; 1207165775Smjacob 1208165775Smjacob return palloc(option); 1209166054Sbrueffer} 1210165775Smjacob 1211165775Smjacob/* 1212165775Smjacob * -path functions -- 1213165775Smjacob * 1214165775Smjacob * True if the path of the filename being examined 1215165775Smjacob * matches pattern using Pattern Matching Notation S3.14 1216165775Smjacob */ 1217165775Smjacobint 1218165775Smjacobf_path(PLAN *plan, FTSENT *entry) 1219165775Smjacob{ 1220165775Smjacob return !fnmatch(plan->c_data, entry->fts_path, 1221165775Smjacob plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); 1222165775Smjacob} 1223165775Smjacob 1224165775Smjacob/* c_path is the same as c_name */ 1225165775Smjacob 1226165775Smjacob/* 1227165775Smjacob * -perm functions -- 1228165775Smjacob * 122974462Salfred * The mode argument is used to represent file mode bits. If it starts 123087096Salfred * with a leading digit, it's treated as an octal mode, otherwise as a 123174462Salfred * symbolic mode. 123287096Salfred */ 123387096Salfredint 123487096Salfredf_perm(PLAN *plan, FTSENT *entry) 1235165775Smjacob{ 1236165775Smjacob mode_t mode; 1237165775Smjacob 1238165775Smjacob mode = entry->fts_statp->st_mode & 1239165775Smjacob (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 1240165775Smjacob if (plan->flags & F_ATLEAST) 1241165775Smjacob return (plan->m_data | mode) == mode; 1242165775Smjacob else if (plan->flags & F_ANY) 1243165775Smjacob return (mode & plan->m_data); 1244165775Smjacob else 1245165775Smjacob return mode == plan->m_data; 1246165775Smjacob /* NOTREACHED */ 124787096Salfred} 124887096Salfred 124987096SalfredPLAN * 125087096Salfredc_perm(OPTION *option, char ***argvp) 125187096Salfred{ 125287096Salfred char *perm; 125387096Salfred PLAN *new; 1254165775Smjacob mode_t *set; 125574462Salfred 125674462Salfred perm = nextarg(option, argvp); 125774462Salfred ftsoptions &= ~FTS_NOSTAT; 125887096Salfred 125974462Salfred new = palloc(option); 126074462Salfred 126187096Salfred if (*perm == '-') { 126287096Salfred new->flags |= F_ATLEAST; 126387096Salfred ++perm; 126487096Salfred } else if (*perm == '+') { 126587096Salfred new->flags |= F_ANY; 126687096Salfred ++perm; 126787096Salfred } 126887096Salfred 126987096Salfred if ((set = setmode(perm)) == NULL) 127087096Salfred errx(1, "%s: %s: illegal mode string", option->name, perm); 127187096Salfred 127287096Salfred new->m_data = getmode(set, 0); 127387096Salfred free(set); 127487096Salfred return new; 127587096Salfred} 127687096Salfred 127787096Salfred/* 127887096Salfred * -print functions -- 127987096Salfred * 128087096Salfred * Always true, causes the current pathname to be written to 128187096Salfred * standard output. 128287096Salfred */ 128387096Salfredint 128487096Salfredf_print(PLAN *plan __unused, FTSENT *entry) 128587096Salfred{ 128687096Salfred (void)puts(entry->fts_path); 128787096Salfred return 1; 128874462Salfred} 128987096Salfred 129087096SalfredPLAN * 129187096Salfredc_print(OPTION *option, char ***argvp __unused) 129287096Salfred{ 129387096Salfred isoutput = 1; 129487096Salfred 129587096Salfred return palloc(option); 129687096Salfred} 129787096Salfred 1298132254Smr/* 129987096Salfred * -print0 functions -- 130087096Salfred * 130187096Salfred * Always true, causes the current pathname to be written to 130287096Salfred * standard output followed by a NUL character 1303132254Smr */ 130487096Salfredint 130587096Salfredf_print0(PLAN *plan __unused, FTSENT *entry) 130687096Salfred{ 130787096Salfred fputs(entry->fts_path, stdout); 130887096Salfred fputc('\0', stdout); 130987096Salfred return 1; 131087096Salfred} 131187096Salfred 131287096Salfred/* c_print0 is the same as c_print */ 131387096Salfred 131487096Salfred/* 131587096Salfred * -prune functions -- 1316165776Smjacob * 131787096Salfred * Prune a portion of the hierarchy. 131887096Salfred */ 131987096Salfredint 132087096Salfredf_prune(PLAN *plan __unused, FTSENT *entry) 132187096Salfred{ 1322132254Smr if (fts_set(tree, entry, FTS_SKIP)) 132387096Salfred err(1, "%s", entry->fts_path); 1324132254Smr return 1; 132574462Salfred} 132687096Salfred 132787096Salfred/* c_prune == c_simple */ 132887096Salfred 132987096Salfred/* 133087096Salfred * -regex functions -- 133187096Salfred * 133287096Salfred * True if the whole path of the file matches pattern using 133387096Salfred * regular expression. 133487096Salfred */ 133587096Salfredint 133687096Salfredf_regex(PLAN *plan, FTSENT *entry) 133787096Salfred{ 133887096Salfred char *str; 133987096Salfred int len; 134087096Salfred regex_t *pre; 134187096Salfred regmatch_t pmatch; 134287096Salfred int errcode; 134387096Salfred char errbuf[LINE_MAX]; 134487096Salfred int matched; 134587096Salfred 134687096Salfred pre = plan->re_data; 134787096Salfred str = entry->fts_path; 134887096Salfred len = strlen(str); 134987096Salfred matched = 0; 135087096Salfred 135187096Salfred pmatch.rm_so = 0; 135287096Salfred pmatch.rm_eo = len; 135387096Salfred 135487096Salfred errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); 135587096Salfred 135687096Salfred if (errcode != 0 && errcode != REG_NOMATCH) { 135787096Salfred regerror(errcode, pre, errbuf, sizeof errbuf); 135887096Salfred errx(1, "%s: %s", 135987096Salfred plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf); 136087096Salfred } 136187096Salfred 136287096Salfred if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) 136387096Salfred matched = 1; 136487096Salfred 136587096Salfred return matched; 136687096Salfred} 136787096Salfred 136887096SalfredPLAN * 136987096Salfredc_regex(OPTION *option, char ***argvp) 137087096Salfred{ 137187096Salfred PLAN *new; 137287096Salfred char *pattern; 137387096Salfred regex_t *pre; 137487096Salfred int errcode; 137587096Salfred char errbuf[LINE_MAX]; 137687096Salfred 137787096Salfred if ((pre = malloc(sizeof(regex_t))) == NULL) 137887096Salfred err(1, NULL); 137987096Salfred 138087096Salfred pattern = nextarg(option, argvp); 138187096Salfred 138287096Salfred if ((errcode = regcomp(pre, pattern, 138387096Salfred regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) { 138474462Salfred regerror(errcode, pre, errbuf, sizeof errbuf); 138587096Salfred errx(1, "%s: %s: %s", 138687096Salfred option->flags & F_IGNCASE ? "-iregex" : "-regex", 138774462Salfred pattern, errbuf); 138887096Salfred } 138987096Salfred 139087096Salfred new = palloc(option); 139187096Salfred new->re_data = pre; 139287096Salfred 139387096Salfred return new; 139487096Salfred} 139587096Salfred 139687096Salfred/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */ 1397165776Smjacob 139887096SalfredPLAN * 139987096Salfredc_simple(OPTION *option, char ***argvp __unused) 140087096Salfred{ 140187096Salfred return palloc(option); 140287096Salfred} 140387096Salfred 140487096Salfred/* 140587096Salfred * -size n[c] functions -- 140687096Salfred * 140787096Salfred * True if the file size in bytes, divided by an implementation defined 140887096Salfred * value and rounded up to the next integer, is n. If n is followed by 140987096Salfred * one of c k M G T P, the size is in bytes, kilobytes, 141087096Salfred * megabytes, gigabytes, terabytes or petabytes respectively. 141187096Salfred */ 141287096Salfred#define FIND_SIZE 512 141387096Salfredstatic int divsize = 1; 141487096Salfred 141587096Salfredint 141687096Salfredf_size(PLAN *plan, FTSENT *entry) 141774462Salfred{ 141887096Salfred off_t size; 141987096Salfred 142087096Salfred size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 142187096Salfred FIND_SIZE : entry->fts_statp->st_size; 142287096Salfred COMPARE(size, plan->o_data); 142387096Salfred} 142487096Salfred 142587096SalfredPLAN * 142687096Salfredc_size(OPTION *option, char ***argvp) 142787096Salfred{ 142887096Salfred char *size_str; 142987096Salfred PLAN *new; 143087096Salfred char endch; 143187096Salfred off_t scale; 143287096Salfred 143387096Salfred size_str = nextarg(option, argvp); 143487096Salfred ftsoptions &= ~FTS_NOSTAT; 143587096Salfred 143687096Salfred new = palloc(option); 143787096Salfred endch = 'c'; 143887096Salfred new->o_data = find_parsenum(new, option->name, size_str, &endch); 143987096Salfred if (endch != '\0') { 144087096Salfred divsize = 0; 144187096Salfred 144287096Salfred switch (endch) { 144387096Salfred case 'c': /* characters */ 144487096Salfred scale = 0x1LL; 144587096Salfred break; 1446115004Srwatson case 'k': /* kilobytes 1<<10 */ 1447115004Srwatson scale = 0x400LL; 1448115004Srwatson break; 1449115004Srwatson case 'M': /* megabytes 1<<20 */ 1450115004Srwatson scale = 0x100000LL; 1451115004Srwatson break; 1452115004Srwatson case 'G': /* gigabytes 1<<30 */ 1453115004Srwatson scale = 0x40000000LL; 1454115004Srwatson break; 1455115004Srwatson case 'T': /* terabytes 1<<40 */ 1456115004Srwatson scale = 0x1000000000LL; 145774462Salfred break; 145887096Salfred case 'P': /* petabytes 1<<50 */ 145987096Salfred scale = 0x4000000000000LL; 146087096Salfred break; 146187096Salfred default: 146287096Salfred errx(1, "%s: %s: illegal trailing character", 146387096Salfred option->name, size_str); 146487096Salfred break; 146587096Salfred } 146687096Salfred if (new->o_data > QUAD_MAX / scale) 146787096Salfred errx(1, "%s: %s: value too large", 146887096Salfred option->name, size_str); 146987096Salfred new->o_data *= scale; 147087096Salfred } 147187096Salfred return new; 147287096Salfred} 147387096Salfred 147487096Salfred/* 147587096Salfred * -type c functions -- 147687096Salfred * 147787096Salfred * True if the type of the file is c, where c is b, c, d, p, f or w 147887096Salfred * for block special file, character special file, directory, FIFO, 147987096Salfred * regular file or whiteout respectively. 148087096Salfred */ 148187096Salfredint 148287096Salfredf_type(PLAN *plan, FTSENT *entry) 148387096Salfred{ 148487096Salfred return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data; 148587096Salfred} 148687096Salfred 148787096SalfredPLAN * 148887096Salfredc_type(OPTION *option, char ***argvp) 148987096Salfred{ 149087096Salfred char *typestring; 149187096Salfred PLAN *new; 149287096Salfred mode_t mask; 149387096Salfred 149487096Salfred typestring = nextarg(option, argvp); 149587096Salfred ftsoptions &= ~FTS_NOSTAT; 149687096Salfred 149787096Salfred switch (typestring[0]) { 149887096Salfred case 'b': 149987096Salfred mask = S_IFBLK; 150087096Salfred break; 150187096Salfred case 'c': 150287096Salfred mask = S_IFCHR; 150387096Salfred break; 150487096Salfred case 'd': 150587096Salfred mask = S_IFDIR; 150687096Salfred break; 150787096Salfred case 'f': 150887096Salfred mask = S_IFREG; 150987096Salfred break; 151087096Salfred case 'l': 151187096Salfred mask = S_IFLNK; 151287096Salfred break; 151387096Salfred case 'p': 151487096Salfred mask = S_IFIFO; 151587096Salfred break; 151687096Salfred case 's': 151787096Salfred mask = S_IFSOCK; 151887096Salfred break; 151987096Salfred#ifdef FTS_WHITEOUT 152087096Salfred case 'w': 152187096Salfred mask = S_IFWHT; 152287096Salfred ftsoptions |= FTS_WHITEOUT; 152387096Salfred break; 152487096Salfred#endif /* FTS_WHITEOUT */ 152587096Salfred default: 152687096Salfred errx(1, "%s: %s: unknown type", option->name, typestring); 152787096Salfred } 152887096Salfred 152974462Salfred new = palloc(option); 153087096Salfred new->m_data = mask; 153187096Salfred return new; 153287096Salfred} 153387096Salfred 153487096Salfred/* 153587096Salfred * -user uname functions -- 153687096Salfred * 153787096Salfred * True if the file belongs to the user uname. If uname is numeric and 153887096Salfred * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 153987096Salfred * return a valid user name, uname is taken as a user ID. 154087096Salfred */ 154187096Salfredint 154287096Salfredf_user(PLAN *plan, FTSENT *entry) 154387096Salfred{ 154487096Salfred COMPARE(entry->fts_statp->st_uid, plan->u_data); 154587096Salfred} 154687096Salfred 154787096SalfredPLAN * 154887096Salfredc_user(OPTION *option, char ***argvp) 154987096Salfred{ 1550165776Smjacob char *username; 155187096Salfred PLAN *new; 155287096Salfred struct passwd *p; 155387096Salfred uid_t uid; 155487096Salfred 155587096Salfred username = nextarg(option, argvp); 155674462Salfred ftsoptions &= ~FTS_NOSTAT; 155787096Salfred 155887096Salfred new = palloc(option); 155987096Salfred p = getpwnam(username); 156087096Salfred if (p == NULL) { 156187096Salfred char* cp = username; 156287096Salfred if( username[0] == '-' || username[0] == '+' ) 156374462Salfred username++; 156474462Salfred uid = atoi(username); 156587096Salfred if (uid == 0 && username[0] != '0') 156687096Salfred errx(1, "%s: %s: no such user", option->name, username); 156787096Salfred uid = find_parsenum(new, option->name, cp, NULL); 156887096Salfred } else 156987096Salfred uid = p->pw_uid; 157074462Salfred 1571165776Smjacob new->u_data = uid; 157287096Salfred return new; 1573165776Smjacob} 157487096Salfred 157587096Salfred/* 157687096Salfred * -xdev functions -- 157787096Salfred * 157887096Salfred * Always true, causes find not to descend past directories that have a 157987096Salfred * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 158087096Salfred */ 158187096SalfredPLAN * 158287096Salfredc_xdev(OPTION *option, char ***argvp __unused) 158387096Salfred{ 158487096Salfred ftsoptions |= FTS_XDEV; 158574462Salfred 158687096Salfred return palloc(option); 158787096Salfred} 158887096Salfred 158987096Salfred/* 159087096Salfred * ( expression ) functions -- 159187096Salfred * 159287096Salfred * True if expression is true. 159387096Salfred */ 159487096Salfredint 159587096Salfredf_expr(PLAN *plan, FTSENT *entry) 159687096Salfred{ 159787096Salfred PLAN *p; 159887096Salfred int state = 0; 159987096Salfred 160087096Salfred for (p = plan->p_data[0]; 160187096Salfred p && (state = (p->execute)(p, entry)); p = p->next); 160287096Salfred return state; 1603165776Smjacob} 160487096Salfred 160587096Salfred/* 160687096Salfred * f_openparen and f_closeparen nodes are temporary place markers. They are 160787096Salfred * eliminated during phase 2 of find_formplan() --- the '(' node is converted 160887096Salfred * to a f_expr node containing the expression and the ')' node is discarded. 160987096Salfred * The functions themselves are only used as constants. 161087096Salfred */ 161187096Salfred 161287096Salfredint 161387096Salfredf_openparen(PLAN *plan __unused, FTSENT *entry __unused) 161487096Salfred{ 161587096Salfred abort(); 161687096Salfred} 161774462Salfred 161887096Salfredint 161987096Salfredf_closeparen(PLAN *plan __unused, FTSENT *entry __unused) 162087096Salfred{ 162187096Salfred abort(); 162274462Salfred} 162374462Salfred 162474462Salfred/* c_openparen == c_simple */ 162587096Salfred/* c_closeparen == c_simple */ 162674462Salfred 162787096Salfred/* 162887096Salfred * AND operator. Since AND is implicit, no node is allocated. 162987096Salfred */ 163087096SalfredPLAN * 163174462Salfredc_and(OPTION *option __unused, char ***argvp __unused) 163274462Salfred{ 1633165776Smjacob return NULL; 163487096Salfred} 163587096Salfred 163687096Salfred/* 163787096Salfred * ! expression functions -- 163887096Salfred * 163987096Salfred * Negation of a primary; the unary NOT operator. 164087096Salfred */ 164187096Salfredint 164287096Salfredf_not(PLAN *plan, FTSENT *entry) 164387096Salfred{ 164487096Salfred PLAN *p; 164587096Salfred int state = 0; 164687096Salfred 164787096Salfred for (p = plan->p_data[0]; 164887096Salfred p && (state = (p->execute)(p, entry)); p = p->next); 164987096Salfred return !state; 165087096Salfred} 165187096Salfred 165287096Salfred/* c_not == c_simple */ 165387096Salfred 165487096Salfred/* 165587096Salfred * expression -o expression functions -- 165687096Salfred * 165787096Salfred * Alternation of primaries; the OR operator. The second expression is 165887096Salfred * not evaluated if the first expression is true. 165987096Salfred */ 166087096Salfredint 166187096Salfredf_or(PLAN *plan, FTSENT *entry) 166287096Salfred{ 166387096Salfred PLAN *p; 166487096Salfred int state = 0; 166587096Salfred 166687096Salfred for (p = plan->p_data[0]; 166787096Salfred p && (state = (p->execute)(p, entry)); p = p->next); 166887096Salfred 166987096Salfred if (state) 167087096Salfred return 1; 167187096Salfred 167287096Salfred for (p = plan->p_data[1]; 167387096Salfred p && (state = (p->execute)(p, entry)); p = p->next); 167487096Salfred return state; 167587096Salfred} 167687096Salfred 167787096Salfred/* c_or == c_simple */ 167887096Salfred 167987096Salfred/* 168087096Salfred * -false 168187096Salfred * 168287096Salfred * Always false. 168387096Salfred */ 168487096Salfredint 168587096Salfredf_false(PLAN *plan __unused, FTSENT *entry __unused) 168687096Salfred{ 168787096Salfred return 0; 168887096Salfred} 168987096Salfred 169087096Salfred/* c_false == c_simple */ 169187096Salfred 169287096Salfred/* 169387096Salfred * -quit 169487096Salfred * 169587096Salfred * Exits the program 169687096Salfred */ 169787096Salfredint 169887096Salfredf_quit(PLAN *plan __unused, FTSENT *entry __unused) 169987096Salfred{ 170087096Salfred exit(0); 170187096Salfred} 170287096Salfred 170387096Salfred/* c_quit == c_simple */ 170487096Salfred