xref: /csrg-svn/usr.bin/find/function.c (revision 59612)
140947Sbostic /*-
240947Sbostic  * Copyright (c) 1990 The Regents of the University of California.
340947Sbostic  * 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*59612Sbostic static char sccsid[] = "@(#)function.c	5.25 (Berkeley) 05/01/93";
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 
21*59612Sbostic #include <err.h>
2247192Sbostic #include <errno.h>
23*59612Sbostic #include <fnmatch.h>
24*59612Sbostic #include <fts.h>
2540947Sbostic #include <grp.h>
2640947Sbostic #include <pwd.h>
2740947Sbostic #include <stdio.h>
2847192Sbostic #include <stdlib.h>
2942047Sbostic #include <string.h>
30*59612Sbostic #include <tzfile.h>
31*59612Sbostic #include <unistd.h>
32*59612Sbostic 
3340947Sbostic #include "find.h"
3440947Sbostic 
35*59612Sbostic #define	COMPARE(a, b) {							\
36*59612Sbostic 	switch (plan->flags) {						\
37*59612Sbostic 	case F_EQUAL:							\
38*59612Sbostic 		return (a == b);					\
39*59612Sbostic 	case F_LESSTHAN:						\
40*59612Sbostic 		return (a < b);						\
41*59612Sbostic 	case F_GREATER:							\
42*59612Sbostic 		return (a > b);						\
43*59612Sbostic 	default:							\
44*59612Sbostic 		abort();						\
45*59612Sbostic 	}								\
4640947Sbostic }
4740947Sbostic 
48*59612Sbostic 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
55*59612Sbostic find_parsenum(plan, option, vp, endch)
5640947Sbostic 	PLAN *plan;
57*59612Sbostic 	char *option, *vp, *endch;
5840947Sbostic {
5940947Sbostic 	long value;
60*59612Sbostic 	char *endchar, *str;	/* pointer to character ending conversion */
6140947Sbostic 
6240947Sbostic 	/* determine comparison from leading + or - */
63*59612Sbostic 	str = vp;
64*59612Sbostic 	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)
85*59612Sbostic 		errx(1, "%s: %s: illegal numeric value", option, vp);
8655710Sbostic 	if (endchar[0] && (endch == NULL || endchar[0] != *endch))
87*59612Sbostic 		errx(1, "%s: %s: illegal trailing character", option, vp);
8840947Sbostic 	if (endch)
8940947Sbostic 		*endch = endchar[0];
90*59612Sbostic 	return (value);
9140947Sbostic }
9240947Sbostic 
9340947Sbostic /*
9440947Sbostic  * -atime n functions --
9540947Sbostic  *
9640947Sbostic  *	True if the difference between the file access time and the
9740947Sbostic  *	current time is n 24 hour periods.
9840947Sbostic  *
9940947Sbostic  */
100*59612Sbostic int
10140947Sbostic f_atime(plan, entry)
10240947Sbostic 	PLAN *plan;
10340947Sbostic 	FTSENT *entry;
10440947Sbostic {
10540947Sbostic 	extern time_t now;
10640947Sbostic 
10752239Sbostic 	COMPARE((now - entry->fts_statp->st_atime +
10842255Sbostic 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
10940947Sbostic }
11040947Sbostic 
11140947Sbostic PLAN *
11240947Sbostic c_atime(arg)
11340947Sbostic 	char *arg;
11440947Sbostic {
11540947Sbostic 	PLAN *new;
11640947Sbostic 
11740947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
11840947Sbostic 
11949868Sbostic 	new = palloc(N_ATIME, f_atime);
12058066Smarc 	new->t_data = find_parsenum(new, "-atime", arg, NULL);
121*59612Sbostic 	return (new);
12240947Sbostic }
12340947Sbostic /*
12440947Sbostic  * -ctime n functions --
12540947Sbostic  *
12640947Sbostic  *	True if the difference between the last change of file
12740947Sbostic  *	status information and the current time is n 24 hour periods.
12840947Sbostic  */
129*59612Sbostic int
13040947Sbostic f_ctime(plan, entry)
13140947Sbostic 	PLAN *plan;
13240947Sbostic 	FTSENT *entry;
13340947Sbostic {
13440947Sbostic 	extern time_t now;
13540947Sbostic 
13652239Sbostic 	COMPARE((now - entry->fts_statp->st_ctime +
13742255Sbostic 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
13840947Sbostic }
13940947Sbostic 
14040947Sbostic PLAN *
14140947Sbostic c_ctime(arg)
14240947Sbostic 	char *arg;
14340947Sbostic {
14440947Sbostic 	PLAN *new;
14540947Sbostic 
14640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
14740947Sbostic 
14849868Sbostic 	new = palloc(N_CTIME, f_ctime);
14958066Smarc 	new->t_data = find_parsenum(new, "-ctime", arg, NULL);
150*59612Sbostic 	return (new);
15140947Sbostic }
15240947Sbostic 
15340947Sbostic /*
15440947Sbostic  * -depth functions --
15540947Sbostic  *
15640947Sbostic  *	Always true, causes descent of the directory hierarchy to be done
15740947Sbostic  *	so that all entries in a directory are acted on before the directory
15840947Sbostic  *	itself.
15940947Sbostic  */
160*59612Sbostic int
16140947Sbostic f_always_true(plan, entry)
16240947Sbostic 	PLAN *plan;
16340947Sbostic 	FTSENT *entry;
16440947Sbostic {
165*59612Sbostic 	return (1);
16640947Sbostic }
16740947Sbostic 
16840947Sbostic PLAN *
16940947Sbostic c_depth()
17040947Sbostic {
17145615Sbostic 	isdepth = 1;
17240947Sbostic 
173*59612Sbostic 	return (palloc(N_DEPTH, f_always_true));
17440947Sbostic }
17540947Sbostic 
17640947Sbostic /*
17740947Sbostic  * [-exec | -ok] utility [arg ... ] ; functions --
17840947Sbostic  *
17940947Sbostic  *	True if the executed utility returns a zero value as exit status.
18040947Sbostic  *	The end of the primary expression is delimited by a semicolon.  If
18140947Sbostic  *	"{}" occurs anywhere, it gets replaced by the current pathname.
18240947Sbostic  *	The current directory for the execution of utility is the same as
18340947Sbostic  *	the current directory when the find utility was started.
18440947Sbostic  *
18540947Sbostic  *	The primary -ok is different in that it requests affirmation of the
18640947Sbostic  *	user before executing the utility.
18740947Sbostic  */
188*59612Sbostic int
18940947Sbostic f_exec(plan, entry)
19040947Sbostic 	register PLAN *plan;
19140947Sbostic 	FTSENT *entry;
19240947Sbostic {
19349869Sbostic 	extern int dotfd;
19440947Sbostic 	register int cnt;
19547192Sbostic 	pid_t pid;
19647192Sbostic 	int status;
19740947Sbostic 
19840947Sbostic 	for (cnt = 0; plan->e_argv[cnt]; ++cnt)
19940947Sbostic 		if (plan->e_len[cnt])
20045616Sbostic 			brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
20149869Sbostic 			    entry->fts_path, plan->e_len[cnt]);
20240947Sbostic 
20350437Sbostic 	if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
204*59612Sbostic 		return (0);
20540947Sbostic 
206*59612Sbostic 	switch (pid = vfork()) {
20740947Sbostic 	case -1:
208*59612Sbostic 		err(1, "fork");
20940947Sbostic 		/* NOTREACHED */
21040947Sbostic 	case 0:
21149869Sbostic 		if (fchdir(dotfd)) {
212*59612Sbostic 			warn("chdir");
21349869Sbostic 			_exit(1);
21449869Sbostic 		}
21540947Sbostic 		execvp(plan->e_argv[0], plan->e_argv);
216*59612Sbostic 		warn("%s", plan->e_argv[0]);
21749869Sbostic 		_exit(1);
21840947Sbostic 	}
21947192Sbostic 	pid = waitpid(pid, &status, 0);
220*59612Sbostic 	return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
22140947Sbostic }
22240947Sbostic 
22340947Sbostic /*
22440947Sbostic  * c_exec --
22540947Sbostic  *	build three parallel arrays, one with pointers to the strings passed
22640947Sbostic  *	on the command line, one with (possibly duplicated) pointers to the
22740947Sbostic  *	argv array, and one with integer values that are lengths of the
22840947Sbostic  *	strings, but also flags meaning that the string has to be massaged.
22940947Sbostic  */
23040947Sbostic PLAN *
23140947Sbostic c_exec(argvp, isok)
23240947Sbostic 	char ***argvp;
23340947Sbostic 	int isok;
23440947Sbostic {
23540947Sbostic 	PLAN *new;			/* node returned */
23640947Sbostic 	register int cnt;
23740947Sbostic 	register char **argv, **ap, *p;
23840947Sbostic 
23947192Sbostic 	isoutput = 1;
24040947Sbostic 
24149868Sbostic 	new = palloc(N_EXEC, f_exec);
24250437Sbostic 	if (isok)
24350437Sbostic 		new->flags = F_NEEDOK;
24440947Sbostic 
24540947Sbostic 	for (ap = argv = *argvp;; ++ap) {
24640947Sbostic 		if (!*ap)
247*59612Sbostic 			errx(1,
248*59612Sbostic 			    "%s: no terminating \";\"", isok ? "-ok" : "-exec");
24940947Sbostic 		if (**ap == ';')
25040947Sbostic 			break;
25140947Sbostic 	}
25240947Sbostic 
25340947Sbostic 	cnt = ap - *argvp + 1;
25440947Sbostic 	new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
25540947Sbostic 	new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
25645868Sbostic 	new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
25740947Sbostic 
25840947Sbostic 	for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
25940947Sbostic 		new->e_orig[cnt] = *argv;
26040947Sbostic 		for (p = *argv; *p; ++p)
26140947Sbostic 			if (p[0] == '{' && p[1] == '}') {
26245868Sbostic 				new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
26345868Sbostic 				new->e_len[cnt] = MAXPATHLEN;
26440947Sbostic 				break;
26540947Sbostic 			}
26640947Sbostic 		if (!*p) {
26740947Sbostic 			new->e_argv[cnt] = *argv;
26840947Sbostic 			new->e_len[cnt] = 0;
26940947Sbostic 		}
27040947Sbostic 	}
27140947Sbostic 	new->e_argv[cnt] = new->e_orig[cnt] = NULL;
27240947Sbostic 
27340947Sbostic 	*argvp = argv + 1;
274*59612Sbostic 	return (new);
27540947Sbostic }
27640947Sbostic 
27740947Sbostic /*
27840947Sbostic  * -follow functions --
27940947Sbostic  *
28040947Sbostic  *	Always true, causes symbolic links to be followed on a global
28140947Sbostic  *	basis.
28240947Sbostic  */
28340947Sbostic PLAN *
28440947Sbostic c_follow()
28540947Sbostic {
28640947Sbostic 	ftsoptions &= ~FTS_PHYSICAL;
28740947Sbostic 	ftsoptions |= FTS_LOGICAL;
28840947Sbostic 
289*59612Sbostic 	return (palloc(N_FOLLOW, f_always_true));
29040947Sbostic }
29140947Sbostic 
29240947Sbostic /*
29340947Sbostic  * -fstype functions --
29440947Sbostic  *
29540947Sbostic  *	True if the file is of a certain type.
29640947Sbostic  */
297*59612Sbostic int
29840947Sbostic f_fstype(plan, entry)
29940947Sbostic 	PLAN *plan;
30040947Sbostic 	FTSENT *entry;
30140947Sbostic {
30242275Sbostic 	static dev_t curdev;	/* need a guaranteed illegal dev value */
30342275Sbostic 	static int first = 1;
30440947Sbostic 	struct statfs sb;
30542255Sbostic 	static short val;
30645626Sbostic 	char *p, save[2];
30740947Sbostic 
30850437Sbostic 	/* Only check when we cross mount point. */
30952239Sbostic 	if (first || curdev != entry->fts_statp->st_dev) {
31052239Sbostic 		curdev = entry->fts_statp->st_dev;
31145626Sbostic 
31245626Sbostic 		/*
31345626Sbostic 		 * Statfs follows symlinks; find wants the link's file system,
31445626Sbostic 		 * not where it points.
31545626Sbostic 		 */
31645626Sbostic 		if (entry->fts_info == FTS_SL ||
31745626Sbostic 		    entry->fts_info == FTS_SLNONE) {
318*59612Sbostic 			if (p = strrchr(entry->fts_accpath, '/'))
31945626Sbostic 				++p;
32045626Sbostic 			else
32145626Sbostic 				p = entry->fts_accpath;
32245626Sbostic 			save[0] = p[0];
32345626Sbostic 			p[0] = '.';
32445626Sbostic 			save[1] = p[1];
32545626Sbostic 			p[1] = '\0';
32645626Sbostic 
32745626Sbostic 		} else
32845626Sbostic 			p = NULL;
32945626Sbostic 
33049868Sbostic 		if (statfs(entry->fts_accpath, &sb))
331*59612Sbostic 			err(1, "%s", entry->fts_accpath);
33245626Sbostic 
33345626Sbostic 		if (p) {
33445626Sbostic 			p[0] = save[0];
33545626Sbostic 			p[1] = save[1];
33645626Sbostic 		}
33745626Sbostic 
33842275Sbostic 		first = 0;
339*59612Sbostic 		switch (plan->flags) {
34050437Sbostic 		case F_MTFLAG:
34150437Sbostic 			val = sb.f_flags;
34250437Sbostic 			break;
34350437Sbostic 		case F_MTTYPE:
34450437Sbostic 			val = sb.f_type;
34550437Sbostic 			break;
346*59612Sbostic 		default:
347*59612Sbostic 			abort();
34850437Sbostic 		}
34940947Sbostic 	}
35050437Sbostic 	switch(plan->flags) {
35150437Sbostic 	case F_MTFLAG:
352*59612Sbostic 		return (val & plan->mt_data);
35350437Sbostic 	case F_MTTYPE:
354*59612Sbostic 		return (val == plan->mt_data);
355*59612Sbostic 	default:
356*59612Sbostic 		abort();
35750437Sbostic 	}
35840947Sbostic }
35940947Sbostic 
36040947Sbostic PLAN *
36140947Sbostic c_fstype(arg)
36240947Sbostic 	char *arg;
36340947Sbostic {
36440947Sbostic 	register PLAN *new;
36540947Sbostic 
36640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
36740947Sbostic 
36849868Sbostic 	new = palloc(N_FSTYPE, f_fstype);
369*59612Sbostic 	switch (*arg) {
37042255Sbostic 	case 'l':
37142255Sbostic 		if (!strcmp(arg, "local")) {
37250437Sbostic 			new->flags = F_MTFLAG;
37350437Sbostic 			new->mt_data = MNT_LOCAL;
374*59612Sbostic 			return (new);
37542255Sbostic 		}
37642255Sbostic 		break;
37740947Sbostic 	case 'm':
37840947Sbostic 		if (!strcmp(arg, "mfs")) {
37950437Sbostic 			new->flags = F_MTTYPE;
38050437Sbostic 			new->mt_data = MOUNT_MFS;
381*59612Sbostic 			return (new);
38240947Sbostic 		}
38340947Sbostic 		break;
38440947Sbostic 	case 'n':
38540947Sbostic 		if (!strcmp(arg, "nfs")) {
38650437Sbostic 			new->flags = F_MTTYPE;
38750437Sbostic 			new->mt_data = MOUNT_NFS;
388*59612Sbostic 			return (new);
38940947Sbostic 		}
39040947Sbostic 		break;
39140947Sbostic 	case 'p':
39240947Sbostic 		if (!strcmp(arg, "pc")) {
39350437Sbostic 			new->flags = F_MTTYPE;
39450437Sbostic 			new->mt_data = MOUNT_PC;
395*59612Sbostic 			return (new);
39640947Sbostic 		}
39740947Sbostic 		break;
39850437Sbostic 	case 'r':
39950437Sbostic 		if (!strcmp(arg, "rdonly")) {
40050437Sbostic 			new->flags = F_MTFLAG;
40150437Sbostic 			new->mt_data = MNT_RDONLY;
402*59612Sbostic 			return (new);
40350437Sbostic 		}
40450437Sbostic 		break;
40540947Sbostic 	case 'u':
40640947Sbostic 		if (!strcmp(arg, "ufs")) {
40750437Sbostic 			new->flags = F_MTTYPE;
40850437Sbostic 			new->mt_data = MOUNT_UFS;
409*59612Sbostic 			return (new);
41040947Sbostic 		}
41140947Sbostic 		break;
41240947Sbostic 	}
413*59612Sbostic 	errx(1, "%s: unknown file type", arg);
41440947Sbostic 	/* NOTREACHED */
41540947Sbostic }
41640947Sbostic 
41740947Sbostic /*
41840947Sbostic  * -group gname functions --
41940947Sbostic  *
42040947Sbostic  *	True if the file belongs to the group gname.  If gname is numeric and
42140947Sbostic  *	an equivalent of the getgrnam() function does not return a valid group
42240947Sbostic  *	name, gname is taken as a group ID.
42340947Sbostic  */
424*59612Sbostic int
42540947Sbostic f_group(plan, entry)
42640947Sbostic 	PLAN *plan;
42740947Sbostic 	FTSENT *entry;
42840947Sbostic {
429*59612Sbostic 	return (entry->fts_statp->st_gid == plan->g_data);
43040947Sbostic }
43140947Sbostic 
43240947Sbostic PLAN *
43340947Sbostic c_group(gname)
43440947Sbostic 	char *gname;
43540947Sbostic {
43640947Sbostic 	PLAN *new;
43740947Sbostic 	struct group *g;
43840947Sbostic 	gid_t gid;
43940947Sbostic 
44040947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
44140947Sbostic 
44240947Sbostic 	g = getgrnam(gname);
44340947Sbostic 	if (g == NULL) {
44440947Sbostic 		gid = atoi(gname);
44540947Sbostic 		if (gid == 0 && gname[0] != '0')
446*59612Sbostic 			errx(1, "-group: %s: no such group", gname);
44740947Sbostic 	} else
44840947Sbostic 		gid = g->gr_gid;
44940947Sbostic 
45049868Sbostic 	new = palloc(N_GROUP, f_group);
45140947Sbostic 	new->g_data = gid;
452*59612Sbostic 	return (new);
45340947Sbostic }
45440947Sbostic 
45540947Sbostic /*
45640947Sbostic  * -inum n functions --
45740947Sbostic  *
45840947Sbostic  *	True if the file has inode # n.
45940947Sbostic  */
460*59612Sbostic int
46140947Sbostic f_inum(plan, entry)
46240947Sbostic 	PLAN *plan;
46340947Sbostic 	FTSENT *entry;
46440947Sbostic {
46552239Sbostic 	COMPARE(entry->fts_statp->st_ino, plan->i_data);
46640947Sbostic }
46740947Sbostic 
46840947Sbostic PLAN *
46940947Sbostic c_inum(arg)
47040947Sbostic 	char *arg;
47140947Sbostic {
47240947Sbostic 	PLAN *new;
47340947Sbostic 
47440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
47540947Sbostic 
47649868Sbostic 	new = palloc(N_INUM, f_inum);
47758066Smarc 	new->i_data = find_parsenum(new, "-inum", arg, NULL);
478*59612Sbostic 	return (new);
47940947Sbostic }
48040947Sbostic 
48140947Sbostic /*
48240947Sbostic  * -links n functions --
48340947Sbostic  *
48440947Sbostic  *	True if the file has n links.
48540947Sbostic  */
486*59612Sbostic int
48740947Sbostic f_links(plan, entry)
48840947Sbostic 	PLAN *plan;
48940947Sbostic 	FTSENT *entry;
49040947Sbostic {
49152239Sbostic 	COMPARE(entry->fts_statp->st_nlink, plan->l_data);
49240947Sbostic }
49340947Sbostic 
49440947Sbostic PLAN *
49540947Sbostic c_links(arg)
49640947Sbostic 	char *arg;
49740947Sbostic {
49840947Sbostic 	PLAN *new;
49940947Sbostic 
50040947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
50140947Sbostic 
50249868Sbostic 	new = palloc(N_LINKS, f_links);
50358066Smarc 	new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
504*59612Sbostic 	return (new);
50540947Sbostic }
50640947Sbostic 
50740947Sbostic /*
50840947Sbostic  * -ls functions --
50940947Sbostic  *
51040947Sbostic  *	Always true - prints the current entry to stdout in "ls" format.
51140947Sbostic  */
512*59612Sbostic int
51340947Sbostic f_ls(plan, entry)
51440947Sbostic 	PLAN *plan;
51540947Sbostic 	FTSENT *entry;
51640947Sbostic {
51752239Sbostic 	printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
518*59612Sbostic 	return (1);
51940947Sbostic }
52040947Sbostic 
52140947Sbostic PLAN *
52240947Sbostic c_ls()
52340947Sbostic {
52440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
52545615Sbostic 	isoutput = 1;
52640947Sbostic 
527*59612Sbostic 	return (palloc(N_LS, f_ls));
52840947Sbostic }
52940947Sbostic 
53040947Sbostic /*
53150437Sbostic  * -mtime n functions --
53250437Sbostic  *
53350437Sbostic  *	True if the difference between the file modification time and the
53450437Sbostic  *	current time is n 24 hour periods.
53550437Sbostic  */
536*59612Sbostic int
53750437Sbostic f_mtime(plan, entry)
53850437Sbostic 	PLAN *plan;
53950437Sbostic 	FTSENT *entry;
54050437Sbostic {
54150437Sbostic 	extern time_t now;
54250437Sbostic 
54352239Sbostic 	COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
54450437Sbostic 	    SECSPERDAY, plan->t_data);
54550437Sbostic }
54650437Sbostic 
54750437Sbostic PLAN *
54850437Sbostic c_mtime(arg)
54950437Sbostic 	char *arg;
55050437Sbostic {
55150437Sbostic 	PLAN *new;
55250437Sbostic 
55350437Sbostic 	ftsoptions &= ~FTS_NOSTAT;
55450437Sbostic 
55550437Sbostic 	new = palloc(N_MTIME, f_mtime);
55658066Smarc 	new->t_data = find_parsenum(new, "-mtime", arg, NULL);
557*59612Sbostic 	return (new);
55850437Sbostic }
55950437Sbostic 
56050437Sbostic /*
56140947Sbostic  * -name functions --
56240947Sbostic  *
56340947Sbostic  *	True if the basename of the filename being examined
56440947Sbostic  *	matches pattern using Pattern Matching Notation S3.14
56540947Sbostic  */
566*59612Sbostic int
56740947Sbostic f_name(plan, entry)
56840947Sbostic 	PLAN *plan;
56940947Sbostic 	FTSENT *entry;
57040947Sbostic {
571*59612Sbostic 	return (!fnmatch(plan->c_data, entry->fts_name, 0));
57240947Sbostic }
57340947Sbostic 
57440947Sbostic PLAN *
57540947Sbostic c_name(pattern)
57640947Sbostic 	char *pattern;
57740947Sbostic {
57840947Sbostic 	PLAN *new;
57940947Sbostic 
58049868Sbostic 	new = palloc(N_NAME, f_name);
58140947Sbostic 	new->c_data = pattern;
582*59612Sbostic 	return (new);
58340947Sbostic }
58440947Sbostic 
58540947Sbostic /*
58640947Sbostic  * -newer file functions --
58740947Sbostic  *
58840947Sbostic  *	True if the current file has been modified more recently
58940947Sbostic  *	then the modification time of the file named by the pathname
59040947Sbostic  *	file.
59140947Sbostic  */
592*59612Sbostic int
59340947Sbostic f_newer(plan, entry)
59440947Sbostic 	PLAN *plan;
59540947Sbostic 	FTSENT *entry;
59640947Sbostic {
597*59612Sbostic 	return (entry->fts_statp->st_mtime > plan->t_data);
59840947Sbostic }
59940947Sbostic 
60040947Sbostic PLAN *
60140947Sbostic c_newer(filename)
60240947Sbostic 	char *filename;
60340947Sbostic {
60440947Sbostic 	PLAN *new;
60540947Sbostic 	struct stat sb;
60640947Sbostic 
60740947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
60840947Sbostic 
60949868Sbostic 	if (stat(filename, &sb))
610*59612Sbostic 		err(1, "%s", filename);
61149868Sbostic 	new = palloc(N_NEWER, f_newer);
61240947Sbostic 	new->t_data = sb.st_mtime;
613*59612Sbostic 	return (new);
61440947Sbostic }
61540947Sbostic 
61640947Sbostic /*
61740947Sbostic  * -nogroup functions --
61840947Sbostic  *
61940947Sbostic  *	True if file belongs to a user ID for which the equivalent
62040947Sbostic  *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
62140947Sbostic  */
622*59612Sbostic int
62340947Sbostic f_nogroup(plan, entry)
62440947Sbostic 	PLAN *plan;
62540947Sbostic 	FTSENT *entry;
62640947Sbostic {
62745615Sbostic 	char *group_from_gid();
62845615Sbostic 
629*59612Sbostic 	return (group_from_gid(entry->fts_statp->st_gid, 1) ? 1 : 0);
63040947Sbostic }
63140947Sbostic 
63240947Sbostic PLAN *
63340947Sbostic c_nogroup()
63440947Sbostic {
63540947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
63640947Sbostic 
637*59612Sbostic 	return (palloc(N_NOGROUP, f_nogroup));
63840947Sbostic }
63940947Sbostic 
64040947Sbostic /*
64140947Sbostic  * -nouser functions --
64240947Sbostic  *
64340947Sbostic  *	True if file belongs to a user ID for which the equivalent
64440947Sbostic  *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
64540947Sbostic  */
646*59612Sbostic int
64740947Sbostic f_nouser(plan, entry)
64840947Sbostic 	PLAN *plan;
64940947Sbostic 	FTSENT *entry;
65040947Sbostic {
65145615Sbostic 	char *user_from_uid();
65245615Sbostic 
653*59612Sbostic 	return (user_from_uid(entry->fts_statp->st_uid, 1) ? 1 : 0);
65440947Sbostic }
65540947Sbostic 
65640947Sbostic PLAN *
65740947Sbostic c_nouser()
65840947Sbostic {
65940947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
66040947Sbostic 
661*59612Sbostic 	return (palloc(N_NOUSER, f_nouser));
66240947Sbostic }
66340947Sbostic 
66440947Sbostic /*
66550437Sbostic  * -path functions --
66650437Sbostic  *
66750437Sbostic  *	True if the path of the filename being examined
66850437Sbostic  *	matches pattern using Pattern Matching Notation S3.14
66950437Sbostic  */
670*59612Sbostic int
67150437Sbostic f_path(plan, entry)
67250437Sbostic 	PLAN *plan;
67350437Sbostic 	FTSENT *entry;
67450437Sbostic {
675*59612Sbostic 	return (!fnmatch(plan->c_data, entry->fts_path, 0));
67650437Sbostic }
67750437Sbostic 
67850437Sbostic PLAN *
67950437Sbostic c_path(pattern)
68050437Sbostic 	char *pattern;
68150437Sbostic {
68250437Sbostic 	PLAN *new;
68350437Sbostic 
68450437Sbostic 	new = palloc(N_NAME, f_path);
68550437Sbostic 	new->c_data = pattern;
686*59612Sbostic 	return (new);
68750437Sbostic }
68850437Sbostic 
68950437Sbostic /*
69040947Sbostic  * -perm functions --
69140947Sbostic  *
69240947Sbostic  *	The mode argument is used to represent file mode bits.  If it starts
69340947Sbostic  *	with a leading digit, it's treated as an octal mode, otherwise as a
69440947Sbostic  *	symbolic mode.
69540947Sbostic  */
696*59612Sbostic int
69740947Sbostic f_perm(plan, entry)
69840947Sbostic 	PLAN *plan;
69940947Sbostic 	FTSENT *entry;
70040947Sbostic {
70140947Sbostic 	mode_t mode;
70240947Sbostic 
70352239Sbostic 	mode = entry->fts_statp->st_mode &
70440947Sbostic 	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
70550437Sbostic 	if (plan->flags == F_ATLEAST)
706*59612Sbostic 		return ((plan->m_data | mode) == mode);
70740947Sbostic 	else
708*59612Sbostic 		return (mode == plan->m_data);
70940947Sbostic 	/* NOTREACHED */
71040947Sbostic }
71140947Sbostic 
71240947Sbostic PLAN *
71340947Sbostic c_perm(perm)
71440947Sbostic 	char *perm;
71540947Sbostic {
71640947Sbostic 	PLAN *new;
71747192Sbostic 	mode_t *set;
71840947Sbostic 
71940947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
72040947Sbostic 
72149868Sbostic 	new = palloc(N_PERM, f_perm);
72240947Sbostic 
72340947Sbostic 	if (*perm == '-') {
72450437Sbostic 		new->flags = F_ATLEAST;
72540947Sbostic 		++perm;
72640947Sbostic 	}
72740947Sbostic 
72844764Strent 	if ((set = setmode(perm)) == NULL)
729*59612Sbostic 		err(1, "-perm: %s: illegal mode string", perm);
73040947Sbostic 
73144764Strent 	new->m_data = getmode(set, 0);
732*59612Sbostic 	return (new);
73340947Sbostic }
73440947Sbostic 
73540947Sbostic /*
73640947Sbostic  * -print functions --
73740947Sbostic  *
73840947Sbostic  *	Always true, causes the current pathame to be written to
73940947Sbostic  *	standard output.
74040947Sbostic  */
741*59612Sbostic int
74240947Sbostic f_print(plan, entry)
74340947Sbostic 	PLAN *plan;
74440947Sbostic 	FTSENT *entry;
74540947Sbostic {
74642255Sbostic 	(void)printf("%s\n", entry->fts_path);
747*59612Sbostic 	return (1);
74840947Sbostic }
74940947Sbostic 
75040947Sbostic PLAN *
75140947Sbostic c_print()
75240947Sbostic {
75345615Sbostic 	isoutput = 1;
75440947Sbostic 
755*59612Sbostic 	return (palloc(N_PRINT, f_print));
75640947Sbostic }
75740947Sbostic 
75840947Sbostic /*
75940947Sbostic  * -prune functions --
76040947Sbostic  *
76140947Sbostic  *	Prune a portion of the hierarchy.
76240947Sbostic  */
763*59612Sbostic int
76440947Sbostic f_prune(plan, entry)
76540947Sbostic 	PLAN *plan;
76640947Sbostic 	FTSENT *entry;
76740947Sbostic {
76840947Sbostic 	extern FTS *tree;
76940947Sbostic 
77049868Sbostic 	if (fts_set(tree, entry, FTS_SKIP))
771*59612Sbostic 		err(1, "%s", entry->fts_path);
772*59612Sbostic 	return (1);
77340947Sbostic }
77440947Sbostic 
77540947Sbostic PLAN *
77640947Sbostic c_prune()
77740947Sbostic {
778*59612Sbostic 	return (palloc(N_PRUNE, f_prune));
77940947Sbostic }
78040947Sbostic 
78140947Sbostic /*
78240947Sbostic  * -size n[c] functions --
78340947Sbostic  *
78440947Sbostic  *	True if the file size in bytes, divided by an implementation defined
78540947Sbostic  *	value and rounded up to the next integer, is n.  If n is followed by
78640947Sbostic  *	a c, the size is in bytes.
78740947Sbostic  */
78840947Sbostic #define	FIND_SIZE	512
78940947Sbostic static int divsize = 1;
79040947Sbostic 
791*59612Sbostic int
79240947Sbostic f_size(plan, entry)
79340947Sbostic 	PLAN *plan;
79440947Sbostic 	FTSENT *entry;
79540947Sbostic {
79640947Sbostic 	off_t size;
79740947Sbostic 
79852239Sbostic 	size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
79952239Sbostic 	    FIND_SIZE : entry->fts_statp->st_size;
80040947Sbostic 	COMPARE(size, plan->o_data);
80140947Sbostic }
80240947Sbostic 
80340947Sbostic PLAN *
80440947Sbostic c_size(arg)
80540947Sbostic 	char *arg;
80640947Sbostic {
80740947Sbostic 	PLAN *new;
80840947Sbostic 	char endch;
80940947Sbostic 
81040947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
81140947Sbostic 
81249868Sbostic 	new = palloc(N_SIZE, f_size);
81355710Sbostic 	endch = 'c';
81458066Smarc 	new->o_data = find_parsenum(new, "-size", arg, &endch);
81540947Sbostic 	if (endch == 'c')
81640947Sbostic 		divsize = 0;
817*59612Sbostic 	return (new);
81840947Sbostic }
81940947Sbostic 
82040947Sbostic /*
82140947Sbostic  * -type c functions --
82240947Sbostic  *
82340947Sbostic  *	True if the type of the file is c, where c is b, c, d, p, or f for
82440947Sbostic  *	block special file, character special file, directory, FIFO, or
82540947Sbostic  *	regular file, respectively.
82640947Sbostic  */
827*59612Sbostic int
82840947Sbostic f_type(plan, entry)
82940947Sbostic 	PLAN *plan;
83040947Sbostic 	FTSENT *entry;
83140947Sbostic {
832*59612Sbostic 	return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
83340947Sbostic }
83440947Sbostic 
83540947Sbostic PLAN *
83640947Sbostic c_type(typestring)
83740947Sbostic 	char *typestring;
83840947Sbostic {
83940947Sbostic 	PLAN *new;
84040947Sbostic 	mode_t  mask;
84140947Sbostic 
84240947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
84340947Sbostic 
84440947Sbostic 	switch (typestring[0]) {
84540947Sbostic 	case 'b':
84640947Sbostic 		mask = S_IFBLK;
84740947Sbostic 		break;
84840947Sbostic 	case 'c':
84940947Sbostic 		mask = S_IFCHR;
85040947Sbostic 		break;
85140947Sbostic 	case 'd':
85240947Sbostic 		mask = S_IFDIR;
85340947Sbostic 		break;
85440947Sbostic 	case 'f':
85540947Sbostic 		mask = S_IFREG;
85640947Sbostic 		break;
85740947Sbostic 	case 'l':
85840947Sbostic 		mask = S_IFLNK;
85940947Sbostic 		break;
86040947Sbostic 	case 'p':
86140947Sbostic 		mask = S_IFIFO;
86240947Sbostic 		break;
86340947Sbostic 	case 's':
86440947Sbostic 		mask = S_IFSOCK;
86540947Sbostic 		break;
86640947Sbostic 	default:
867*59612Sbostic 		errx(1, "-type: %s: unknown type", typestring);
86840947Sbostic 	}
86940947Sbostic 
87049868Sbostic 	new = palloc(N_TYPE, f_type);
87140947Sbostic 	new->m_data = mask;
872*59612Sbostic 	return (new);
87340947Sbostic }
87440947Sbostic 
87540947Sbostic /*
87640947Sbostic  * -user uname functions --
87740947Sbostic  *
87840947Sbostic  *	True if the file belongs to the user uname.  If uname is numeric and
87940947Sbostic  *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
88040947Sbostic  *	return a valid user name, uname is taken as a user ID.
88140947Sbostic  */
882*59612Sbostic int
88340947Sbostic f_user(plan, entry)
88440947Sbostic 	PLAN *plan;
88540947Sbostic 	FTSENT *entry;
88640947Sbostic {
887*59612Sbostic 	return (entry->fts_statp->st_uid == plan->u_data);
88840947Sbostic }
88940947Sbostic 
89040947Sbostic PLAN *
89140947Sbostic c_user(username)
89240947Sbostic 	char *username;
89340947Sbostic {
89440947Sbostic 	PLAN *new;
89540947Sbostic 	struct passwd *p;
89640947Sbostic 	uid_t uid;
89740947Sbostic 
89840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
89940947Sbostic 
90040947Sbostic 	p = getpwnam(username);
90140947Sbostic 	if (p == NULL) {
90240947Sbostic 		uid = atoi(username);
90340947Sbostic 		if (uid == 0 && username[0] != '0')
904*59612Sbostic 			errx(1, "-user: %s: no such user", username);
90540947Sbostic 	} else
90640947Sbostic 		uid = p->pw_uid;
90740947Sbostic 
90849868Sbostic 	new = palloc(N_USER, f_user);
90940947Sbostic 	new->u_data = uid;
910*59612Sbostic 	return (new);
91140947Sbostic }
91240947Sbostic 
91340947Sbostic /*
91440947Sbostic  * -xdev functions --
91540947Sbostic  *
91640947Sbostic  *	Always true, causes find not to decend past directories that have a
91740947Sbostic  *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
91840947Sbostic  */
91940947Sbostic PLAN *
92040947Sbostic c_xdev()
92140947Sbostic {
92242275Sbostic 	ftsoptions |= FTS_XDEV;
92340947Sbostic 
924*59612Sbostic 	return (palloc(N_XDEV, f_always_true));
92540947Sbostic }
92640947Sbostic 
92740947Sbostic /*
92840947Sbostic  * ( expression ) functions --
92940947Sbostic  *
93040947Sbostic  *	True if expression is true.
93140947Sbostic  */
932*59612Sbostic int
93340947Sbostic f_expr(plan, entry)
93440947Sbostic 	PLAN *plan;
93540947Sbostic 	FTSENT *entry;
93640947Sbostic {
93740947Sbostic 	register PLAN *p;
93840947Sbostic 	register int state;
93940947Sbostic 
94040947Sbostic 	for (p = plan->p_data[0];
94140947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
942*59612Sbostic 	return (state);
94340947Sbostic }
94440947Sbostic 
94540947Sbostic /*
94649864Sbostic  * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
94740947Sbostic  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
94849864Sbostic  * to a N_EXPR node containing the expression and the ')' node is discarded.
94940947Sbostic  */
95040947Sbostic PLAN *
95140947Sbostic c_openparen()
95240947Sbostic {
953*59612Sbostic 	return (palloc(N_OPENPAREN, (int (*)())-1));
95440947Sbostic }
95540947Sbostic 
95640947Sbostic PLAN *
95740947Sbostic c_closeparen()
95840947Sbostic {
959*59612Sbostic 	return (palloc(N_CLOSEPAREN, (int (*)())-1));
96040947Sbostic }
96140947Sbostic 
96240947Sbostic /*
96340947Sbostic  * ! expression functions --
96440947Sbostic  *
96540947Sbostic  *	Negation of a primary; the unary NOT operator.
96640947Sbostic  */
967*59612Sbostic int
96840947Sbostic f_not(plan, entry)
96940947Sbostic 	PLAN *plan;
97040947Sbostic 	FTSENT *entry;
97140947Sbostic {
97240947Sbostic 	register PLAN *p;
97340947Sbostic 	register int state;
97440947Sbostic 
97540947Sbostic 	for (p = plan->p_data[0];
97640947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
977*59612Sbostic 	return (!state);
97840947Sbostic }
97940947Sbostic 
98040947Sbostic PLAN *
98140947Sbostic c_not()
98240947Sbostic {
983*59612Sbostic 	return (palloc(N_NOT, f_not));
98440947Sbostic }
98540947Sbostic 
98640947Sbostic /*
98740947Sbostic  * expression -o expression functions --
98840947Sbostic  *
98940947Sbostic  *	Alternation of primaries; the OR operator.  The second expression is
99040947Sbostic  * not evaluated if the first expression is true.
99140947Sbostic  */
992*59612Sbostic int
99340947Sbostic f_or(plan, entry)
99440947Sbostic 	PLAN *plan;
99540947Sbostic 	FTSENT *entry;
99640947Sbostic {
99740947Sbostic 	register PLAN *p;
99840947Sbostic 	register int state;
99940947Sbostic 
100040947Sbostic 	for (p = plan->p_data[0];
100140947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
100240947Sbostic 
100340947Sbostic 	if (state)
1004*59612Sbostic 		return (1);
100540947Sbostic 
100640947Sbostic 	for (p = plan->p_data[1];
100740947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
1008*59612Sbostic 	return (state);
100940947Sbostic }
101040947Sbostic 
101140947Sbostic PLAN *
101240947Sbostic c_or()
101340947Sbostic {
1014*59612Sbostic 	return (palloc(N_OR, f_or));
101549868Sbostic }
101649868Sbostic 
101749868Sbostic static PLAN *
101849868Sbostic palloc(t, f)
101949868Sbostic 	enum ntype t;
1020*59612Sbostic 	int (*f) __P((PLAN *, FTSENT *));
102149868Sbostic {
102240947Sbostic 	PLAN *new;
102340947Sbostic 
102449868Sbostic 	if (new = malloc(sizeof(PLAN))) {
102549868Sbostic 		new->type = t;
102649868Sbostic 		new->eval = f;
102749868Sbostic 		new->flags = 0;
102849868Sbostic 		new->next = NULL;
1029*59612Sbostic 		return (new);
103049868Sbostic 	}
1031*59612Sbostic 	err(1, NULL);
103249868Sbostic 	/* NOTREACHED */
103340947Sbostic }
1034