diff options
| author | Poul-Henning Kamp <phk@FreeBSD.org> | 2001-05-03 18:05:35 +0000 |
|---|---|---|
| committer | Poul-Henning Kamp <phk@FreeBSD.org> | 2001-05-03 18:05:35 +0000 |
| commit | ea92232a82ea162614c49a396cc18e3a7c1b8e5c (patch) | |
| tree | e8f0dc285279beeffb244a282e0daf9b786ef7c2 /usr.bin/find/function.c | |
| parent | 96fc25ff5ebcf760b84ee81ad4bdcfa035556b7c (diff) | |
Notes
Diffstat (limited to 'usr.bin/find/function.c')
| -rw-r--r-- | usr.bin/find/function.c | 1296 |
1 files changed, 537 insertions, 759 deletions
diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c index d448946cb2de..a60c4c41a2b9 100644 --- a/usr.bin/find/function.c +++ b/usr.bin/find/function.c @@ -48,6 +48,7 @@ static const char rcsid[] = #include <sys/stat.h> #include <sys/wait.h> #include <sys/mount.h> +#include <sys/timeb.h> #include <dirent.h> #include <err.h> @@ -64,8 +65,10 @@ static const char rcsid[] = #include "find.h" +time_t get_date __P((char *date, struct timeb *now)); + #define COMPARE(a, b) { \ - switch (plan->flags) { \ + switch (plan->flags & F_ELG_MASK) { \ case F_EQUAL: \ return (a == b); \ case F_LESSTHAN: \ @@ -77,9 +80,19 @@ static const char rcsid[] = } \ } -static int do_f_regex __P((PLAN *, FTSENT *, int)); -static PLAN *do_c_regex __P((char *, int)); -static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *)))); +static PLAN * +palloc(option) + OPTION *option; +{ + PLAN *new; + + if ((new = malloc(sizeof(PLAN))) == NULL) + err(1, NULL); + new->execute = option->execute; + new->flags = option->flags; + new->next = NULL; + return new; +} /* * find_parsenum -- @@ -98,14 +111,14 @@ find_parsenum(plan, option, vp, endch) switch (*str) { case '+': ++str; - plan->flags = F_GREATER; + plan->flags |= F_GREATER; break; case '-': ++str; - plan->flags = F_LESSTHAN; + plan->flags |= F_LESSTHAN; break; default: - plan->flags = F_EQUAL; + plan->flags |= F_EQUAL; break; } @@ -121,143 +134,218 @@ find_parsenum(plan, option, vp, endch) errx(1, "%s: %s: illegal trailing character", option, vp); if (endch) *endch = endchar[0]; - return (value); + return value; } /* + * nextarg -- + * Check that another argument still exists, return a pointer to it, + * and increment the argument vector pointer. + */ +static char * +nextarg(option, argvp) + OPTION *option; + char ***argvp; +{ + char *arg; + + if ((arg = **argvp) == 0) + errx(1, "%s: requires additional arguments", option->name); + (*argvp)++; + return arg; +} /* nextarg() */ + +/* * The value of n for the inode times (atime, ctime, and mtime) is a range, * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with * -n, such that "-mtime -1" would be less than 0 days, which isn't what the * user wanted. Correct so that -1 is "less than 1". */ -#define TIME_CORRECT(p, ttype) \ - if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ +#define TIME_CORRECT(p) \ + if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \ ++((p)->t_data); /* - * -amin n functions -- + * -[acm]min n functions -- * - * True if the difference between the file access time and the - * current time is n min periods. + * True if the difference between the + * file access time (-amin) + * last change of file status information (-cmin) + * file modification time (-mmin) + * and the current time is n min periods. */ int -f_amin(plan, entry) +f_Xmin(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; - COMPARE((now - entry->fts_statp->st_atime + - 60 - 1) / 60, plan->t_data); + if (plan->flags & F_TIME_C) { + COMPARE((now - entry->fts_statp->st_ctime + + 60 - 1) / 60, plan->t_data); + } else if (plan->flags & F_TIME_A) { + COMPARE((now - entry->fts_statp->st_atime + + 60 - 1) / 60, plan->t_data); + } else { + COMPARE((now - entry->fts_statp->st_mtime + + 60 - 1) / 60, plan->t_data); + } } PLAN * -c_amin(arg) - char *arg; +c_Xmin(option, argvp) + OPTION *option; + char ***argvp; { + char *nmins; PLAN *new; + nmins = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_AMIN, f_amin); - new->t_data = find_parsenum(new, "-amin", arg, NULL); - TIME_CORRECT(new, N_AMIN); - return (new); + new = palloc(option); + new->t_data = find_parsenum(new, option->name, nmins, NULL); + TIME_CORRECT(new); + return new; } - /* - * -atime n functions -- + * -[acm]time n functions -- * - * True if the difference between the file access time and the - * current time is n 24 hour periods. + * True if the difference between the + * file access time (-atime) + * last change of file status information (-ctime) + * file modification time (-mtime) + * and the current time is n 24 hour periods. */ + int -f_atime(plan, entry) +f_Xtime(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; - COMPARE((now - entry->fts_statp->st_atime + - 86400 - 1) / 86400, plan->t_data); + if (plan->flags & F_TIME_C) { + COMPARE((now - entry->fts_statp->st_ctime + + 86400 - 1) / 86400, plan->t_data); + } else if (plan->flags & F_TIME_A) { + COMPARE((now - entry->fts_statp->st_atime + + 86400 - 1) / 86400, plan->t_data); + } else { + COMPARE((now - entry->fts_statp->st_mtime + + 86400 - 1) / 86400, plan->t_data); + } } PLAN * -c_atime(arg) - char *arg; +c_Xtime(option, argvp) + OPTION *option; + char ***argvp; { + char *ndays; PLAN *new; + ndays = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_ATIME, f_atime); - new->t_data = find_parsenum(new, "-atime", arg, NULL); - TIME_CORRECT(new, N_ATIME); - return (new); + new = palloc(option); + new->t_data = find_parsenum(new, option->name, ndays, NULL); + TIME_CORRECT(new); + return new; } - /* - * -cmin n functions -- + * -maxdepth/-mindepth n functions -- + * + * Does the same as -prune if the level of the current file is + * greater/less than the specified maximum/minimum depth. * - * True if the difference between the last change of file - * status information and the current time is n min periods. + * Note that -maxdepth and -mindepth are handled specially in + * find_execute() so their f_* functions are set to f_always_true(). */ -int -f_cmin(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - extern time_t now; - - COMPARE((now - entry->fts_statp->st_ctime + - 60 - 1) / 60, plan->t_data); -} - PLAN * -c_cmin(arg) - char *arg; +c_mXXdepth(option, argvp) + OPTION *option; + char ***argvp; { + char *dstr; PLAN *new; - ftsoptions &= ~FTS_NOSTAT; + dstr = nextarg(option, argvp); + if (dstr[0] == '-') + /* all other errors handled by find_parsenum() */ + errx(1, "%s: %s: value must be positive", option->name, dstr); - new = palloc(N_CMIN, f_cmin); - new->t_data = find_parsenum(new, "-cmin", arg, NULL); - TIME_CORRECT(new, N_CMIN); - return (new); + new = palloc(option); + if (option->flags & F_MAXDEPTH) + maxdepth = find_parsenum(new, option->name, dstr, NULL); + else + mindepth = find_parsenum(new, option->name, dstr, NULL); + return new; } /* - * -ctime n functions -- + * -delete functions -- * - * True if the difference between the last change of file - * status information and the current time is n 24 hour periods. + * True always. Makes its best shot and continues on regardless. */ int -f_ctime(plan, entry) +f_delete(plan, entry) PLAN *plan; FTSENT *entry; { - extern time_t now; + /* ignore these from fts */ + if (strcmp(entry->fts_accpath, ".") == 0 || + strcmp(entry->fts_accpath, "..") == 0) + return 1; + + /* sanity check */ + if (isdepth == 0 || /* depth off */ + (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ + !(ftsoptions & FTS_PHYSICAL) || /* physical off */ + (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ + errx(1, "-delete: insecure options got turned on"); + + /* Potentially unsafe - do not accept relative paths whatsoever */ + if (strchr(entry->fts_accpath, '/') != NULL) + errx(1, "-delete: %s: relative path potentially not safe", + entry->fts_accpath); - COMPARE((now - entry->fts_statp->st_ctime + - 86400 - 1) / 86400, plan->t_data); + /* Turn off user immutable bits if running as root */ + if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + geteuid() == 0) + chflags(entry->fts_accpath, + entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); + + /* rmdir directories, unlink everything else */ + if (S_ISDIR(entry->fts_statp->st_mode)) { + if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) + warn("-delete: rmdir(%s)", entry->fts_path); + } else { + if (unlink(entry->fts_accpath) < 0) + warn("-delete: unlink(%s)", entry->fts_path); + } + + /* "succeed" */ + return 1; } PLAN * -c_ctime(arg) - char *arg; +c_delete(option, argvp) + OPTION *option; + char ***argvp; { - PLAN *new; - ftsoptions &= ~FTS_NOSTAT; + ftsoptions &= ~FTS_NOSTAT; /* no optimise */ + ftsoptions |= FTS_PHYSICAL; /* disable -follow */ + ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ + isoutput = 1; /* possible output */ + isdepth = 1; /* -depth implied */ - new = palloc(N_CTIME, f_ctime); - new->t_data = find_parsenum(new, "-ctime", arg, NULL); - TIME_CORRECT(new, N_CTIME); - return (new); + return palloc(option); } @@ -273,119 +361,17 @@ f_always_true(plan, entry) PLAN *plan; FTSENT *entry; { - return (1); + return 1; } PLAN * -c_depth() -{ - isdepth = 1; - - return (palloc(N_DEPTH, f_always_true)); -} - -/* - * [-exec | -ok] utility [arg ... ] ; functions -- - * - * True if the executed utility returns a zero value as exit status. - * The end of the primary expression is delimited by a semicolon. If - * "{}" occurs anywhere, it gets replaced by the current pathname. - * The current directory for the execution of utility is the same as - * the current directory when the find utility was started. - * - * The primary -ok is different in that it requests affirmation of the - * user before executing the utility. - */ -int -f_exec(plan, entry) - register PLAN *plan; - FTSENT *entry; -{ - extern int dotfd; - register int cnt; - pid_t pid; - int status; - - for (cnt = 0; plan->e_argv[cnt]; ++cnt) - if (plan->e_len[cnt]) - brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], - entry->fts_path, plan->e_len[cnt]); - - if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) - return (0); - - /* make sure find output is interspersed correctly with subprocesses */ - fflush(stdout); - - switch (pid = fork()) { - case -1: - err(1, "fork"); - /* NOTREACHED */ - case 0: - if (fchdir(dotfd)) { - warn("chdir"); - _exit(1); - } - execvp(plan->e_argv[0], plan->e_argv); - warn("%s", plan->e_argv[0]); - _exit(1); - } - pid = waitpid(pid, &status, 0); - return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); -} - -/* - * c_exec -- - * build three parallel arrays, one with pointers to the strings passed - * on the command line, one with (possibly duplicated) pointers to the - * argv array, and one with integer values that are lengths of the - * strings, but also flags meaning that the string has to be massaged. - */ -PLAN * -c_exec(argvp, isok) +c_depth(option, argvp) + OPTION *option; char ***argvp; - int isok; { - PLAN *new; /* node returned */ - register int cnt; - register char **argv, **ap, *p; - - isoutput = 1; - - new = palloc(N_EXEC, f_exec); - if (isok) - new->flags = F_NEEDOK; - - for (ap = argv = *argvp;; ++ap) { - if (!*ap) - errx(1, - "%s: no terminating \";\"", isok ? "-ok" : "-exec"); - if (**ap == ';') - break; - } - - cnt = ap - *argvp + 1; - new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); - new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); - new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); - - for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { - new->e_orig[cnt] = *argv; - for (p = *argv; *p; ++p) - if (p[0] == '{' && p[1] == '}') { - new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); - new->e_len[cnt] = MAXPATHLEN; - break; - } - if (!*p) { - new->e_argv[cnt] = *argv; - new->e_len[cnt] = 0; - } - } - new->e_argv[cnt] = new->e_orig[cnt] = NULL; + isdepth = 1; - *argvp = argv + 1; - return (new); + return palloc(option); } /* @@ -398,8 +384,9 @@ f_empty(plan, entry) PLAN *plan; FTSENT *entry; { - if (S_ISREG(entry->fts_statp->st_mode) && entry->fts_statp->st_size == 0) - return (1); + if (S_ISREG(entry->fts_statp->st_mode) && + entry->fts_statp->st_size == 0) + return 1; if (S_ISDIR(entry->fts_statp->st_mode)) { struct dirent *dp; int empty; @@ -417,50 +404,63 @@ f_empty(plan, entry) break; } closedir(dir); - return (empty); + return empty; } - return (0); + return 0; } PLAN * -c_empty() +c_empty(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions &= ~FTS_NOSTAT; - return (palloc(N_EMPTY, f_empty)); + return palloc(option); } /* - * -execdir utility [arg ... ] ; functions -- + * [-exec | -execdir | -ok] utility [arg ... ] ; functions -- * * True if the executed utility returns a zero value as exit status. * The end of the primary expression is delimited by a semicolon. If - * "{}" occurs anywhere, it gets replaced by the unqualified pathname. - * The current directory for the execution of utility is the same as - * the directory where the file lives. + * "{}" occurs anywhere, it gets replaced by the current pathname, + * or, in the case of -execdir, the current basename (filename + * without leading directory prefix). For -exec and -ok, + * the current directory for the execution of utility is the same as + * the current directory when the find utility was started, whereas + * for -execdir, it is the directory the file resides in. + * + * The primary -ok differs from -exec in that it requests affirmation + * of the user before executing the utility. */ int -f_execdir(plan, entry) +f_exec(plan, entry) register PLAN *plan; FTSENT *entry; { + extern int dotfd; register int cnt; pid_t pid; int status; char *file; /* XXX - if file/dir ends in '/' this will not work -- can it? */ - if ((file = strrchr(entry->fts_path, '/'))) - file++; + if ((plan->flags & F_EXECDIR) && \ + (file = strrchr(entry->fts_path, '/'))) + file++; else - file = entry->fts_path; + file = entry->fts_path; for (cnt = 0; plan->e_argv[cnt]; ++cnt) if (plan->e_len[cnt]) brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], file, plan->e_len[cnt]); - /* don't mix output of command with find output */ + if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv)) + return 0; + + /* make sure find output is interspersed correctly with subprocesses */ fflush(stdout); fflush(stderr); @@ -469,6 +469,11 @@ f_execdir(plan, entry) err(1, "fork"); /* NOTREACHED */ case 0: + /* change dir back from where we started */ + if (!(plan->flags & F_EXECDIR) && fchdir(dotfd)) { + warn("chdir"); + _exit(1); + } execvp(plan->e_argv[0], plan->e_argv); warn("%s", plan->e_argv[0]); _exit(1); @@ -476,31 +481,35 @@ f_execdir(plan, entry) pid = waitpid(pid, &status, 0); return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); } - + /* - * c_execdir -- + * c_exec, c_execdir, c_ok -- * build three parallel arrays, one with pointers to the strings passed * on the command line, one with (possibly duplicated) pointers to the * argv array, and one with integer values that are lengths of the * strings, but also flags meaning that the string has to be massaged. */ PLAN * -c_execdir(argvp) +c_exec(option, argvp) + OPTION *option; char ***argvp; { PLAN *new; /* node returned */ register int cnt; register char **argv, **ap, *p; + /* XXX - was in c_execdir, but seems unnecessary!? ftsoptions &= ~FTS_NOSTAT; + */ isoutput = 1; - - new = palloc(N_EXECDIR, f_execdir); + + /* XXX - this is a change from the previous coding */ + new = palloc(option); for (ap = argv = *argvp;; ++ap) { if (!*ap) errx(1, - "-execdir: no terminating \";\""); + "%s: no terminating \";\"", option->name); if (**ap == ';') break; } @@ -526,7 +535,54 @@ c_execdir(argvp) new->e_argv[cnt] = new->e_orig[cnt] = NULL; *argvp = argv + 1; - return (new); + return new; +} + +int +f_flags(plan, entry) + PLAN *plan; + FTSENT *entry; +{ + u_long flags; + + flags = entry->fts_statp->st_flags & + (UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE | + SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND); + if (plan->flags & F_ATLEAST) + /* note that plan->fl_flags always is a subset of + plan->fl_mask */ + return (flags & plan->fl_mask) == plan->fl_flags; + else if (plan->flags & F_ANY) + return flags & plan->fl_mask; + else + return flags == plan->fl_flags; + /* NOTREACHED */ +} + +PLAN * +c_flags(option, argvp) + OPTION *option; + char ***argvp; +{ + char *flags_str; + PLAN *new; + u_long flags, notflags; + + flags_str = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + + if (*flags_str == '-') { + new->flags |= F_ATLEAST; + flags_str++; + } + if (strtofflags(&flags_str, &flags, ¬flags) == 1) + errx(1, "%s: %s: illegal flags string", option->name, flags_str); + + new->fl_flags = flags; + new->fl_mask = flags | notflags; + return new; } /* @@ -536,12 +592,14 @@ c_execdir(argvp) * basis. */ PLAN * -c_follow() +c_follow(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions &= ~FTS_PHYSICAL; ftsoptions |= FTS_LOGICAL; - return (palloc(N_FOLLOW, f_always_true)); + return palloc(option); } /* @@ -560,6 +618,9 @@ f_fstype(plan, entry) static int val_type, val_flags; char *p, save[2]; + if ((plan->flags & F_MTMASK) == F_MTUNKNOWN) + return 0; + /* Only check when we cross mount point. */ if (first || curdev != entry->fts_statp->st_dev) { curdev = entry->fts_statp->st_dev; @@ -578,7 +639,6 @@ f_fstype(plan, entry) p[0] = '.'; save[1] = p[1]; p[1] = '\0'; - } else p = NULL; @@ -599,71 +659,66 @@ f_fstype(plan, entry) val_flags = sb.f_flags; val_type = sb.f_type; } - switch (plan->flags) { + switch (plan->flags & F_MTMASK) { case F_MTFLAG: - return (val_flags & plan->mt_data) != 0; + return val_flags & plan->mt_data; case F_MTTYPE: - return (val_type == plan->mt_data); + return val_type == plan->mt_data; default: abort(); } } #if !defined(__NetBSD__) -int -f_always_false(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (0); -} - PLAN * -c_fstype(arg) - char *arg; +c_fstype(option, argvp) + OPTION *option; + char ***argvp; { + char *fsname; register PLAN *new; struct vfsconf vfc; - + + fsname = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_FSTYPE, f_fstype); + new = palloc(option); /* * Check first for a filesystem name. */ - if (getvfsbyname(arg, &vfc) == 0) { - new->flags = F_MTTYPE; + if (getvfsbyname(fsname, &vfc) == 0) { + new->flags |= F_MTTYPE; new->mt_data = vfc.vfc_typenum; - return (new); + return new; } - switch (*arg) { + switch (*fsname) { case 'l': - if (!strcmp(arg, "local")) { - new->flags = F_MTFLAG; + if (!strcmp(fsname, "local")) { + new->flags |= F_MTFLAG; new->mt_data = MNT_LOCAL; - return (new); + return new; } break; case 'r': - if (!strcmp(arg, "rdonly")) { - new->flags = F_MTFLAG; + if (!strcmp(fsname, "rdonly")) { + new->flags |= F_MTFLAG; new->mt_data = MNT_RDONLY; - return (new); + return new; } break; } + /* * We need to make filesystem checks for filesystems * that exists but aren't in the kernel work. */ - fprintf(stderr, "Warning: Unknown filesystem type %s\n", arg); - free(new); - - return (palloc(N_FSTYPE, f_always_false)); + fprintf(stderr, "Warning: Unknown filesystem type %s\n", fsname); + new->flags |= F_MTUNKNOWN; + return new; } -#endif +#endif /* __NetBSD__ */ /* * -group gname functions -- @@ -677,30 +732,33 @@ f_group(plan, entry) PLAN *plan; FTSENT *entry; { - return (entry->fts_statp->st_gid == plan->g_data); + return entry->fts_statp->st_gid == plan->g_data; } PLAN * -c_group(gname) - char *gname; +c_group(option, argvp) + OPTION *option; + char ***argvp; { + char *gname; PLAN *new; struct group *g; gid_t gid; + gname = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; g = getgrnam(gname); if (g == NULL) { gid = atoi(gname); if (gid == 0 && gname[0] != '0') - errx(1, "-group: %s: no such group", gname); + errx(1, "%s: %s: no such group", option->name, gname); } else gid = g->gr_gid; - new = palloc(N_GROUP, f_group); + new = palloc(option); new->g_data = gid; - return (new); + return new; } /* @@ -717,16 +775,19 @@ f_inum(plan, entry) } PLAN * -c_inum(arg) - char *arg; +c_inum(option, argvp) + OPTION *option; + char ***argvp; { + char *inum_str; PLAN *new; + inum_str = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_INUM, f_inum); - new->i_data = find_parsenum(new, "-inum", arg, NULL); - return (new); + new = palloc(option); + new->i_data = find_parsenum(new, option->name, inum_str, NULL); + return new; } /* @@ -743,16 +804,19 @@ f_links(plan, entry) } PLAN * -c_links(arg) - char *arg; +c_links(option, argvp) + OPTION *option; + char ***argvp; { + char *nlinks; PLAN *new; + nlinks = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_LINKS, f_links); - new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); - return (new); + new = palloc(option); + new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL); + return new; } /* @@ -766,143 +830,21 @@ f_ls(plan, entry) FTSENT *entry; { printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); - return (1); + return 1; } PLAN * -c_ls() +c_ls(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions &= ~FTS_NOSTAT; isoutput = 1; - return (palloc(N_LS, f_ls)); + return palloc(option); } /* - * -maxdepth n functions -- - * - * Does the same as -prune if the level of the current file is greater - * than the specified maximum depth. - * - * Note that -maxdepth and -mindepth are handled specially in - * find_execute() so their f_* functions here do nothing. - */ -int -f_maxdepth(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (1); -} - -PLAN * -c_maxdepth(arg) - char *arg; -{ - PLAN *new; - - if (*arg == '-') - /* all other errors handled by find_parsenum() */ - errx(1, "-maxdepth: %s: value must be positive", arg); - - new = palloc(N_MAXDEPTH, f_maxdepth); - maxdepth = find_parsenum(new, "-maxdepth", arg, NULL); - return (new); -} - -/* - * -mindepth n functions -- - * - * True if the current file is at or deeper than the specified minimum - * depth. - */ -int -f_mindepth(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (1); -} - -PLAN * -c_mindepth(arg) - char *arg; -{ - PLAN *new; - - if (*arg == '-') - /* all other errors handled by find_parsenum() */ - errx(1, "-maxdepth: %s: value must be positive", arg); - - new = palloc(N_MINDEPTH, f_mindepth); - mindepth = find_parsenum(new, "-mindepth", arg, NULL); - return (new); -} - -/* - * -mtime n functions -- - * - * True if the difference between the file modification time and the - * current time is n 24 hour periods. - */ -int -f_mtime(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - extern time_t now; - - COMPARE((now - entry->fts_statp->st_mtime + 86400 - 1) / - 86400, plan->t_data); -} - -PLAN * -c_mtime(arg) - char *arg; -{ - PLAN *new; - - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_MTIME, f_mtime); - new->t_data = find_parsenum(new, "-mtime", arg, NULL); - TIME_CORRECT(new, N_MTIME); - return (new); -} - -/* - * -mmin n functions -- - * - * True if the difference between the file modification time and the - * current time is n min periods. - */ -int -f_mmin(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - extern time_t now; - - COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) / - 60, plan->t_data); -} - -PLAN * -c_mmin(arg) - char *arg; -{ - PLAN *new; - - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_MMIN, f_mmin); - new->t_data = find_parsenum(new, "-mmin", arg, NULL); - TIME_CORRECT(new, N_MMIN); - return (new); -} - - -/* * -name functions -- * * True if the basename of the filename being examined @@ -913,145 +855,22 @@ f_name(plan, entry) PLAN *plan; FTSENT *entry; { - return (!fnmatch(plan->c_data, entry->fts_name, 0)); + return !fnmatch(plan->c_data, entry->fts_name, + plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); } PLAN * -c_name(pattern) - char *pattern; -{ - PLAN *new; - - new = palloc(N_NAME, f_name); - new->c_data = pattern; - return (new); -} - - -/* - * -iname functions -- - * - * Like -iname, but the match is case insensitive. - */ -int -f_iname(plan, entry) - PLAN *plan; - FTSENT *entry; +c_name(option, argvp) + OPTION *option; + char ***argvp; { - return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD)); -} - -PLAN * -c_iname(pattern) char *pattern; -{ PLAN *new; - new = palloc(N_INAME, f_iname); + pattern = nextarg(option, argvp); + new = palloc(option); new->c_data = pattern; - return (new); -} - - -/* - * -regex functions -- - * - * True if the whole path of the file matches pattern using - * regular expression. - */ -int -f_regex(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (do_f_regex(plan, entry, 0)); -} - -PLAN * -c_regex(pattern) - char *pattern; -{ - return (do_c_regex(pattern, 0)); -} - -/* - * -iregex functions -- - * - * Like -regex, but the match is case insensitive. - */ -int -f_iregex(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (do_f_regex(plan, entry, REG_ICASE)); -} - -PLAN * -c_iregex(pattern) - char *pattern; -{ - return (do_c_regex(pattern, REG_ICASE)); -} - -static int -do_f_regex(plan, entry, icase) - PLAN *plan; - FTSENT *entry; - int icase; -{ - char *str; - size_t len; - regex_t *pre; - regmatch_t pmatch; - int errcode; - char errbuf[LINE_MAX]; - int matched; - - pre = plan->re_data; - str = entry->fts_path; - len = strlen(str); - matched = 0; - - pmatch.rm_so = 0; - pmatch.rm_eo = len; - - errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); - - if (errcode != 0 && errcode != REG_NOMATCH) { - regerror(errcode, pre, errbuf, sizeof errbuf); - errx(1, "%s: %s", - icase == 0 ? "-regex" : "-iregex", errbuf); - } - - if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) - matched = 1; - - return (matched); -} - -PLAN * -do_c_regex(pattern, icase) - char *pattern; - int icase; -{ - PLAN *new; - regex_t *pre; - int errcode; - char errbuf[LINE_MAX]; - - if ((pre = malloc(sizeof(regex_t))) == NULL) - err(1, NULL); - - if ((errcode = regcomp(pre, pattern, regexp_flags | icase)) != 0) { - regerror(errcode, pre, errbuf, sizeof errbuf); - errx(1, "%s: %s: %s", - icase == 0 ? "-regex" : "-iregex", pattern, errbuf); - } - - new = icase == 0 ? palloc(N_REGEX, f_regex) : palloc(N_IREGEX, f_iregex); - new->re_data = pre; - return (new); + return new; } /* @@ -1066,23 +885,43 @@ f_newer(plan, entry) PLAN *plan; FTSENT *entry; { - return (entry->fts_statp->st_mtime > plan->t_data); + if (plan->flags & F_TIME_C) + return entry->fts_statp->st_ctime > plan->t_data; + else if (plan->flags & F_TIME_A) + return entry->fts_statp->st_atime > plan->t_data; + else + return entry->fts_statp->st_mtime > plan->t_data; } PLAN * -c_newer(filename) - char *filename; +c_newer(option, argvp) + OPTION *option; + char ***argvp; { + char *fn_or_tspec; PLAN *new; struct stat sb; + fn_or_tspec = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - if (stat(filename, &sb)) - err(1, "%s", filename); - new = palloc(N_NEWER, f_newer); - new->t_data = sb.st_mtime; - return (new); + new = palloc(option); + /* compare against what */ + if (option->flags & F_TIME2_T) { + new->t_data = get_date(fn_or_tspec, (struct timeb *) 0); + if (new->t_data == (time_t) -1) + errx(1, "Can't parse date/time: %s", fn_or_tspec); + } else { + if (stat(fn_or_tspec, &sb)) + err(1, "%s", fn_or_tspec); + if (option->flags & F_TIME2_C) + new->t_data = sb.st_ctime; + else if (option->flags & F_TIME2_A) + new->t_data = sb.st_atime; + else + new->t_data = sb.st_mtime; + } + return new; } /* @@ -1096,15 +935,17 @@ f_nogroup(plan, entry) PLAN *plan; FTSENT *entry; { - return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); + return group_from_gid(entry->fts_statp->st_gid, 1) == NULL; } PLAN * -c_nogroup() +c_nogroup(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions &= ~FTS_NOSTAT; - return (palloc(N_NOGROUP, f_nogroup)); + return palloc(option); } /* @@ -1118,15 +959,17 @@ f_nouser(plan, entry) PLAN *plan; FTSENT *entry; { - return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); + return user_from_uid(entry->fts_statp->st_uid, 1) == NULL; } PLAN * -c_nouser() +c_nouser(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions &= ~FTS_NOSTAT; - return (palloc(N_NOUSER, f_nouser)); + return palloc(option); } /* @@ -1140,43 +983,11 @@ f_path(plan, entry) PLAN *plan; FTSENT *entry; { - return (!fnmatch(plan->c_data, entry->fts_path, 0)); -} - -PLAN * -c_path(pattern) - char *pattern; -{ - PLAN *new; - - new = palloc(N_PATH, f_path); - new->c_data = pattern; - return (new); -} - -/* - * -ipath functions -- - * - * Like -path, but the match is case insensitive. - */ -int -f_ipath(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - return (!fnmatch(plan->c_data, entry->fts_path, FNM_CASEFOLD)); + return !fnmatch(plan->c_data, entry->fts_path, + plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); } -PLAN * -c_ipath(pattern) - char *pattern; -{ - PLAN *new; - - new = palloc(N_IPATH, f_ipath); - new->c_data = pattern; - return (new); -} +/* c_path is the same as c_name */ /* * -perm functions -- @@ -1194,90 +1005,40 @@ f_perm(plan, entry) mode = entry->fts_statp->st_mode & (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); - if (plan->flags == F_ATLEAST) - return ((plan->m_data | mode) == mode); - else if (plan->flags == F_ANY ) - return (plan->m_data & mode); + if (plan->flags & F_ATLEAST) + return (plan->m_data | mode) == mode; else - return (mode == plan->m_data); + return mode == plan->m_data; /* NOTREACHED */ } PLAN * -c_perm(perm) - char *perm; +c_perm(option, argvp) + OPTION *option; + char ***argvp; { + char *perm; PLAN *new; mode_t *set; + perm = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_PERM, f_perm); + new = palloc(option); if (*perm == '-') { - new->flags = F_ATLEAST; + new->flags |= F_ATLEAST; ++perm; } else if (*perm == '+') { - new->flags = F_ANY; + new->flags |= F_ANY; ++perm; } if ((set = setmode(perm)) == NULL) - errx(1, "-perm: %s: illegal mode string", perm); + errx(1, "%s: %s: illegal mode string", option->name, perm); new->m_data = getmode(set, 0); free(set); - return (new); -} - -/* - * -flags functions -- - * - * The flags argument is used to represent file flags bits. - */ -int -f_flags(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - u_long flags; - - flags = entry->fts_statp->st_flags & - (UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE | - SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND); - if (plan->flags == F_ATLEAST) - /* note that plan->fl_flags always is a subset of - plan->fl_mask */ - return (flags & plan->fl_mask) == plan->fl_flags; - else - return flags == plan->fl_flags; - /* NOTREACHED */ -} - -PLAN * -c_flags(flags_str) - char *flags_str; -{ - PLAN *new; - u_long flags, notflags; - - ftsoptions &= ~FTS_NOSTAT; - - new = palloc(N_FLAGS, f_flags); - - if (*flags_str == '-') { - new->flags = F_ATLEAST; - flags_str++; - } - if (strtofflags(&flags_str, &flags, ¬flags) == 1) - errx(1, "-flags: %s: illegal flags string", flags_str); - - new->fl_flags = flags; - new->fl_mask = flags | notflags; -#if 0 - printf("flags = %08x, mask = %08x (%08x, %08x)\n", - new->fl_flags, new->fl_mask, flags, notflags); -#endif return new; } @@ -1293,15 +1054,17 @@ f_print(plan, entry) FTSENT *entry; { (void)puts(entry->fts_path); - return (1); + return 1; } PLAN * -c_print() +c_print(option, argvp) + OPTION *option; + char ***argvp; { isoutput = 1; - return (palloc(N_PRINT, f_print)); + return palloc(option); } /* @@ -1317,16 +1080,10 @@ f_print0(plan, entry) { fputs(entry->fts_path, stdout); fputc('\0', stdout); - return (1); + return 1; } -PLAN * -c_print0() -{ - isoutput = 1; - - return (palloc(N_PRINT0, f_print0)); -} +/* c_print0 is the same as c_print */ /* * -prune functions -- @@ -1342,13 +1099,90 @@ f_prune(plan, entry) if (fts_set(tree, entry, FTS_SKIP)) err(1, "%s", entry->fts_path); - return (1); + return 1; } +/* c_prune == c_simple */ + +/* + * -regex functions -- + * + * True if the whole path of the file matches pattern using + * regular expression. + */ +int +f_regex(plan, entry) + PLAN *plan; + FTSENT *entry; +{ + char *str; + size_t len; + regex_t *pre; + regmatch_t pmatch; + int errcode; + char errbuf[LINE_MAX]; + int matched; + + pre = plan->re_data; + str = entry->fts_path; + len = strlen(str); + matched = 0; + + pmatch.rm_so = 0; + pmatch.rm_eo = len; + + errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); + + if (errcode != 0 && errcode != REG_NOMATCH) { + regerror(errcode, pre, errbuf, sizeof errbuf); + errx(1, "%s: %s", + plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf); + } + + if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) + matched = 1; + + return matched; +} + +PLAN * +c_regex(option, argvp) + OPTION *option; + char ***argvp; +{ + PLAN *new; + char *pattern; + regex_t *pre; + int errcode; + char errbuf[LINE_MAX]; + + if ((pre = malloc(sizeof(regex_t))) == NULL) + err(1, NULL); + + pattern = nextarg(option, argvp); + + if ((errcode = regcomp(pre, pattern, + regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) { + regerror(errcode, pre, errbuf, sizeof errbuf); + errx(1, "%s: %s: %s", + option->flags & F_IGNCASE ? "-iregex" : "-regex", + pattern, errbuf); + } + + new = palloc(option); + new->re_data = pre; + + return new; +} + +/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or */ + PLAN * -c_prune() +c_simple(option, argvp) + OPTION *option; + char ***argvp; { - return (palloc(N_PRUNE, f_prune)); + return palloc(option); } /* @@ -1374,20 +1208,23 @@ f_size(plan, entry) } PLAN * -c_size(arg) - char *arg; +c_size(option, argvp) + OPTION *option; + char ***argvp; { + char *size_str; PLAN *new; char endch; + size_str = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; - new = palloc(N_SIZE, f_size); + new = palloc(option); endch = 'c'; - new->o_data = find_parsenum(new, "-size", arg, &endch); + new->o_data = find_parsenum(new, option->name, size_str, &endch); if (endch == 'c') divsize = 0; - return (new); + return new; } /* @@ -1402,16 +1239,19 @@ f_type(plan, entry) PLAN *plan; FTSENT *entry; { - return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); + return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data; } PLAN * -c_type(typestring) - char *typestring; +c_type(option, argvp) + OPTION *option; + char ***argvp; { + char *typestring; PLAN *new; mode_t mask; + typestring = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; switch (typestring[0]) { @@ -1443,72 +1283,12 @@ c_type(typestring) break; #endif /* FTS_WHITEOUT */ default: - errx(1, "-type: %s: unknown type", typestring); + errx(1, "%s: %s: unknown type", option->name, typestring); } - new = palloc(N_TYPE, f_type); + new = palloc(option); new->m_data = mask; - return (new); -} - -/* - * -delete functions -- - * - * True always. Makes it's best shot and continues on regardless. - */ -int -f_delete(plan, entry) - PLAN *plan; - FTSENT *entry; -{ - /* ignore these from fts */ - if (strcmp(entry->fts_accpath, ".") == 0 || - strcmp(entry->fts_accpath, "..") == 0) - return (1); - - /* sanity check */ - if (isdepth == 0 || /* depth off */ - (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ - !(ftsoptions & FTS_PHYSICAL) || /* physical off */ - (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ - errx(1, "-delete: insecure options got turned on"); - - /* Potentially unsafe - do not accept relative paths whatsoever */ - if (strchr(entry->fts_accpath, '/') != NULL) - errx(1, "-delete: %s: relative path potentially not safe", - entry->fts_accpath); - - /* Turn off user immutable bits if running as root */ - if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && - !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && - geteuid() == 0) - chflags(entry->fts_accpath, - entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); - - /* rmdir directories, unlink everything else */ - if (S_ISDIR(entry->fts_statp->st_mode)) { - if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) - warn("-delete: rmdir(%s)", entry->fts_path); - } else { - if (unlink(entry->fts_accpath) < 0) - warn("-delete: unlink(%s)", entry->fts_path); - } - - /* "succeed" */ - return (1); -} - -PLAN * -c_delete() -{ - - ftsoptions &= ~FTS_NOSTAT; /* no optimise */ - ftsoptions |= FTS_PHYSICAL; /* disable -follow */ - ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ - isoutput = 1; /* possible output */ - isdepth = 1; /* -depth implied */ - - return (palloc(N_DELETE, f_delete)); + return new; } /* @@ -1523,30 +1303,33 @@ f_user(plan, entry) PLAN *plan; FTSENT *entry; { - return (entry->fts_statp->st_uid == plan->u_data); + return entry->fts_statp->st_uid == plan->u_data; } PLAN * -c_user(username) - char *username; +c_user(option, argvp) + OPTION *option; + char ***argvp; { + char *username; PLAN *new; struct passwd *p; uid_t uid; + username = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; p = getpwnam(username); if (p == NULL) { uid = atoi(username); if (uid == 0 && username[0] != '0') - errx(1, "-user: %s: no such user", username); + errx(1, "%s: %s: no such user", option->name, username); } else uid = p->pw_uid; - new = palloc(N_USER, f_user); + new = palloc(option); new->u_data = uid; - return (new); + return new; } /* @@ -1556,11 +1339,13 @@ c_user(username) * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) */ PLAN * -c_xdev() +c_xdev(option, argvp) + OPTION *option; + char ***argvp; { ftsoptions |= FTS_XDEV; - return (palloc(N_XDEV, f_always_true)); + return palloc(option); } /* @@ -1574,29 +1359,48 @@ f_expr(plan, entry) FTSENT *entry; { register PLAN *p; - register int state; + register int state = 0; - state = 0; for (p = plan->p_data[0]; - p && (state = (p->eval)(p, entry)); p = p->next); - return (state); + p && (state = (p->execute)(p, entry)); p = p->next); + return state; } /* - * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are + * f_openparen and f_closeparen nodes are temporary place markers. They are * eliminated during phase 2 of find_formplan() --- the '(' node is converted - * to a N_EXPR node containing the expression and the ')' node is discarded. + * to a f_expr node containing the expression and the ')' node is discarded. + * The functions themselves are only used as constants. */ -PLAN * -c_openparen() + +int +f_openparen(plan, entry) + PLAN *plan; + FTSENT *entry; +{ + abort(); +} + +int +f_closeparen(plan, entry) + PLAN *plan; + FTSENT *entry; { - return (palloc(N_OPENPAREN, (int (*)())-1)); + abort(); } +/* c_openparen == c_simple */ +/* c_closeparen == c_simple */ + +/* + * AND operator. Since AND is implicit, no node is allocated. + */ PLAN * -c_closeparen() +c_and(option, argvp) + OPTION *option; + char ***argvp; { - return (palloc(N_CLOSEPAREN, (int (*)())-1)); + return NULL; } /* @@ -1610,19 +1414,14 @@ f_not(plan, entry) FTSENT *entry; { register PLAN *p; - register int state; + register int state = 0; - state = 0; for (p = plan->p_data[0]; - p && (state = (p->eval)(p, entry)); p = p->next); - return (!state); + p && (state = (p->execute)(p, entry)); p = p->next); + return !state; } -PLAN * -c_not() -{ - return (palloc(N_NOT, f_not)); -} +/* c_not == c_simple */ /* * expression -o expression functions -- @@ -1636,38 +1435,17 @@ f_or(plan, entry) FTSENT *entry; { register PLAN *p; - register int state; + register int state = 0; - state = 0; for (p = plan->p_data[0]; - p && (state = (p->eval)(p, entry)); p = p->next); + p && (state = (p->execute)(p, entry)); p = p->next); if (state) - return (1); + return 1; for (p = plan->p_data[1]; - p && (state = (p->eval)(p, entry)); p = p->next); - return (state); + p && (state = (p->execute)(p, entry)); p = p->next); + return state; } -PLAN * -c_or() -{ - return (palloc(N_OR, f_or)); -} - -static PLAN * -palloc(t, f) - enum ntype t; - int (*f) __P((PLAN *, FTSENT *)); -{ - PLAN *new; - - if ((new = malloc(sizeof(PLAN))) == NULL) - err(1, NULL); - new->type = t; - new->eval = f; - new->flags = 0; - new->next = NULL; - return (new); -} +/* c_or == c_simple */ |
