xref: /csrg-svn/usr.bin/find/function.c (revision 69051)
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*69051Sbostic static char sccsid[] = "@(#)function.c	8.9 (Berkeley) 04/28/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
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
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 *
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
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 *
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
17240947Sbostic f_always_true(plan, entry)
17340947Sbostic 	PLAN *plan;
17440947Sbostic 	FTSENT *entry;
17540947Sbostic {
17659612Sbostic 	return (1);
17740947Sbostic }
17840947Sbostic 
17940947Sbostic PLAN *
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
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 *
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 *
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
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;
350*69051Sbostic 
351*69051Sbostic 		/*
352*69051Sbostic 		 * Further tests may need both of these values, so
353*69051Sbostic 		 * always copy both of them.
354*69051Sbostic 		 */
355*69051Sbostic 		val = sb.f_flags;
356*69051Sbostic 		val = sb.f_type;
35740947Sbostic 	}
358*69051Sbostic 	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 *
36940947Sbostic c_fstype(arg)
37040947Sbostic 	char *arg;
37140947Sbostic {
37240947Sbostic 	register PLAN *new;
37340947Sbostic 
37440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
37540947Sbostic 
37649868Sbostic 	new = palloc(N_FSTYPE, f_fstype);
37759612Sbostic 	switch (*arg) {
37842255Sbostic 	case 'l':
37942255Sbostic 		if (!strcmp(arg, "local")) {
38050437Sbostic 			new->flags = F_MTFLAG;
38150437Sbostic 			new->mt_data = MNT_LOCAL;
38259612Sbostic 			return (new);
38342255Sbostic 		}
38442255Sbostic 		break;
38540947Sbostic 	case 'm':
38640947Sbostic 		if (!strcmp(arg, "mfs")) {
38750437Sbostic 			new->flags = F_MTTYPE;
38850437Sbostic 			new->mt_data = MOUNT_MFS;
38959612Sbostic 			return (new);
39040947Sbostic 		}
39140947Sbostic 		break;
39240947Sbostic 	case 'n':
39340947Sbostic 		if (!strcmp(arg, "nfs")) {
39450437Sbostic 			new->flags = F_MTTYPE;
39550437Sbostic 			new->mt_data = MOUNT_NFS;
39659612Sbostic 			return (new);
39740947Sbostic 		}
39840947Sbostic 		break;
39940947Sbostic 	case 'p':
40065849Smckusick 		if (!strcmp(arg, "msdos")) {
40150437Sbostic 			new->flags = F_MTTYPE;
40265839Sbostic 			new->mt_data = MOUNT_MSDOS;
40359612Sbostic 			return (new);
40440947Sbostic 		}
40540947Sbostic 		break;
40650437Sbostic 	case 'r':
40750437Sbostic 		if (!strcmp(arg, "rdonly")) {
40850437Sbostic 			new->flags = F_MTFLAG;
40950437Sbostic 			new->mt_data = MNT_RDONLY;
41059612Sbostic 			return (new);
41150437Sbostic 		}
41250437Sbostic 		break;
41340947Sbostic 	case 'u':
41440947Sbostic 		if (!strcmp(arg, "ufs")) {
41550437Sbostic 			new->flags = F_MTTYPE;
41650437Sbostic 			new->mt_data = MOUNT_UFS;
41759612Sbostic 			return (new);
41840947Sbostic 		}
41940947Sbostic 		break;
42040947Sbostic 	}
42159612Sbostic 	errx(1, "%s: unknown file type", arg);
42240947Sbostic 	/* NOTREACHED */
42340947Sbostic }
42440947Sbostic 
42540947Sbostic /*
42640947Sbostic  * -group gname functions --
42740947Sbostic  *
42840947Sbostic  *	True if the file belongs to the group gname.  If gname is numeric and
42940947Sbostic  *	an equivalent of the getgrnam() function does not return a valid group
43040947Sbostic  *	name, gname is taken as a group ID.
43140947Sbostic  */
43259612Sbostic int
43340947Sbostic f_group(plan, entry)
43440947Sbostic 	PLAN *plan;
43540947Sbostic 	FTSENT *entry;
43640947Sbostic {
43759612Sbostic 	return (entry->fts_statp->st_gid == plan->g_data);
43840947Sbostic }
43940947Sbostic 
44040947Sbostic PLAN *
44140947Sbostic c_group(gname)
44240947Sbostic 	char *gname;
44340947Sbostic {
44440947Sbostic 	PLAN *new;
44540947Sbostic 	struct group *g;
44640947Sbostic 	gid_t gid;
44740947Sbostic 
44840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
44940947Sbostic 
45040947Sbostic 	g = getgrnam(gname);
45140947Sbostic 	if (g == NULL) {
45240947Sbostic 		gid = atoi(gname);
45340947Sbostic 		if (gid == 0 && gname[0] != '0')
45459612Sbostic 			errx(1, "-group: %s: no such group", gname);
45540947Sbostic 	} else
45640947Sbostic 		gid = g->gr_gid;
45740947Sbostic 
45849868Sbostic 	new = palloc(N_GROUP, f_group);
45940947Sbostic 	new->g_data = gid;
46059612Sbostic 	return (new);
46140947Sbostic }
46240947Sbostic 
46340947Sbostic /*
46440947Sbostic  * -inum n functions --
46540947Sbostic  *
46640947Sbostic  *	True if the file has inode # n.
46740947Sbostic  */
46859612Sbostic int
46940947Sbostic f_inum(plan, entry)
47040947Sbostic 	PLAN *plan;
47140947Sbostic 	FTSENT *entry;
47240947Sbostic {
47352239Sbostic 	COMPARE(entry->fts_statp->st_ino, plan->i_data);
47440947Sbostic }
47540947Sbostic 
47640947Sbostic PLAN *
47740947Sbostic c_inum(arg)
47840947Sbostic 	char *arg;
47940947Sbostic {
48040947Sbostic 	PLAN *new;
48140947Sbostic 
48240947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
48340947Sbostic 
48449868Sbostic 	new = palloc(N_INUM, f_inum);
48558066Smarc 	new->i_data = find_parsenum(new, "-inum", arg, NULL);
48659612Sbostic 	return (new);
48740947Sbostic }
48840947Sbostic 
48940947Sbostic /*
49040947Sbostic  * -links n functions --
49140947Sbostic  *
49240947Sbostic  *	True if the file has n links.
49340947Sbostic  */
49459612Sbostic int
49540947Sbostic f_links(plan, entry)
49640947Sbostic 	PLAN *plan;
49740947Sbostic 	FTSENT *entry;
49840947Sbostic {
49952239Sbostic 	COMPARE(entry->fts_statp->st_nlink, plan->l_data);
50040947Sbostic }
50140947Sbostic 
50240947Sbostic PLAN *
50340947Sbostic c_links(arg)
50440947Sbostic 	char *arg;
50540947Sbostic {
50640947Sbostic 	PLAN *new;
50740947Sbostic 
50840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
50940947Sbostic 
51049868Sbostic 	new = palloc(N_LINKS, f_links);
51158066Smarc 	new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
51259612Sbostic 	return (new);
51340947Sbostic }
51440947Sbostic 
51540947Sbostic /*
51640947Sbostic  * -ls functions --
51740947Sbostic  *
51840947Sbostic  *	Always true - prints the current entry to stdout in "ls" format.
51940947Sbostic  */
52059612Sbostic int
52140947Sbostic f_ls(plan, entry)
52240947Sbostic 	PLAN *plan;
52340947Sbostic 	FTSENT *entry;
52440947Sbostic {
52552239Sbostic 	printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
52659612Sbostic 	return (1);
52740947Sbostic }
52840947Sbostic 
52940947Sbostic PLAN *
53040947Sbostic c_ls()
53140947Sbostic {
53240947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
53345615Sbostic 	isoutput = 1;
53440947Sbostic 
53559612Sbostic 	return (palloc(N_LS, f_ls));
53640947Sbostic }
53740947Sbostic 
53840947Sbostic /*
53950437Sbostic  * -mtime n functions --
54050437Sbostic  *
54150437Sbostic  *	True if the difference between the file modification time and the
54250437Sbostic  *	current time is n 24 hour periods.
54350437Sbostic  */
54459612Sbostic int
54550437Sbostic f_mtime(plan, entry)
54650437Sbostic 	PLAN *plan;
54750437Sbostic 	FTSENT *entry;
54850437Sbostic {
54950437Sbostic 	extern time_t now;
55050437Sbostic 
55152239Sbostic 	COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
55250437Sbostic 	    SECSPERDAY, plan->t_data);
55350437Sbostic }
55450437Sbostic 
55550437Sbostic PLAN *
55650437Sbostic c_mtime(arg)
55750437Sbostic 	char *arg;
55850437Sbostic {
55950437Sbostic 	PLAN *new;
56050437Sbostic 
56150437Sbostic 	ftsoptions &= ~FTS_NOSTAT;
56250437Sbostic 
56350437Sbostic 	new = palloc(N_MTIME, f_mtime);
56458066Smarc 	new->t_data = find_parsenum(new, "-mtime", arg, NULL);
56559613Sbostic 	TIME_CORRECT(new, N_MTIME);
56659612Sbostic 	return (new);
56750437Sbostic }
56850437Sbostic 
56950437Sbostic /*
57040947Sbostic  * -name functions --
57140947Sbostic  *
57240947Sbostic  *	True if the basename of the filename being examined
57340947Sbostic  *	matches pattern using Pattern Matching Notation S3.14
57440947Sbostic  */
57559612Sbostic int
57640947Sbostic f_name(plan, entry)
57740947Sbostic 	PLAN *plan;
57840947Sbostic 	FTSENT *entry;
57940947Sbostic {
58059612Sbostic 	return (!fnmatch(plan->c_data, entry->fts_name, 0));
58140947Sbostic }
58240947Sbostic 
58340947Sbostic PLAN *
58440947Sbostic c_name(pattern)
58540947Sbostic 	char *pattern;
58640947Sbostic {
58740947Sbostic 	PLAN *new;
58840947Sbostic 
58949868Sbostic 	new = palloc(N_NAME, f_name);
59040947Sbostic 	new->c_data = pattern;
59159612Sbostic 	return (new);
59240947Sbostic }
59340947Sbostic 
59440947Sbostic /*
59540947Sbostic  * -newer file functions --
59640947Sbostic  *
59740947Sbostic  *	True if the current file has been modified more recently
59840947Sbostic  *	then the modification time of the file named by the pathname
59940947Sbostic  *	file.
60040947Sbostic  */
60159612Sbostic int
60240947Sbostic f_newer(plan, entry)
60340947Sbostic 	PLAN *plan;
60440947Sbostic 	FTSENT *entry;
60540947Sbostic {
60659612Sbostic 	return (entry->fts_statp->st_mtime > plan->t_data);
60740947Sbostic }
60840947Sbostic 
60940947Sbostic PLAN *
61040947Sbostic c_newer(filename)
61140947Sbostic 	char *filename;
61240947Sbostic {
61340947Sbostic 	PLAN *new;
61440947Sbostic 	struct stat sb;
61540947Sbostic 
61640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
61740947Sbostic 
61849868Sbostic 	if (stat(filename, &sb))
61959612Sbostic 		err(1, "%s", filename);
62049868Sbostic 	new = palloc(N_NEWER, f_newer);
62140947Sbostic 	new->t_data = sb.st_mtime;
62259612Sbostic 	return (new);
62340947Sbostic }
62440947Sbostic 
62540947Sbostic /*
62640947Sbostic  * -nogroup functions --
62740947Sbostic  *
62840947Sbostic  *	True if file belongs to a user ID for which the equivalent
62940947Sbostic  *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
63040947Sbostic  */
63159612Sbostic int
63240947Sbostic f_nogroup(plan, entry)
63340947Sbostic 	PLAN *plan;
63440947Sbostic 	FTSENT *entry;
63540947Sbostic {
63645615Sbostic 	char *group_from_gid();
63745615Sbostic 
63866220Sbostic 	return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
63940947Sbostic }
64040947Sbostic 
64140947Sbostic PLAN *
64240947Sbostic c_nogroup()
64340947Sbostic {
64440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
64540947Sbostic 
64659612Sbostic 	return (palloc(N_NOGROUP, f_nogroup));
64740947Sbostic }
64840947Sbostic 
64940947Sbostic /*
65040947Sbostic  * -nouser functions --
65140947Sbostic  *
65240947Sbostic  *	True if file belongs to a user ID for which the equivalent
65340947Sbostic  *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
65440947Sbostic  */
65559612Sbostic int
65640947Sbostic f_nouser(plan, entry)
65740947Sbostic 	PLAN *plan;
65840947Sbostic 	FTSENT *entry;
65940947Sbostic {
66045615Sbostic 	char *user_from_uid();
66145615Sbostic 
66266220Sbostic 	return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
66340947Sbostic }
66440947Sbostic 
66540947Sbostic PLAN *
66640947Sbostic c_nouser()
66740947Sbostic {
66840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
66940947Sbostic 
67059612Sbostic 	return (palloc(N_NOUSER, f_nouser));
67140947Sbostic }
67240947Sbostic 
67340947Sbostic /*
67450437Sbostic  * -path functions --
67550437Sbostic  *
67650437Sbostic  *	True if the path of the filename being examined
67750437Sbostic  *	matches pattern using Pattern Matching Notation S3.14
67850437Sbostic  */
67959612Sbostic int
68050437Sbostic f_path(plan, entry)
68150437Sbostic 	PLAN *plan;
68250437Sbostic 	FTSENT *entry;
68350437Sbostic {
68459612Sbostic 	return (!fnmatch(plan->c_data, entry->fts_path, 0));
68550437Sbostic }
68650437Sbostic 
68750437Sbostic PLAN *
68850437Sbostic c_path(pattern)
68950437Sbostic 	char *pattern;
69050437Sbostic {
69150437Sbostic 	PLAN *new;
69250437Sbostic 
69350437Sbostic 	new = palloc(N_NAME, f_path);
69450437Sbostic 	new->c_data = pattern;
69559612Sbostic 	return (new);
69650437Sbostic }
69750437Sbostic 
69850437Sbostic /*
69940947Sbostic  * -perm functions --
70040947Sbostic  *
70140947Sbostic  *	The mode argument is used to represent file mode bits.  If it starts
70240947Sbostic  *	with a leading digit, it's treated as an octal mode, otherwise as a
70340947Sbostic  *	symbolic mode.
70440947Sbostic  */
70559612Sbostic int
70640947Sbostic f_perm(plan, entry)
70740947Sbostic 	PLAN *plan;
70840947Sbostic 	FTSENT *entry;
70940947Sbostic {
71040947Sbostic 	mode_t mode;
71140947Sbostic 
71252239Sbostic 	mode = entry->fts_statp->st_mode &
71340947Sbostic 	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
71450437Sbostic 	if (plan->flags == F_ATLEAST)
71559612Sbostic 		return ((plan->m_data | mode) == mode);
71640947Sbostic 	else
71759612Sbostic 		return (mode == plan->m_data);
71840947Sbostic 	/* NOTREACHED */
71940947Sbostic }
72040947Sbostic 
72140947Sbostic PLAN *
72240947Sbostic c_perm(perm)
72340947Sbostic 	char *perm;
72440947Sbostic {
72540947Sbostic 	PLAN *new;
72647192Sbostic 	mode_t *set;
72740947Sbostic 
72840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
72940947Sbostic 
73049868Sbostic 	new = palloc(N_PERM, f_perm);
73140947Sbostic 
73240947Sbostic 	if (*perm == '-') {
73350437Sbostic 		new->flags = F_ATLEAST;
73440947Sbostic 		++perm;
73540947Sbostic 	}
73640947Sbostic 
73744764Strent 	if ((set = setmode(perm)) == NULL)
73859612Sbostic 		err(1, "-perm: %s: illegal mode string", perm);
73940947Sbostic 
74044764Strent 	new->m_data = getmode(set, 0);
74159612Sbostic 	return (new);
74240947Sbostic }
74340947Sbostic 
74440947Sbostic /*
74540947Sbostic  * -print functions --
74640947Sbostic  *
74740947Sbostic  *	Always true, causes the current pathame to be written to
74840947Sbostic  *	standard output.
74940947Sbostic  */
75059612Sbostic int
75140947Sbostic f_print(plan, entry)
75240947Sbostic 	PLAN *plan;
75340947Sbostic 	FTSENT *entry;
75440947Sbostic {
75542255Sbostic 	(void)printf("%s\n", entry->fts_path);
75659612Sbostic 	return (1);
75740947Sbostic }
75840947Sbostic 
75940947Sbostic PLAN *
76040947Sbostic c_print()
76140947Sbostic {
76245615Sbostic 	isoutput = 1;
76340947Sbostic 
76459612Sbostic 	return (palloc(N_PRINT, f_print));
76540947Sbostic }
76640947Sbostic 
76740947Sbostic /*
76840947Sbostic  * -prune functions --
76940947Sbostic  *
77040947Sbostic  *	Prune a portion of the hierarchy.
77140947Sbostic  */
77259612Sbostic int
77340947Sbostic f_prune(plan, entry)
77440947Sbostic 	PLAN *plan;
77540947Sbostic 	FTSENT *entry;
77640947Sbostic {
77740947Sbostic 	extern FTS *tree;
77840947Sbostic 
77949868Sbostic 	if (fts_set(tree, entry, FTS_SKIP))
78059612Sbostic 		err(1, "%s", entry->fts_path);
78159612Sbostic 	return (1);
78240947Sbostic }
78340947Sbostic 
78440947Sbostic PLAN *
78540947Sbostic c_prune()
78640947Sbostic {
78759612Sbostic 	return (palloc(N_PRUNE, f_prune));
78840947Sbostic }
78940947Sbostic 
79040947Sbostic /*
79140947Sbostic  * -size n[c] functions --
79240947Sbostic  *
79340947Sbostic  *	True if the file size in bytes, divided by an implementation defined
79440947Sbostic  *	value and rounded up to the next integer, is n.  If n is followed by
79540947Sbostic  *	a c, the size is in bytes.
79640947Sbostic  */
79740947Sbostic #define	FIND_SIZE	512
79840947Sbostic static int divsize = 1;
79940947Sbostic 
80059612Sbostic int
80140947Sbostic f_size(plan, entry)
80240947Sbostic 	PLAN *plan;
80340947Sbostic 	FTSENT *entry;
80440947Sbostic {
80540947Sbostic 	off_t size;
80640947Sbostic 
80752239Sbostic 	size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
80852239Sbostic 	    FIND_SIZE : entry->fts_statp->st_size;
80940947Sbostic 	COMPARE(size, plan->o_data);
81040947Sbostic }
81140947Sbostic 
81240947Sbostic PLAN *
81340947Sbostic c_size(arg)
81440947Sbostic 	char *arg;
81540947Sbostic {
81640947Sbostic 	PLAN *new;
81740947Sbostic 	char endch;
81840947Sbostic 
81940947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
82040947Sbostic 
82149868Sbostic 	new = palloc(N_SIZE, f_size);
82255710Sbostic 	endch = 'c';
82358066Smarc 	new->o_data = find_parsenum(new, "-size", arg, &endch);
82440947Sbostic 	if (endch == 'c')
82540947Sbostic 		divsize = 0;
82659612Sbostic 	return (new);
82740947Sbostic }
82840947Sbostic 
82940947Sbostic /*
83040947Sbostic  * -type c functions --
83140947Sbostic  *
83267575Spendry  *	True if the type of the file is c, where c is b, c, d, p, f or w
83367575Spendry  *	for block special file, character special file, directory, FIFO,
83467575Spendry  *	regular file or whiteout respectively.
83540947Sbostic  */
83659612Sbostic int
83740947Sbostic f_type(plan, entry)
83840947Sbostic 	PLAN *plan;
83940947Sbostic 	FTSENT *entry;
84040947Sbostic {
84159612Sbostic 	return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
84240947Sbostic }
84340947Sbostic 
84440947Sbostic PLAN *
84540947Sbostic c_type(typestring)
84640947Sbostic 	char *typestring;
84740947Sbostic {
84840947Sbostic 	PLAN *new;
84940947Sbostic 	mode_t  mask;
85040947Sbostic 
85140947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
85240947Sbostic 
85340947Sbostic 	switch (typestring[0]) {
85440947Sbostic 	case 'b':
85540947Sbostic 		mask = S_IFBLK;
85640947Sbostic 		break;
85740947Sbostic 	case 'c':
85840947Sbostic 		mask = S_IFCHR;
85940947Sbostic 		break;
86040947Sbostic 	case 'd':
86140947Sbostic 		mask = S_IFDIR;
86240947Sbostic 		break;
86340947Sbostic 	case 'f':
86440947Sbostic 		mask = S_IFREG;
86540947Sbostic 		break;
86640947Sbostic 	case 'l':
86740947Sbostic 		mask = S_IFLNK;
86840947Sbostic 		break;
86940947Sbostic 	case 'p':
87040947Sbostic 		mask = S_IFIFO;
87140947Sbostic 		break;
87240947Sbostic 	case 's':
87340947Sbostic 		mask = S_IFSOCK;
87440947Sbostic 		break;
87567593Smckusick #ifdef FTS_WHITEOUT
87667575Spendry 	case 'w':
87767575Spendry 		mask = S_IFWHT;
87867575Spendry 		ftsoptions |= FTS_WHITEOUT;
87967575Spendry 		break;
88067593Smckusick #endif /* FTS_WHITEOUT */
88140947Sbostic 	default:
88259612Sbostic 		errx(1, "-type: %s: unknown type", typestring);
88340947Sbostic 	}
88440947Sbostic 
88549868Sbostic 	new = palloc(N_TYPE, f_type);
88640947Sbostic 	new->m_data = mask;
88759612Sbostic 	return (new);
88840947Sbostic }
88940947Sbostic 
89040947Sbostic /*
89140947Sbostic  * -user uname functions --
89240947Sbostic  *
89340947Sbostic  *	True if the file belongs to the user uname.  If uname is numeric and
89440947Sbostic  *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
89540947Sbostic  *	return a valid user name, uname is taken as a user ID.
89640947Sbostic  */
89759612Sbostic int
89840947Sbostic f_user(plan, entry)
89940947Sbostic 	PLAN *plan;
90040947Sbostic 	FTSENT *entry;
90140947Sbostic {
90259612Sbostic 	return (entry->fts_statp->st_uid == plan->u_data);
90340947Sbostic }
90440947Sbostic 
90540947Sbostic PLAN *
90640947Sbostic c_user(username)
90740947Sbostic 	char *username;
90840947Sbostic {
90940947Sbostic 	PLAN *new;
91040947Sbostic 	struct passwd *p;
91140947Sbostic 	uid_t uid;
91240947Sbostic 
91340947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
91440947Sbostic 
91540947Sbostic 	p = getpwnam(username);
91640947Sbostic 	if (p == NULL) {
91740947Sbostic 		uid = atoi(username);
91840947Sbostic 		if (uid == 0 && username[0] != '0')
91959612Sbostic 			errx(1, "-user: %s: no such user", username);
92040947Sbostic 	} else
92140947Sbostic 		uid = p->pw_uid;
92240947Sbostic 
92349868Sbostic 	new = palloc(N_USER, f_user);
92440947Sbostic 	new->u_data = uid;
92559612Sbostic 	return (new);
92640947Sbostic }
92740947Sbostic 
92840947Sbostic /*
92940947Sbostic  * -xdev functions --
93040947Sbostic  *
93140947Sbostic  *	Always true, causes find not to decend past directories that have a
93240947Sbostic  *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
93340947Sbostic  */
93440947Sbostic PLAN *
93540947Sbostic c_xdev()
93640947Sbostic {
93742275Sbostic 	ftsoptions |= FTS_XDEV;
93840947Sbostic 
93959612Sbostic 	return (palloc(N_XDEV, f_always_true));
94040947Sbostic }
94140947Sbostic 
94240947Sbostic /*
94340947Sbostic  * ( expression ) functions --
94440947Sbostic  *
94540947Sbostic  *	True if expression is true.
94640947Sbostic  */
94759612Sbostic int
94840947Sbostic f_expr(plan, entry)
94940947Sbostic 	PLAN *plan;
95040947Sbostic 	FTSENT *entry;
95140947Sbostic {
95240947Sbostic 	register PLAN *p;
95340947Sbostic 	register int state;
95440947Sbostic 
95540947Sbostic 	for (p = plan->p_data[0];
95640947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
95759612Sbostic 	return (state);
95840947Sbostic }
95940947Sbostic 
96040947Sbostic /*
96149864Sbostic  * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
96240947Sbostic  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
96349864Sbostic  * to a N_EXPR node containing the expression and the ')' node is discarded.
96440947Sbostic  */
96540947Sbostic PLAN *
96640947Sbostic c_openparen()
96740947Sbostic {
96859612Sbostic 	return (palloc(N_OPENPAREN, (int (*)())-1));
96940947Sbostic }
97040947Sbostic 
97140947Sbostic PLAN *
97240947Sbostic c_closeparen()
97340947Sbostic {
97459612Sbostic 	return (palloc(N_CLOSEPAREN, (int (*)())-1));
97540947Sbostic }
97640947Sbostic 
97740947Sbostic /*
97840947Sbostic  * ! expression functions --
97940947Sbostic  *
98040947Sbostic  *	Negation of a primary; the unary NOT operator.
98140947Sbostic  */
98259612Sbostic int
98340947Sbostic f_not(plan, entry)
98440947Sbostic 	PLAN *plan;
98540947Sbostic 	FTSENT *entry;
98640947Sbostic {
98740947Sbostic 	register PLAN *p;
98840947Sbostic 	register int state;
98940947Sbostic 
99040947Sbostic 	for (p = plan->p_data[0];
99140947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
99259612Sbostic 	return (!state);
99340947Sbostic }
99440947Sbostic 
99540947Sbostic PLAN *
99640947Sbostic c_not()
99740947Sbostic {
99859612Sbostic 	return (palloc(N_NOT, f_not));
99940947Sbostic }
100040947Sbostic 
100140947Sbostic /*
100240947Sbostic  * expression -o expression functions --
100340947Sbostic  *
100440947Sbostic  *	Alternation of primaries; the OR operator.  The second expression is
100540947Sbostic  * not evaluated if the first expression is true.
100640947Sbostic  */
100759612Sbostic int
100840947Sbostic f_or(plan, entry)
100940947Sbostic 	PLAN *plan;
101040947Sbostic 	FTSENT *entry;
101140947Sbostic {
101240947Sbostic 	register PLAN *p;
101340947Sbostic 	register int state;
101440947Sbostic 
101540947Sbostic 	for (p = plan->p_data[0];
101640947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
101740947Sbostic 
101840947Sbostic 	if (state)
101959612Sbostic 		return (1);
102040947Sbostic 
102140947Sbostic 	for (p = plan->p_data[1];
102240947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
102359612Sbostic 	return (state);
102440947Sbostic }
102540947Sbostic 
102640947Sbostic PLAN *
102740947Sbostic c_or()
102840947Sbostic {
102959612Sbostic 	return (palloc(N_OR, f_or));
103049868Sbostic }
103149868Sbostic 
103249868Sbostic static PLAN *
103349868Sbostic palloc(t, f)
103449868Sbostic 	enum ntype t;
103559612Sbostic 	int (*f) __P((PLAN *, FTSENT *));
103649868Sbostic {
103740947Sbostic 	PLAN *new;
103840947Sbostic 
103966585Sbostic 	if ((new = malloc(sizeof(PLAN))) == NULL)
104066585Sbostic 		err(1, NULL);
104166585Sbostic 	new->type = t;
104266585Sbostic 	new->eval = f;
104366585Sbostic 	new->flags = 0;
104466585Sbostic 	new->next = NULL;
104566585Sbostic 	return (new);
104640947Sbostic }
1047