140947Sbostic /*-
261995Sbostic * Copyright (c) 1990, 1993
361995Sbostic * The Regents of the University of California. All rights reserved.
440947Sbostic *
540947Sbostic * This code is derived from software contributed to Berkeley by
640947Sbostic * Cimarron D. Taylor of the University of California, Berkeley.
740947Sbostic *
840947Sbostic * %sccs.include.redist.c%
940947Sbostic */
1040947Sbostic
1140947Sbostic #ifndef lint
12*69264Smckusick static char sccsid[] = "@(#)function.c 8.10 (Berkeley) 05/04/95";
1340947Sbostic #endif /* not lint */
1440947Sbostic
1545868Sbostic #include <sys/param.h>
1651833Sbostic #include <sys/ucred.h>
1740947Sbostic #include <sys/stat.h>
1840947Sbostic #include <sys/wait.h>
1940947Sbostic #include <sys/mount.h>
2054558Sbostic
2159612Sbostic #include <err.h>
2247192Sbostic #include <errno.h>
2359612Sbostic #include <fnmatch.h>
2459612Sbostic #include <fts.h>
2540947Sbostic #include <grp.h>
2640947Sbostic #include <pwd.h>
2740947Sbostic #include <stdio.h>
2847192Sbostic #include <stdlib.h>
2942047Sbostic #include <string.h>
3059612Sbostic #include <tzfile.h>
3159612Sbostic #include <unistd.h>
3259612Sbostic
3340947Sbostic #include "find.h"
3440947Sbostic
3559612Sbostic #define COMPARE(a, b) { \
3659612Sbostic switch (plan->flags) { \
3759612Sbostic case F_EQUAL: \
3859612Sbostic return (a == b); \
3959612Sbostic case F_LESSTHAN: \
4059612Sbostic return (a < b); \
4159612Sbostic case F_GREATER: \
4259612Sbostic return (a > b); \
4359612Sbostic default: \
4459612Sbostic abort(); \
4559612Sbostic } \
4640947Sbostic }
4740947Sbostic
4859612Sbostic static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *))));
4940947Sbostic
5040947Sbostic /*
5140947Sbostic * find_parsenum --
5240947Sbostic * Parse a string of the form [+-]# and return the value.
5340947Sbostic */
5455710Sbostic static long
find_parsenum(plan,option,vp,endch)5559612Sbostic find_parsenum(plan, option, vp, endch)
5640947Sbostic PLAN *plan;
5759612Sbostic char *option, *vp, *endch;
5840947Sbostic {
5940947Sbostic long value;
6059613Sbostic char *endchar, *str; /* Pointer to character ending conversion. */
6140947Sbostic
6259613Sbostic /* Determine comparison from leading + or -. */
6359612Sbostic str = vp;
6459612Sbostic switch (*str) {
6540947Sbostic case '+':
6640947Sbostic ++str;
6750437Sbostic plan->flags = F_GREATER;
6840947Sbostic break;
6940947Sbostic case '-':
7040947Sbostic ++str;
7150437Sbostic plan->flags = F_LESSTHAN;
7240947Sbostic break;
7340947Sbostic default:
7450437Sbostic plan->flags = F_EQUAL;
7540947Sbostic break;
7640947Sbostic }
7740947Sbostic
7840947Sbostic /*
7955710Sbostic * Convert the string with strtol(). Note, if strtol() returns zero
8040947Sbostic * and endchar points to the beginning of the string we know we have
8140947Sbostic * a syntax error.
8240947Sbostic */
8340947Sbostic value = strtol(str, &endchar, 10);
8455710Sbostic if (value == 0 && endchar == str)
8559612Sbostic errx(1, "%s: %s: illegal numeric value", option, vp);
8655710Sbostic if (endchar[0] && (endch == NULL || endchar[0] != *endch))
8759612Sbostic errx(1, "%s: %s: illegal trailing character", option, vp);
8840947Sbostic if (endch)
8940947Sbostic *endch = endchar[0];
9059612Sbostic return (value);
9140947Sbostic }
9240947Sbostic
9340947Sbostic /*
9459613Sbostic * The value of n for the inode times (atime, ctime, and mtime) is a range,
9559613Sbostic * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with
9659613Sbostic * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
9759613Sbostic * user wanted. Correct so that -1 is "less than 1".
9859613Sbostic */
9959613Sbostic #define TIME_CORRECT(p, ttype) \
10059613Sbostic if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \
10159613Sbostic ++((p)->t_data);
10259613Sbostic
10359613Sbostic /*
10440947Sbostic * -atime n functions --
10540947Sbostic *
10640947Sbostic * True if the difference between the file access time and the
10740947Sbostic * current time is n 24 hour periods.
10840947Sbostic */
10959612Sbostic int
f_atime(plan,entry)11040947Sbostic f_atime(plan, entry)
11140947Sbostic PLAN *plan;
11240947Sbostic FTSENT *entry;
11340947Sbostic {
11440947Sbostic extern time_t now;
11540947Sbostic
11652239Sbostic COMPARE((now - entry->fts_statp->st_atime +
11742255Sbostic SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
11840947Sbostic }
11940947Sbostic
12040947Sbostic PLAN *
c_atime(arg)12140947Sbostic c_atime(arg)
12240947Sbostic char *arg;
12340947Sbostic {
12440947Sbostic PLAN *new;
12540947Sbostic
12640947Sbostic ftsoptions &= ~FTS_NOSTAT;
12740947Sbostic
12849868Sbostic new = palloc(N_ATIME, f_atime);
12958066Smarc new->t_data = find_parsenum(new, "-atime", arg, NULL);
13059613Sbostic TIME_CORRECT(new, N_ATIME);
13159612Sbostic return (new);
13240947Sbostic }
13340947Sbostic /*
13440947Sbostic * -ctime n functions --
13540947Sbostic *
13640947Sbostic * True if the difference between the last change of file
13740947Sbostic * status information and the current time is n 24 hour periods.
13840947Sbostic */
13959612Sbostic int
f_ctime(plan,entry)14040947Sbostic f_ctime(plan, entry)
14140947Sbostic PLAN *plan;
14240947Sbostic FTSENT *entry;
14340947Sbostic {
14440947Sbostic extern time_t now;
14540947Sbostic
14652239Sbostic COMPARE((now - entry->fts_statp->st_ctime +
14742255Sbostic SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
14840947Sbostic }
14940947Sbostic
15040947Sbostic PLAN *
c_ctime(arg)15140947Sbostic c_ctime(arg)
15240947Sbostic char *arg;
15340947Sbostic {
15440947Sbostic PLAN *new;
15540947Sbostic
15640947Sbostic ftsoptions &= ~FTS_NOSTAT;
15740947Sbostic
15849868Sbostic new = palloc(N_CTIME, f_ctime);
15958066Smarc new->t_data = find_parsenum(new, "-ctime", arg, NULL);
16059613Sbostic TIME_CORRECT(new, N_CTIME);
16159612Sbostic return (new);
16240947Sbostic }
16340947Sbostic
16440947Sbostic /*
16540947Sbostic * -depth functions --
16640947Sbostic *
16740947Sbostic * Always true, causes descent of the directory hierarchy to be done
16840947Sbostic * so that all entries in a directory are acted on before the directory
16940947Sbostic * itself.
17040947Sbostic */
17159612Sbostic int
f_always_true(plan,entry)17240947Sbostic f_always_true(plan, entry)
17340947Sbostic PLAN *plan;
17440947Sbostic FTSENT *entry;
17540947Sbostic {
17659612Sbostic return (1);
17740947Sbostic }
17840947Sbostic
17940947Sbostic PLAN *
c_depth()18040947Sbostic c_depth()
18140947Sbostic {
18245615Sbostic isdepth = 1;
18340947Sbostic
18459612Sbostic return (palloc(N_DEPTH, f_always_true));
18540947Sbostic }
18640947Sbostic
18740947Sbostic /*
18840947Sbostic * [-exec | -ok] utility [arg ... ] ; functions --
18940947Sbostic *
19040947Sbostic * True if the executed utility returns a zero value as exit status.
19140947Sbostic * The end of the primary expression is delimited by a semicolon. If
19240947Sbostic * "{}" occurs anywhere, it gets replaced by the current pathname.
19340947Sbostic * The current directory for the execution of utility is the same as
19440947Sbostic * the current directory when the find utility was started.
19540947Sbostic *
19640947Sbostic * The primary -ok is different in that it requests affirmation of the
19740947Sbostic * user before executing the utility.
19840947Sbostic */
19959612Sbostic int
f_exec(plan,entry)20040947Sbostic f_exec(plan, entry)
20140947Sbostic register PLAN *plan;
20240947Sbostic FTSENT *entry;
20340947Sbostic {
20449869Sbostic extern int dotfd;
20540947Sbostic register int cnt;
20647192Sbostic pid_t pid;
20747192Sbostic int status;
20840947Sbostic
20940947Sbostic for (cnt = 0; plan->e_argv[cnt]; ++cnt)
21040947Sbostic if (plan->e_len[cnt])
21145616Sbostic brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
21249869Sbostic entry->fts_path, plan->e_len[cnt]);
21340947Sbostic
21450437Sbostic if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
21559612Sbostic return (0);
21640947Sbostic
21759612Sbostic switch (pid = vfork()) {
21840947Sbostic case -1:
21959612Sbostic err(1, "fork");
22040947Sbostic /* NOTREACHED */
22140947Sbostic case 0:
22249869Sbostic if (fchdir(dotfd)) {
22359612Sbostic warn("chdir");
22449869Sbostic _exit(1);
22549869Sbostic }
22640947Sbostic execvp(plan->e_argv[0], plan->e_argv);
22759612Sbostic warn("%s", plan->e_argv[0]);
22849869Sbostic _exit(1);
22940947Sbostic }
23047192Sbostic pid = waitpid(pid, &status, 0);
23159612Sbostic return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
23240947Sbostic }
23340947Sbostic
23440947Sbostic /*
23540947Sbostic * c_exec --
23640947Sbostic * build three parallel arrays, one with pointers to the strings passed
23740947Sbostic * on the command line, one with (possibly duplicated) pointers to the
23840947Sbostic * argv array, and one with integer values that are lengths of the
23940947Sbostic * strings, but also flags meaning that the string has to be massaged.
24040947Sbostic */
24140947Sbostic PLAN *
c_exec(argvp,isok)24240947Sbostic c_exec(argvp, isok)
24340947Sbostic char ***argvp;
24440947Sbostic int isok;
24540947Sbostic {
24640947Sbostic PLAN *new; /* node returned */
24740947Sbostic register int cnt;
24840947Sbostic register char **argv, **ap, *p;
24940947Sbostic
25047192Sbostic isoutput = 1;
25140947Sbostic
25249868Sbostic new = palloc(N_EXEC, f_exec);
25350437Sbostic if (isok)
25450437Sbostic new->flags = F_NEEDOK;
25540947Sbostic
25640947Sbostic for (ap = argv = *argvp;; ++ap) {
25740947Sbostic if (!*ap)
25859612Sbostic errx(1,
25959612Sbostic "%s: no terminating \";\"", isok ? "-ok" : "-exec");
26040947Sbostic if (**ap == ';')
26140947Sbostic break;
26240947Sbostic }
26340947Sbostic
26440947Sbostic cnt = ap - *argvp + 1;
26540947Sbostic new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
26640947Sbostic new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
26745868Sbostic new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
26840947Sbostic
26940947Sbostic for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
27040947Sbostic new->e_orig[cnt] = *argv;
27140947Sbostic for (p = *argv; *p; ++p)
27240947Sbostic if (p[0] == '{' && p[1] == '}') {
27345868Sbostic new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
27445868Sbostic new->e_len[cnt] = MAXPATHLEN;
27540947Sbostic break;
27640947Sbostic }
27740947Sbostic if (!*p) {
27840947Sbostic new->e_argv[cnt] = *argv;
27940947Sbostic new->e_len[cnt] = 0;
28040947Sbostic }
28140947Sbostic }
28240947Sbostic new->e_argv[cnt] = new->e_orig[cnt] = NULL;
28340947Sbostic
28440947Sbostic *argvp = argv + 1;
28559612Sbostic return (new);
28640947Sbostic }
28740947Sbostic
28840947Sbostic /*
28940947Sbostic * -follow functions --
29040947Sbostic *
29140947Sbostic * Always true, causes symbolic links to be followed on a global
29240947Sbostic * basis.
29340947Sbostic */
29440947Sbostic PLAN *
c_follow()29540947Sbostic c_follow()
29640947Sbostic {
29740947Sbostic ftsoptions &= ~FTS_PHYSICAL;
29840947Sbostic ftsoptions |= FTS_LOGICAL;
29940947Sbostic
30059612Sbostic return (palloc(N_FOLLOW, f_always_true));
30140947Sbostic }
30240947Sbostic
30340947Sbostic /*
30440947Sbostic * -fstype functions --
30540947Sbostic *
30640947Sbostic * True if the file is of a certain type.
30740947Sbostic */
30859612Sbostic int
f_fstype(plan,entry)30940947Sbostic f_fstype(plan, entry)
31040947Sbostic PLAN *plan;
31140947Sbostic FTSENT *entry;
31240947Sbostic {
31342275Sbostic static dev_t curdev; /* need a guaranteed illegal dev value */
31442275Sbostic static int first = 1;
31540947Sbostic struct statfs sb;
31642255Sbostic static short val;
31745626Sbostic char *p, save[2];
31840947Sbostic
31950437Sbostic /* Only check when we cross mount point. */
32052239Sbostic if (first || curdev != entry->fts_statp->st_dev) {
32152239Sbostic curdev = entry->fts_statp->st_dev;
32245626Sbostic
32345626Sbostic /*
32445626Sbostic * Statfs follows symlinks; find wants the link's file system,
32545626Sbostic * not where it points.
32645626Sbostic */
32745626Sbostic if (entry->fts_info == FTS_SL ||
32845626Sbostic entry->fts_info == FTS_SLNONE) {
32966584Sbostic if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
33045626Sbostic ++p;
33145626Sbostic else
33245626Sbostic p = entry->fts_accpath;
33345626Sbostic save[0] = p[0];
33445626Sbostic p[0] = '.';
33545626Sbostic save[1] = p[1];
33645626Sbostic p[1] = '\0';
33745626Sbostic
33845626Sbostic } else
33945626Sbostic p = NULL;
34045626Sbostic
34149868Sbostic if (statfs(entry->fts_accpath, &sb))
34259612Sbostic err(1, "%s", entry->fts_accpath);
34345626Sbostic
34445626Sbostic if (p) {
34545626Sbostic p[0] = save[0];
34645626Sbostic p[1] = save[1];
34745626Sbostic }
34845626Sbostic
34942275Sbostic first = 0;
35069051Sbostic
35169051Sbostic /*
35269051Sbostic * Further tests may need both of these values, so
35369051Sbostic * always copy both of them.
35469051Sbostic */
35569051Sbostic val = sb.f_flags;
35669051Sbostic val = sb.f_type;
35740947Sbostic }
35869051Sbostic switch (plan->flags) {
35950437Sbostic case F_MTFLAG:
36059612Sbostic return (val & plan->mt_data);
36150437Sbostic case F_MTTYPE:
36259612Sbostic return (val == plan->mt_data);
36359612Sbostic default:
36459612Sbostic abort();
36550437Sbostic }
36640947Sbostic }
36740947Sbostic
36840947Sbostic PLAN *
c_fstype(arg)36940947Sbostic c_fstype(arg)
37040947Sbostic char *arg;
37140947Sbostic {
37240947Sbostic register PLAN *new;
373*69264Smckusick struct vfsconf vfc;
37440947Sbostic
37540947Sbostic ftsoptions &= ~FTS_NOSTAT;
37640947Sbostic
37749868Sbostic new = palloc(N_FSTYPE, f_fstype);
378*69264Smckusick
379*69264Smckusick /*
380*69264Smckusick * Check first for a filesystem name.
381*69264Smckusick */
382*69264Smckusick if (getvfsbyname(arg, &vfc) == 0) {
383*69264Smckusick new->flags = F_MTTYPE;
384*69264Smckusick new->mt_data = vfc.vfc_typenum;
385*69264Smckusick return (new);
386*69264Smckusick }
387*69264Smckusick
38859612Sbostic switch (*arg) {
38942255Sbostic case 'l':
39042255Sbostic if (!strcmp(arg, "local")) {
39150437Sbostic new->flags = F_MTFLAG;
39250437Sbostic new->mt_data = MNT_LOCAL;
39359612Sbostic return (new);
39442255Sbostic }
39542255Sbostic break;
39650437Sbostic case 'r':
39750437Sbostic if (!strcmp(arg, "rdonly")) {
39850437Sbostic new->flags = F_MTFLAG;
39950437Sbostic new->mt_data = MNT_RDONLY;
40059612Sbostic return (new);
40150437Sbostic }
40250437Sbostic break;
40340947Sbostic }
40459612Sbostic errx(1, "%s: unknown file type", arg);
40540947Sbostic /* NOTREACHED */
40640947Sbostic }
40740947Sbostic
40840947Sbostic /*
40940947Sbostic * -group gname functions --
41040947Sbostic *
41140947Sbostic * True if the file belongs to the group gname. If gname is numeric and
41240947Sbostic * an equivalent of the getgrnam() function does not return a valid group
41340947Sbostic * name, gname is taken as a group ID.
41440947Sbostic */
41559612Sbostic int
f_group(plan,entry)41640947Sbostic f_group(plan, entry)
41740947Sbostic PLAN *plan;
41840947Sbostic FTSENT *entry;
41940947Sbostic {
42059612Sbostic return (entry->fts_statp->st_gid == plan->g_data);
42140947Sbostic }
42240947Sbostic
42340947Sbostic PLAN *
c_group(gname)42440947Sbostic c_group(gname)
42540947Sbostic char *gname;
42640947Sbostic {
42740947Sbostic PLAN *new;
42840947Sbostic struct group *g;
42940947Sbostic gid_t gid;
43040947Sbostic
43140947Sbostic ftsoptions &= ~FTS_NOSTAT;
43240947Sbostic
43340947Sbostic g = getgrnam(gname);
43440947Sbostic if (g == NULL) {
43540947Sbostic gid = atoi(gname);
43640947Sbostic if (gid == 0 && gname[0] != '0')
43759612Sbostic errx(1, "-group: %s: no such group", gname);
43840947Sbostic } else
43940947Sbostic gid = g->gr_gid;
44040947Sbostic
44149868Sbostic new = palloc(N_GROUP, f_group);
44240947Sbostic new->g_data = gid;
44359612Sbostic return (new);
44440947Sbostic }
44540947Sbostic
44640947Sbostic /*
44740947Sbostic * -inum n functions --
44840947Sbostic *
44940947Sbostic * True if the file has inode # n.
45040947Sbostic */
45159612Sbostic int
f_inum(plan,entry)45240947Sbostic f_inum(plan, entry)
45340947Sbostic PLAN *plan;
45440947Sbostic FTSENT *entry;
45540947Sbostic {
45652239Sbostic COMPARE(entry->fts_statp->st_ino, plan->i_data);
45740947Sbostic }
45840947Sbostic
45940947Sbostic PLAN *
c_inum(arg)46040947Sbostic c_inum(arg)
46140947Sbostic char *arg;
46240947Sbostic {
46340947Sbostic PLAN *new;
46440947Sbostic
46540947Sbostic ftsoptions &= ~FTS_NOSTAT;
46640947Sbostic
46749868Sbostic new = palloc(N_INUM, f_inum);
46858066Smarc new->i_data = find_parsenum(new, "-inum", arg, NULL);
46959612Sbostic return (new);
47040947Sbostic }
47140947Sbostic
47240947Sbostic /*
47340947Sbostic * -links n functions --
47440947Sbostic *
47540947Sbostic * True if the file has n links.
47640947Sbostic */
47759612Sbostic int
f_links(plan,entry)47840947Sbostic f_links(plan, entry)
47940947Sbostic PLAN *plan;
48040947Sbostic FTSENT *entry;
48140947Sbostic {
48252239Sbostic COMPARE(entry->fts_statp->st_nlink, plan->l_data);
48340947Sbostic }
48440947Sbostic
48540947Sbostic PLAN *
c_links(arg)48640947Sbostic c_links(arg)
48740947Sbostic char *arg;
48840947Sbostic {
48940947Sbostic PLAN *new;
49040947Sbostic
49140947Sbostic ftsoptions &= ~FTS_NOSTAT;
49240947Sbostic
49349868Sbostic new = palloc(N_LINKS, f_links);
49458066Smarc new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
49559612Sbostic return (new);
49640947Sbostic }
49740947Sbostic
49840947Sbostic /*
49940947Sbostic * -ls functions --
50040947Sbostic *
50140947Sbostic * Always true - prints the current entry to stdout in "ls" format.
50240947Sbostic */
50359612Sbostic int
f_ls(plan,entry)50440947Sbostic f_ls(plan, entry)
50540947Sbostic PLAN *plan;
50640947Sbostic FTSENT *entry;
50740947Sbostic {
50852239Sbostic printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
50959612Sbostic return (1);
51040947Sbostic }
51140947Sbostic
51240947Sbostic PLAN *
c_ls()51340947Sbostic c_ls()
51440947Sbostic {
51540947Sbostic ftsoptions &= ~FTS_NOSTAT;
51645615Sbostic isoutput = 1;
51740947Sbostic
51859612Sbostic return (palloc(N_LS, f_ls));
51940947Sbostic }
52040947Sbostic
52140947Sbostic /*
52250437Sbostic * -mtime n functions --
52350437Sbostic *
52450437Sbostic * True if the difference between the file modification time and the
52550437Sbostic * current time is n 24 hour periods.
52650437Sbostic */
52759612Sbostic int
f_mtime(plan,entry)52850437Sbostic f_mtime(plan, entry)
52950437Sbostic PLAN *plan;
53050437Sbostic FTSENT *entry;
53150437Sbostic {
53250437Sbostic extern time_t now;
53350437Sbostic
53452239Sbostic COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
53550437Sbostic SECSPERDAY, plan->t_data);
53650437Sbostic }
53750437Sbostic
53850437Sbostic PLAN *
c_mtime(arg)53950437Sbostic c_mtime(arg)
54050437Sbostic char *arg;
54150437Sbostic {
54250437Sbostic PLAN *new;
54350437Sbostic
54450437Sbostic ftsoptions &= ~FTS_NOSTAT;
54550437Sbostic
54650437Sbostic new = palloc(N_MTIME, f_mtime);
54758066Smarc new->t_data = find_parsenum(new, "-mtime", arg, NULL);
54859613Sbostic TIME_CORRECT(new, N_MTIME);
54959612Sbostic return (new);
55050437Sbostic }
55150437Sbostic
55250437Sbostic /*
55340947Sbostic * -name functions --
55440947Sbostic *
55540947Sbostic * True if the basename of the filename being examined
55640947Sbostic * matches pattern using Pattern Matching Notation S3.14
55740947Sbostic */
55859612Sbostic int
f_name(plan,entry)55940947Sbostic f_name(plan, entry)
56040947Sbostic PLAN *plan;
56140947Sbostic FTSENT *entry;
56240947Sbostic {
56359612Sbostic return (!fnmatch(plan->c_data, entry->fts_name, 0));
56440947Sbostic }
56540947Sbostic
56640947Sbostic PLAN *
c_name(pattern)56740947Sbostic c_name(pattern)
56840947Sbostic char *pattern;
56940947Sbostic {
57040947Sbostic PLAN *new;
57140947Sbostic
57249868Sbostic new = palloc(N_NAME, f_name);
57340947Sbostic new->c_data = pattern;
57459612Sbostic return (new);
57540947Sbostic }
57640947Sbostic
57740947Sbostic /*
57840947Sbostic * -newer file functions --
57940947Sbostic *
58040947Sbostic * True if the current file has been modified more recently
58140947Sbostic * then the modification time of the file named by the pathname
58240947Sbostic * file.
58340947Sbostic */
58459612Sbostic int
f_newer(plan,entry)58540947Sbostic f_newer(plan, entry)
58640947Sbostic PLAN *plan;
58740947Sbostic FTSENT *entry;
58840947Sbostic {
58959612Sbostic return (entry->fts_statp->st_mtime > plan->t_data);
59040947Sbostic }
59140947Sbostic
59240947Sbostic PLAN *
c_newer(filename)59340947Sbostic c_newer(filename)
59440947Sbostic char *filename;
59540947Sbostic {
59640947Sbostic PLAN *new;
59740947Sbostic struct stat sb;
59840947Sbostic
59940947Sbostic ftsoptions &= ~FTS_NOSTAT;
60040947Sbostic
60149868Sbostic if (stat(filename, &sb))
60259612Sbostic err(1, "%s", filename);
60349868Sbostic new = palloc(N_NEWER, f_newer);
60440947Sbostic new->t_data = sb.st_mtime;
60559612Sbostic return (new);
60640947Sbostic }
60740947Sbostic
60840947Sbostic /*
60940947Sbostic * -nogroup functions --
61040947Sbostic *
61140947Sbostic * True if file belongs to a user ID for which the equivalent
61240947Sbostic * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
61340947Sbostic */
61459612Sbostic int
f_nogroup(plan,entry)61540947Sbostic f_nogroup(plan, entry)
61640947Sbostic PLAN *plan;
61740947Sbostic FTSENT *entry;
61840947Sbostic {
61945615Sbostic char *group_from_gid();
62045615Sbostic
62166220Sbostic return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
62240947Sbostic }
62340947Sbostic
62440947Sbostic PLAN *
c_nogroup()62540947Sbostic c_nogroup()
62640947Sbostic {
62740947Sbostic ftsoptions &= ~FTS_NOSTAT;
62840947Sbostic
62959612Sbostic return (palloc(N_NOGROUP, f_nogroup));
63040947Sbostic }
63140947Sbostic
63240947Sbostic /*
63340947Sbostic * -nouser functions --
63440947Sbostic *
63540947Sbostic * True if file belongs to a user ID for which the equivalent
63640947Sbostic * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
63740947Sbostic */
63859612Sbostic int
f_nouser(plan,entry)63940947Sbostic f_nouser(plan, entry)
64040947Sbostic PLAN *plan;
64140947Sbostic FTSENT *entry;
64240947Sbostic {
64345615Sbostic char *user_from_uid();
64445615Sbostic
64566220Sbostic return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
64640947Sbostic }
64740947Sbostic
64840947Sbostic PLAN *
c_nouser()64940947Sbostic c_nouser()
65040947Sbostic {
65140947Sbostic ftsoptions &= ~FTS_NOSTAT;
65240947Sbostic
65359612Sbostic return (palloc(N_NOUSER, f_nouser));
65440947Sbostic }
65540947Sbostic
65640947Sbostic /*
65750437Sbostic * -path functions --
65850437Sbostic *
65950437Sbostic * True if the path of the filename being examined
66050437Sbostic * matches pattern using Pattern Matching Notation S3.14
66150437Sbostic */
66259612Sbostic int
f_path(plan,entry)66350437Sbostic f_path(plan, entry)
66450437Sbostic PLAN *plan;
66550437Sbostic FTSENT *entry;
66650437Sbostic {
66759612Sbostic return (!fnmatch(plan->c_data, entry->fts_path, 0));
66850437Sbostic }
66950437Sbostic
67050437Sbostic PLAN *
c_path(pattern)67150437Sbostic c_path(pattern)
67250437Sbostic char *pattern;
67350437Sbostic {
67450437Sbostic PLAN *new;
67550437Sbostic
67650437Sbostic new = palloc(N_NAME, f_path);
67750437Sbostic new->c_data = pattern;
67859612Sbostic return (new);
67950437Sbostic }
68050437Sbostic
68150437Sbostic /*
68240947Sbostic * -perm functions --
68340947Sbostic *
68440947Sbostic * The mode argument is used to represent file mode bits. If it starts
68540947Sbostic * with a leading digit, it's treated as an octal mode, otherwise as a
68640947Sbostic * symbolic mode.
68740947Sbostic */
68859612Sbostic int
f_perm(plan,entry)68940947Sbostic f_perm(plan, entry)
69040947Sbostic PLAN *plan;
69140947Sbostic FTSENT *entry;
69240947Sbostic {
69340947Sbostic mode_t mode;
69440947Sbostic
69552239Sbostic mode = entry->fts_statp->st_mode &
69640947Sbostic (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
69750437Sbostic if (plan->flags == F_ATLEAST)
69859612Sbostic return ((plan->m_data | mode) == mode);
69940947Sbostic else
70059612Sbostic return (mode == plan->m_data);
70140947Sbostic /* NOTREACHED */
70240947Sbostic }
70340947Sbostic
70440947Sbostic PLAN *
c_perm(perm)70540947Sbostic c_perm(perm)
70640947Sbostic char *perm;
70740947Sbostic {
70840947Sbostic PLAN *new;
70947192Sbostic mode_t *set;
71040947Sbostic
71140947Sbostic ftsoptions &= ~FTS_NOSTAT;
71240947Sbostic
71349868Sbostic new = palloc(N_PERM, f_perm);
71440947Sbostic
71540947Sbostic if (*perm == '-') {
71650437Sbostic new->flags = F_ATLEAST;
71740947Sbostic ++perm;
71840947Sbostic }
71940947Sbostic
72044764Strent if ((set = setmode(perm)) == NULL)
72159612Sbostic err(1, "-perm: %s: illegal mode string", perm);
72240947Sbostic
72344764Strent new->m_data = getmode(set, 0);
72459612Sbostic return (new);
72540947Sbostic }
72640947Sbostic
72740947Sbostic /*
72840947Sbostic * -print functions --
72940947Sbostic *
73040947Sbostic * Always true, causes the current pathame to be written to
73140947Sbostic * standard output.
73240947Sbostic */
73359612Sbostic int
f_print(plan,entry)73440947Sbostic f_print(plan, entry)
73540947Sbostic PLAN *plan;
73640947Sbostic FTSENT *entry;
73740947Sbostic {
73842255Sbostic (void)printf("%s\n", entry->fts_path);
73959612Sbostic return (1);
74040947Sbostic }
74140947Sbostic
74240947Sbostic PLAN *
c_print()74340947Sbostic c_print()
74440947Sbostic {
74545615Sbostic isoutput = 1;
74640947Sbostic
74759612Sbostic return (palloc(N_PRINT, f_print));
74840947Sbostic }
74940947Sbostic
75040947Sbostic /*
75140947Sbostic * -prune functions --
75240947Sbostic *
75340947Sbostic * Prune a portion of the hierarchy.
75440947Sbostic */
75559612Sbostic int
f_prune(plan,entry)75640947Sbostic f_prune(plan, entry)
75740947Sbostic PLAN *plan;
75840947Sbostic FTSENT *entry;
75940947Sbostic {
76040947Sbostic extern FTS *tree;
76140947Sbostic
76249868Sbostic if (fts_set(tree, entry, FTS_SKIP))
76359612Sbostic err(1, "%s", entry->fts_path);
76459612Sbostic return (1);
76540947Sbostic }
76640947Sbostic
76740947Sbostic PLAN *
c_prune()76840947Sbostic c_prune()
76940947Sbostic {
77059612Sbostic return (palloc(N_PRUNE, f_prune));
77140947Sbostic }
77240947Sbostic
77340947Sbostic /*
77440947Sbostic * -size n[c] functions --
77540947Sbostic *
77640947Sbostic * True if the file size in bytes, divided by an implementation defined
77740947Sbostic * value and rounded up to the next integer, is n. If n is followed by
77840947Sbostic * a c, the size is in bytes.
77940947Sbostic */
78040947Sbostic #define FIND_SIZE 512
78140947Sbostic static int divsize = 1;
78240947Sbostic
78359612Sbostic int
f_size(plan,entry)78440947Sbostic f_size(plan, entry)
78540947Sbostic PLAN *plan;
78640947Sbostic FTSENT *entry;
78740947Sbostic {
78840947Sbostic off_t size;
78940947Sbostic
79052239Sbostic size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
79152239Sbostic FIND_SIZE : entry->fts_statp->st_size;
79240947Sbostic COMPARE(size, plan->o_data);
79340947Sbostic }
79440947Sbostic
79540947Sbostic PLAN *
c_size(arg)79640947Sbostic c_size(arg)
79740947Sbostic char *arg;
79840947Sbostic {
79940947Sbostic PLAN *new;
80040947Sbostic char endch;
80140947Sbostic
80240947Sbostic ftsoptions &= ~FTS_NOSTAT;
80340947Sbostic
80449868Sbostic new = palloc(N_SIZE, f_size);
80555710Sbostic endch = 'c';
80658066Smarc new->o_data = find_parsenum(new, "-size", arg, &endch);
80740947Sbostic if (endch == 'c')
80840947Sbostic divsize = 0;
80959612Sbostic return (new);
81040947Sbostic }
81140947Sbostic
81240947Sbostic /*
81340947Sbostic * -type c functions --
81440947Sbostic *
81567575Spendry * True if the type of the file is c, where c is b, c, d, p, f or w
81667575Spendry * for block special file, character special file, directory, FIFO,
81767575Spendry * regular file or whiteout respectively.
81840947Sbostic */
81959612Sbostic int
f_type(plan,entry)82040947Sbostic f_type(plan, entry)
82140947Sbostic PLAN *plan;
82240947Sbostic FTSENT *entry;
82340947Sbostic {
82459612Sbostic return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
82540947Sbostic }
82640947Sbostic
82740947Sbostic PLAN *
c_type(typestring)82840947Sbostic c_type(typestring)
82940947Sbostic char *typestring;
83040947Sbostic {
83140947Sbostic PLAN *new;
83240947Sbostic mode_t mask;
83340947Sbostic
83440947Sbostic ftsoptions &= ~FTS_NOSTAT;
83540947Sbostic
83640947Sbostic switch (typestring[0]) {
83740947Sbostic case 'b':
83840947Sbostic mask = S_IFBLK;
83940947Sbostic break;
84040947Sbostic case 'c':
84140947Sbostic mask = S_IFCHR;
84240947Sbostic break;
84340947Sbostic case 'd':
84440947Sbostic mask = S_IFDIR;
84540947Sbostic break;
84640947Sbostic case 'f':
84740947Sbostic mask = S_IFREG;
84840947Sbostic break;
84940947Sbostic case 'l':
85040947Sbostic mask = S_IFLNK;
85140947Sbostic break;
85240947Sbostic case 'p':
85340947Sbostic mask = S_IFIFO;
85440947Sbostic break;
85540947Sbostic case 's':
85640947Sbostic mask = S_IFSOCK;
85740947Sbostic break;
85867593Smckusick #ifdef FTS_WHITEOUT
85967575Spendry case 'w':
86067575Spendry mask = S_IFWHT;
86167575Spendry ftsoptions |= FTS_WHITEOUT;
86267575Spendry break;
86367593Smckusick #endif /* FTS_WHITEOUT */
86440947Sbostic default:
86559612Sbostic errx(1, "-type: %s: unknown type", typestring);
86640947Sbostic }
86740947Sbostic
86849868Sbostic new = palloc(N_TYPE, f_type);
86940947Sbostic new->m_data = mask;
87059612Sbostic return (new);
87140947Sbostic }
87240947Sbostic
87340947Sbostic /*
87440947Sbostic * -user uname functions --
87540947Sbostic *
87640947Sbostic * True if the file belongs to the user uname. If uname is numeric and
87740947Sbostic * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
87840947Sbostic * return a valid user name, uname is taken as a user ID.
87940947Sbostic */
88059612Sbostic int
f_user(plan,entry)88140947Sbostic f_user(plan, entry)
88240947Sbostic PLAN *plan;
88340947Sbostic FTSENT *entry;
88440947Sbostic {
88559612Sbostic return (entry->fts_statp->st_uid == plan->u_data);
88640947Sbostic }
88740947Sbostic
88840947Sbostic PLAN *
c_user(username)88940947Sbostic c_user(username)
89040947Sbostic char *username;
89140947Sbostic {
89240947Sbostic PLAN *new;
89340947Sbostic struct passwd *p;
89440947Sbostic uid_t uid;
89540947Sbostic
89640947Sbostic ftsoptions &= ~FTS_NOSTAT;
89740947Sbostic
89840947Sbostic p = getpwnam(username);
89940947Sbostic if (p == NULL) {
90040947Sbostic uid = atoi(username);
90140947Sbostic if (uid == 0 && username[0] != '0')
90259612Sbostic errx(1, "-user: %s: no such user", username);
90340947Sbostic } else
90440947Sbostic uid = p->pw_uid;
90540947Sbostic
90649868Sbostic new = palloc(N_USER, f_user);
90740947Sbostic new->u_data = uid;
90859612Sbostic return (new);
90940947Sbostic }
91040947Sbostic
91140947Sbostic /*
91240947Sbostic * -xdev functions --
91340947Sbostic *
91440947Sbostic * Always true, causes find not to decend past directories that have a
91540947Sbostic * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
91640947Sbostic */
91740947Sbostic PLAN *
c_xdev()91840947Sbostic c_xdev()
91940947Sbostic {
92042275Sbostic ftsoptions |= FTS_XDEV;
92140947Sbostic
92259612Sbostic return (palloc(N_XDEV, f_always_true));
92340947Sbostic }
92440947Sbostic
92540947Sbostic /*
92640947Sbostic * ( expression ) functions --
92740947Sbostic *
92840947Sbostic * True if expression is true.
92940947Sbostic */
93059612Sbostic int
f_expr(plan,entry)93140947Sbostic f_expr(plan, entry)
93240947Sbostic PLAN *plan;
93340947Sbostic FTSENT *entry;
93440947Sbostic {
93540947Sbostic register PLAN *p;
93640947Sbostic register int state;
93740947Sbostic
93840947Sbostic for (p = plan->p_data[0];
93940947Sbostic p && (state = (p->eval)(p, entry)); p = p->next);
94059612Sbostic return (state);
94140947Sbostic }
94240947Sbostic
94340947Sbostic /*
94449864Sbostic * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
94540947Sbostic * eliminated during phase 2 of find_formplan() --- the '(' node is converted
94649864Sbostic * to a N_EXPR node containing the expression and the ')' node is discarded.
94740947Sbostic */
94840947Sbostic PLAN *
c_openparen()94940947Sbostic c_openparen()
95040947Sbostic {
95159612Sbostic return (palloc(N_OPENPAREN, (int (*)())-1));
95240947Sbostic }
95340947Sbostic
95440947Sbostic PLAN *
c_closeparen()95540947Sbostic c_closeparen()
95640947Sbostic {
95759612Sbostic return (palloc(N_CLOSEPAREN, (int (*)())-1));
95840947Sbostic }
95940947Sbostic
96040947Sbostic /*
96140947Sbostic * ! expression functions --
96240947Sbostic *
96340947Sbostic * Negation of a primary; the unary NOT operator.
96440947Sbostic */
96559612Sbostic int
f_not(plan,entry)96640947Sbostic f_not(plan, entry)
96740947Sbostic PLAN *plan;
96840947Sbostic FTSENT *entry;
96940947Sbostic {
97040947Sbostic register PLAN *p;
97140947Sbostic register int state;
97240947Sbostic
97340947Sbostic for (p = plan->p_data[0];
97440947Sbostic p && (state = (p->eval)(p, entry)); p = p->next);
97559612Sbostic return (!state);
97640947Sbostic }
97740947Sbostic
97840947Sbostic PLAN *
c_not()97940947Sbostic c_not()
98040947Sbostic {
98159612Sbostic return (palloc(N_NOT, f_not));
98240947Sbostic }
98340947Sbostic
98440947Sbostic /*
98540947Sbostic * expression -o expression functions --
98640947Sbostic *
98740947Sbostic * Alternation of primaries; the OR operator. The second expression is
98840947Sbostic * not evaluated if the first expression is true.
98940947Sbostic */
99059612Sbostic int
f_or(plan,entry)99140947Sbostic f_or(plan, entry)
99240947Sbostic PLAN *plan;
99340947Sbostic FTSENT *entry;
99440947Sbostic {
99540947Sbostic register PLAN *p;
99640947Sbostic register int state;
99740947Sbostic
99840947Sbostic for (p = plan->p_data[0];
99940947Sbostic p && (state = (p->eval)(p, entry)); p = p->next);
100040947Sbostic
100140947Sbostic if (state)
100259612Sbostic return (1);
100340947Sbostic
100440947Sbostic for (p = plan->p_data[1];
100540947Sbostic p && (state = (p->eval)(p, entry)); p = p->next);
100659612Sbostic return (state);
100740947Sbostic }
100840947Sbostic
100940947Sbostic PLAN *
c_or()101040947Sbostic c_or()
101140947Sbostic {
101259612Sbostic return (palloc(N_OR, f_or));
101349868Sbostic }
101449868Sbostic
101549868Sbostic static PLAN *
palloc(t,f)101649868Sbostic palloc(t, f)
101749868Sbostic enum ntype t;
101859612Sbostic int (*f) __P((PLAN *, FTSENT *));
101949868Sbostic {
102040947Sbostic PLAN *new;
102140947Sbostic
102266585Sbostic if ((new = malloc(sizeof(PLAN))) == NULL)
102366585Sbostic err(1, NULL);
102466585Sbostic new->type = t;
102566585Sbostic new->eval = f;
102666585Sbostic new->flags = 0;
102766585Sbostic new->next = NULL;
102866585Sbostic return (new);
102940947Sbostic }
1030