xref: /csrg-svn/usr.bin/find/function.c (revision 49864)
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*49864Sbostic static char sccsid[] = "@(#)function.c	5.15 (Berkeley) 05/23/91";
1340947Sbostic #endif /* not lint */
1440947Sbostic 
1545868Sbostic #include <sys/param.h>
1640947Sbostic #include <sys/stat.h>
1740947Sbostic #include <sys/wait.h>
1840947Sbostic #include <sys/mount.h>
1947192Sbostic #include <errno.h>
2040947Sbostic #include <grp.h>
2140947Sbostic #include <pwd.h>
2240947Sbostic #include <fts.h>
2340947Sbostic #include <unistd.h>
2440947Sbostic #include <tzfile.h>
2540947Sbostic #include <stdio.h>
2647192Sbostic #include <stdlib.h>
2742047Sbostic #include <string.h>
2840947Sbostic #include "find.h"
2940947Sbostic 
3040947Sbostic #define	FIND_EQUAL	0
3140947Sbostic #define	FIND_LESSTHAN	1
3240947Sbostic #define	FIND_GREATER	2
3340947Sbostic 
3440947Sbostic #define	COMPARE(a, b) { \
3540947Sbostic 	switch(plan->flags) { \
3640947Sbostic 	case FIND_EQUAL: \
3740947Sbostic 		return(a == b); \
3840947Sbostic 	case FIND_LESSTHAN: \
3940947Sbostic 		return(a < b); \
4040947Sbostic 	case FIND_GREATER: \
4140947Sbostic 		return(a > b); \
4240947Sbostic 	} \
4342255Sbostic 	return(0); \
4440947Sbostic }
4540947Sbostic 
4640947Sbostic #define NEW(t, f) { \
4740947Sbostic 	new = (PLAN *)emalloc(sizeof(PLAN)); \
4840947Sbostic 	new->type = t; \
4940947Sbostic 	new->eval = f; \
5040947Sbostic 	new->flags = 0; \
5140947Sbostic 	new->next = NULL; \
5240947Sbostic }
5340947Sbostic 
5440947Sbostic /*
5540947Sbostic  * find_parsenum --
5640947Sbostic  *	Parse a string of the form [+-]# and return the value.
5740947Sbostic  */
5840947Sbostic long
5940947Sbostic find_parsenum(plan, option, str, endch)
6040947Sbostic 	PLAN *plan;
6140947Sbostic 	char *option, *str, *endch;
6240947Sbostic {
6340947Sbostic 	long value;
6440947Sbostic 	char *endchar;		/* pointer to character ending conversion */
6545615Sbostic 	void bad_arg();
6640947Sbostic 
6740947Sbostic 	/* determine comparison from leading + or - */
6840947Sbostic 	switch(*str) {
6940947Sbostic 	case '+':
7040947Sbostic 		++str;
7140947Sbostic 		plan->flags = FIND_GREATER;
7240947Sbostic 		break;
7340947Sbostic 	case '-':
7440947Sbostic 		++str;
7540947Sbostic 		plan->flags = FIND_LESSTHAN;
7640947Sbostic 		break;
7740947Sbostic 	default:
7840947Sbostic 		plan->flags = FIND_EQUAL;
7940947Sbostic 		break;
8040947Sbostic 	}
8140947Sbostic 
8240947Sbostic 	/*
8340947Sbostic 	 * convert the string with strtol().  Note, if strtol() returns zero
8440947Sbostic 	 * and endchar points to the beginning of the string we know we have
8540947Sbostic 	 * a syntax error.
8640947Sbostic 	 */
8740947Sbostic 	value = strtol(str, &endchar, 10);
8840947Sbostic 	if (!value && endchar == str ||
8940947Sbostic 	    endchar[0] && (!endch || endchar[0] != *endch))
9040947Sbostic 		bad_arg(option, "illegal numeric value");
9140947Sbostic 	if (endch)
9240947Sbostic 		*endch = endchar[0];
9340947Sbostic 	return(value);
9440947Sbostic }
9540947Sbostic 
9640947Sbostic /*
9740947Sbostic  * -atime n functions --
9840947Sbostic  *
9940947Sbostic  *	True if the difference between the file access time and the
10040947Sbostic  *	current time is n 24 hour periods.
10140947Sbostic  *
10240947Sbostic  */
10340947Sbostic f_atime(plan, entry)
10440947Sbostic 	PLAN *plan;
10540947Sbostic 	FTSENT *entry;
10640947Sbostic {
10740947Sbostic 	extern time_t now;
10840947Sbostic 
10942255Sbostic 	COMPARE((now - entry->fts_statb.st_atime +
11042255Sbostic 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
11140947Sbostic }
11240947Sbostic 
11340947Sbostic PLAN *
11440947Sbostic c_atime(arg)
11540947Sbostic 	char *arg;
11640947Sbostic {
11740947Sbostic 	PLAN *new;
11840947Sbostic 
11940947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
12040947Sbostic 
121*49864Sbostic 	NEW(N_ATIME, f_atime);
12240947Sbostic 	new->t_data = find_parsenum(new, "-atime", arg, (char *)NULL);
12340947Sbostic 	return(new);
12440947Sbostic }
12540947Sbostic /*
12640947Sbostic  * -ctime n functions --
12740947Sbostic  *
12840947Sbostic  *	True if the difference between the last change of file
12940947Sbostic  *	status information and the current time is n 24 hour periods.
13040947Sbostic  */
13140947Sbostic f_ctime(plan, entry)
13240947Sbostic 	PLAN *plan;
13340947Sbostic 	FTSENT *entry;
13440947Sbostic {
13540947Sbostic 	extern time_t now;
13640947Sbostic 
13742255Sbostic 	COMPARE((now - entry->fts_statb.st_ctime +
13842255Sbostic 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
13940947Sbostic }
14040947Sbostic 
14140947Sbostic PLAN *
14240947Sbostic c_ctime(arg)
14340947Sbostic 	char *arg;
14440947Sbostic {
14540947Sbostic 	PLAN *new;
14640947Sbostic 
14740947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
14840947Sbostic 
149*49864Sbostic 	NEW(N_CTIME, f_ctime);
15040947Sbostic 	new->t_data = find_parsenum(new, "-ctime", arg, (char *)NULL);
15140947Sbostic 	return(new);
15240947Sbostic }
15340947Sbostic 
15440947Sbostic /*
15540947Sbostic  * -depth functions --
15640947Sbostic  *
15740947Sbostic  *	Always true, causes descent of the directory hierarchy to be done
15840947Sbostic  *	so that all entries in a directory are acted on before the directory
15940947Sbostic  *	itself.
16040947Sbostic  */
16140947Sbostic /* ARGSUSED */
16240947Sbostic f_always_true(plan, entry)
16340947Sbostic 	PLAN *plan;
16440947Sbostic 	FTSENT *entry;
16540947Sbostic {
16642255Sbostic 	return(1);
16740947Sbostic }
16840947Sbostic 
16940947Sbostic PLAN *
17040947Sbostic c_depth()
17140947Sbostic {
17240947Sbostic 	PLAN *new;
17340947Sbostic 
17445615Sbostic 	isdepth = 1;
17540947Sbostic 
176*49864Sbostic 	NEW(N_DEPTH, f_always_true);
17740947Sbostic 	return(new);
17840947Sbostic }
17940947Sbostic 
18040947Sbostic /*
18140947Sbostic  * [-exec | -ok] utility [arg ... ] ; functions --
18240947Sbostic  *
18340947Sbostic  *	True if the executed utility returns a zero value as exit status.
18440947Sbostic  *	The end of the primary expression is delimited by a semicolon.  If
18540947Sbostic  *	"{}" occurs anywhere, it gets replaced by the current pathname.
18640947Sbostic  *	The current directory for the execution of utility is the same as
18740947Sbostic  *	the current directory when the find utility was started.
18840947Sbostic  *
18940947Sbostic  *	The primary -ok is different in that it requests affirmation of the
19040947Sbostic  *	user before executing the utility.
19140947Sbostic  */
19240947Sbostic f_exec(plan, entry)
19340947Sbostic 	register PLAN *plan;
19440947Sbostic 	FTSENT *entry;
19540947Sbostic {
19640947Sbostic 	register int cnt;
19747192Sbostic 	pid_t pid;
19847192Sbostic 	int status;
19945616Sbostic 	void brace_subst();
20040947Sbostic 
20140947Sbostic 	for (cnt = 0; plan->e_argv[cnt]; ++cnt)
20240947Sbostic 		if (plan->e_len[cnt])
20345616Sbostic 			brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
20445404Sbostic 			    entry->fts_accpath, plan->e_len[cnt]);
20540947Sbostic 
20645616Sbostic 	if (plan->flags && !queryuser(plan->e_argv))
20742255Sbostic 		return(0);
20840947Sbostic 
20940947Sbostic 	switch(pid = vfork()) {
21040947Sbostic 	case -1:
21147192Sbostic 		error("fork", errno);
21240947Sbostic 		exit(1);
21340947Sbostic 		/* NOTREACHED */
21440947Sbostic 	case 0:
21540947Sbostic 		execvp(plan->e_argv[0], plan->e_argv);
21647192Sbostic 		error(plan->e_argv[0], errno);
21740947Sbostic 		exit(1);
21840947Sbostic 		/* NOTREACHED */
21940947Sbostic 	}
22047192Sbostic 	pid = waitpid(pid, &status, 0);
22147192Sbostic 	return(pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
22240947Sbostic }
22340947Sbostic 
22440947Sbostic /*
22540947Sbostic  * c_exec --
22640947Sbostic  *	build three parallel arrays, one with pointers to the strings passed
22740947Sbostic  *	on the command line, one with (possibly duplicated) pointers to the
22840947Sbostic  *	argv array, and one with integer values that are lengths of the
22940947Sbostic  *	strings, but also flags meaning that the string has to be massaged.
23040947Sbostic  */
23140947Sbostic PLAN *
23240947Sbostic c_exec(argvp, isok)
23340947Sbostic 	char ***argvp;
23440947Sbostic 	int isok;
23540947Sbostic {
23640947Sbostic 	PLAN *new;			/* node returned */
23740947Sbostic 	register int cnt;
23840947Sbostic 	register char **argv, **ap, *p;
23945615Sbostic 	void bad_arg();
24040947Sbostic 
24145615Sbostic 	if (!isrelative)
24245404Sbostic 		ftsoptions |= FTS_NOCHDIR;
24347192Sbostic 	isoutput = 1;
24440947Sbostic 
245*49864Sbostic 	NEW(N_EXEC, f_exec);
24640947Sbostic 	new->flags = isok;
24740947Sbostic 
24840947Sbostic 	for (ap = argv = *argvp;; ++ap) {
24940947Sbostic 		if (!*ap)
25040947Sbostic 			bad_arg(isok ? "-ok" : "-exec", "no terminating \";\"");
25140947Sbostic 		if (**ap == ';')
25240947Sbostic 			break;
25340947Sbostic 	}
25440947Sbostic 
25540947Sbostic 	cnt = ap - *argvp + 1;
25640947Sbostic 	new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
25740947Sbostic 	new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
25845868Sbostic 	new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
25940947Sbostic 
26040947Sbostic 	for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
26140947Sbostic 		new->e_orig[cnt] = *argv;
26240947Sbostic 		for (p = *argv; *p; ++p)
26340947Sbostic 			if (p[0] == '{' && p[1] == '}') {
26445868Sbostic 				new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
26545868Sbostic 				new->e_len[cnt] = MAXPATHLEN;
26640947Sbostic 				break;
26740947Sbostic 			}
26840947Sbostic 		if (!*p) {
26940947Sbostic 			new->e_argv[cnt] = *argv;
27040947Sbostic 			new->e_len[cnt] = 0;
27140947Sbostic 		}
27240947Sbostic 	}
27340947Sbostic 	new->e_argv[cnt] = new->e_orig[cnt] = NULL;
27440947Sbostic 
27540947Sbostic 	*argvp = argv + 1;
27640947Sbostic 	return(new);
27740947Sbostic }
27840947Sbostic 
27940947Sbostic /*
28040947Sbostic  * -follow functions --
28140947Sbostic  *
28240947Sbostic  *	Always true, causes symbolic links to be followed on a global
28340947Sbostic  *	basis.
28440947Sbostic  */
28540947Sbostic PLAN *
28640947Sbostic c_follow()
28740947Sbostic {
28840947Sbostic 	PLAN *new;
28940947Sbostic 
29040947Sbostic 	ftsoptions &= ~FTS_PHYSICAL;
29140947Sbostic 	ftsoptions |= FTS_LOGICAL;
29240947Sbostic 
293*49864Sbostic 	NEW(N_FOLLOW, f_always_true);
29440947Sbostic 	return(new);
29540947Sbostic }
29640947Sbostic 
29740947Sbostic /*
29840947Sbostic  * -fstype functions --
29940947Sbostic  *
30040947Sbostic  *	True if the file is of a certain type.
30140947Sbostic  */
30240947Sbostic f_fstype(plan, entry)
30340947Sbostic 	PLAN *plan;
30440947Sbostic 	FTSENT *entry;
30540947Sbostic {
30642275Sbostic 	static dev_t curdev;	/* need a guaranteed illegal dev value */
30742275Sbostic 	static int first = 1;
30840947Sbostic 	struct statfs sb;
30942255Sbostic 	static short val;
31045626Sbostic 	char *p, save[2];
31140947Sbostic 
31240947Sbostic 	/* only check when we cross mount point */
31342275Sbostic 	if (first || curdev != entry->fts_statb.st_dev) {
31443856Sbostic 		curdev = entry->fts_statb.st_dev;
31545626Sbostic 
31645626Sbostic 		/*
31745626Sbostic 		 * Statfs follows symlinks; find wants the link's file system,
31845626Sbostic 		 * not where it points.
31945626Sbostic 		 */
32045626Sbostic 		if (entry->fts_info == FTS_SL ||
32145626Sbostic 		    entry->fts_info == FTS_SLNONE) {
32245626Sbostic 			if (p = rindex(entry->fts_accpath, '/'))
32345626Sbostic 				++p;
32445626Sbostic 			else
32545626Sbostic 				p = entry->fts_accpath;
32645626Sbostic 			save[0] = p[0];
32745626Sbostic 			p[0] = '.';
32845626Sbostic 			save[1] = p[1];
32945626Sbostic 			p[1] = '\0';
33045626Sbostic 
33145626Sbostic 		} else
33245626Sbostic 			p = NULL;
33345626Sbostic 
33442255Sbostic 		if (statfs(entry->fts_accpath, &sb)) {
33547192Sbostic 			error(entry->fts_accpath, errno);
33640947Sbostic 			exit(1);
33740947Sbostic 		}
33845626Sbostic 
33945626Sbostic 		if (p) {
34045626Sbostic 			p[0] = save[0];
34145626Sbostic 			p[1] = save[1];
34245626Sbostic 		}
34345626Sbostic 
34442275Sbostic 		first = 0;
34542255Sbostic 		val = plan->flags == MOUNT_NONE ? sb.f_flags : sb.f_type;
34640947Sbostic 	}
34742255Sbostic 	return(plan->flags == MOUNT_NONE ?
34842255Sbostic 	    val & MNT_LOCAL : val == plan->flags);
34940947Sbostic }
35040947Sbostic 
35140947Sbostic PLAN *
35240947Sbostic c_fstype(arg)
35340947Sbostic 	char *arg;
35440947Sbostic {
35540947Sbostic 	register PLAN *new;
35640947Sbostic 
35740947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
35840947Sbostic 
359*49864Sbostic 	NEW(N_FSTYPE, f_fstype);
36040947Sbostic 	switch(*arg) {
36142255Sbostic 	case 'l':
36242255Sbostic 		if (!strcmp(arg, "local")) {
36342255Sbostic 			new->flags = MOUNT_NONE;
36442255Sbostic 			return(new);
36542255Sbostic 		}
36642255Sbostic 		break;
36740947Sbostic 	case 'm':
36840947Sbostic 		if (!strcmp(arg, "mfs")) {
36940947Sbostic 			new->flags = MOUNT_MFS;
37040947Sbostic 			return(new);
37140947Sbostic 		}
37240947Sbostic 		break;
37340947Sbostic 	case 'n':
37440947Sbostic 		if (!strcmp(arg, "nfs")) {
37540947Sbostic 			new->flags = MOUNT_NFS;
37640947Sbostic 			return(new);
37740947Sbostic 		}
37840947Sbostic 		break;
37940947Sbostic 	case 'p':
38040947Sbostic 		if (!strcmp(arg, "pc")) {
38140947Sbostic 			new->flags = MOUNT_PC;
38240947Sbostic 			return(new);
38340947Sbostic 		}
38440947Sbostic 		break;
38540947Sbostic 	case 'u':
38640947Sbostic 		if (!strcmp(arg, "ufs")) {
38740947Sbostic 			new->flags = MOUNT_UFS;
38840947Sbostic 			return(new);
38940947Sbostic 		}
39040947Sbostic 		break;
39140947Sbostic 	}
39240947Sbostic 	(void)fprintf(stderr, "find: unknown file type %s.\n", arg);
39340947Sbostic 	exit(1);
39440947Sbostic 	/* NOTREACHED */
39540947Sbostic }
39640947Sbostic 
39740947Sbostic /*
39840947Sbostic  * -group gname functions --
39940947Sbostic  *
40040947Sbostic  *	True if the file belongs to the group gname.  If gname is numeric and
40140947Sbostic  *	an equivalent of the getgrnam() function does not return a valid group
40240947Sbostic  *	name, gname is taken as a group ID.
40340947Sbostic  */
40440947Sbostic f_group(plan, entry)
40540947Sbostic 	PLAN *plan;
40640947Sbostic 	FTSENT *entry;
40740947Sbostic {
40842255Sbostic 	return(entry->fts_statb.st_gid == plan->g_data);
40940947Sbostic }
41040947Sbostic 
41140947Sbostic PLAN *
41240947Sbostic c_group(gname)
41340947Sbostic 	char *gname;
41440947Sbostic {
41540947Sbostic 	PLAN *new;
41640947Sbostic 	struct group *g;
41740947Sbostic 	gid_t gid;
41845615Sbostic 	void bad_arg();
41940947Sbostic 
42040947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
42140947Sbostic 
42240947Sbostic 	g = getgrnam(gname);
42340947Sbostic 	if (g == NULL) {
42440947Sbostic 		gid = atoi(gname);
42540947Sbostic 		if (gid == 0 && gname[0] != '0')
42640947Sbostic 			bad_arg("-group", "no such group");
42740947Sbostic 	} else
42840947Sbostic 		gid = g->gr_gid;
42940947Sbostic 
430*49864Sbostic 	NEW(N_GROUP, f_group);
43140947Sbostic 	new->g_data = gid;
43240947Sbostic 	return(new);
43340947Sbostic }
43440947Sbostic 
43540947Sbostic /*
43640947Sbostic  * -inum n functions --
43740947Sbostic  *
43840947Sbostic  *	True if the file has inode # n.
43940947Sbostic  */
44040947Sbostic f_inum(plan, entry)
44140947Sbostic 	PLAN *plan;
44240947Sbostic 	FTSENT *entry;
44340947Sbostic {
44442255Sbostic 	COMPARE(entry->fts_statb.st_ino, plan->i_data);
44540947Sbostic }
44640947Sbostic 
44740947Sbostic PLAN *
44840947Sbostic c_inum(arg)
44940947Sbostic 	char *arg;
45040947Sbostic {
45140947Sbostic 	PLAN *new;
45240947Sbostic 
45340947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
45440947Sbostic 
455*49864Sbostic 	NEW(N_INUM, f_inum);
45640947Sbostic 	new->i_data = find_parsenum(new, "-inum", arg, (char *)NULL);
45740947Sbostic 	return(new);
45840947Sbostic }
45940947Sbostic 
46040947Sbostic /*
46140947Sbostic  * -links n functions --
46240947Sbostic  *
46340947Sbostic  *	True if the file has n links.
46440947Sbostic  */
46540947Sbostic f_links(plan, entry)
46640947Sbostic 	PLAN *plan;
46740947Sbostic 	FTSENT *entry;
46840947Sbostic {
46942255Sbostic 	COMPARE(entry->fts_statb.st_nlink, plan->l_data);
47040947Sbostic }
47140947Sbostic 
47240947Sbostic PLAN *
47340947Sbostic c_links(arg)
47440947Sbostic 	char *arg;
47540947Sbostic {
47640947Sbostic 	PLAN *new;
47740947Sbostic 
47840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
47940947Sbostic 
480*49864Sbostic 	NEW(N_LINKS, f_links);
48145616Sbostic 	new->l_data = (nlink_t)find_parsenum(new, "-links", arg, (char *)NULL);
48240947Sbostic 	return(new);
48340947Sbostic }
48440947Sbostic 
48540947Sbostic /*
48640947Sbostic  * -ls functions --
48740947Sbostic  *
48840947Sbostic  *	Always true - prints the current entry to stdout in "ls" format.
48940947Sbostic  */
49040947Sbostic /* ARGSUSED */
49140947Sbostic f_ls(plan, entry)
49240947Sbostic 	PLAN *plan;
49340947Sbostic 	FTSENT *entry;
49440947Sbostic {
49545615Sbostic 	void printlong();
49645615Sbostic 
49742255Sbostic 	printlong(entry->fts_path, entry->fts_accpath, &entry->fts_statb);
49842255Sbostic 	return(1);
49940947Sbostic }
50040947Sbostic 
50140947Sbostic PLAN *
50240947Sbostic c_ls()
50340947Sbostic {
50440947Sbostic 	PLAN *new;
50540947Sbostic 
50640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
50745615Sbostic 	isoutput = 1;
50840947Sbostic 
509*49864Sbostic 	NEW(N_LS, f_ls);
51040947Sbostic 	return(new);
51140947Sbostic }
51240947Sbostic 
51340947Sbostic /*
51440947Sbostic  * -name functions --
51540947Sbostic  *
51640947Sbostic  *	True if the basename of the filename being examined
51740947Sbostic  *	matches pattern using Pattern Matching Notation S3.14
51840947Sbostic  */
51940947Sbostic f_name(plan, entry)
52040947Sbostic 	PLAN *plan;
52140947Sbostic 	FTSENT *entry;
52240947Sbostic {
52342255Sbostic 	return(fnmatch(plan->c_data, entry->fts_name, FNM_QUOTE));
52440947Sbostic }
52540947Sbostic 
52640947Sbostic PLAN *
52740947Sbostic c_name(pattern)
52840947Sbostic 	char *pattern;
52940947Sbostic {
53040947Sbostic 	PLAN *new;
53140947Sbostic 
532*49864Sbostic 	NEW(N_NAME, f_name);
53340947Sbostic 	new->c_data = pattern;
53440947Sbostic 	return(new);
53540947Sbostic }
53640947Sbostic 
53740947Sbostic /*
53840947Sbostic  * -newer file functions --
53940947Sbostic  *
54040947Sbostic  *	True if the current file has been modified more recently
54140947Sbostic  *	then the modification time of the file named by the pathname
54240947Sbostic  *	file.
54340947Sbostic  */
54440947Sbostic f_newer(plan, entry)
54540947Sbostic 	PLAN *plan;
54640947Sbostic 	FTSENT *entry;
54740947Sbostic {
54842255Sbostic 	return(entry->fts_statb.st_mtime > plan->t_data);
54940947Sbostic }
55040947Sbostic 
55140947Sbostic PLAN *
55240947Sbostic c_newer(filename)
55340947Sbostic 	char *filename;
55440947Sbostic {
55540947Sbostic 	PLAN *new;
55640947Sbostic 	struct stat sb;
55740947Sbostic 
55840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
55940947Sbostic 
56040947Sbostic 	if (stat(filename, &sb)) {
56147192Sbostic 		error(filename, errno);
56240947Sbostic 		exit(1);
56340947Sbostic 	}
564*49864Sbostic 	NEW(N_NEWER, f_newer);
56540947Sbostic 	new->t_data = sb.st_mtime;
56640947Sbostic 	return(new);
56740947Sbostic }
56840947Sbostic 
56940947Sbostic /*
57040947Sbostic  * -nogroup functions --
57140947Sbostic  *
57240947Sbostic  *	True if file belongs to a user ID for which the equivalent
57340947Sbostic  *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
57440947Sbostic  */
57540947Sbostic /* ARGSUSED */
57640947Sbostic f_nogroup(plan, entry)
57740947Sbostic 	PLAN *plan;
57840947Sbostic 	FTSENT *entry;
57940947Sbostic {
58045615Sbostic 	char *group_from_gid();
58145615Sbostic 
58245615Sbostic 	return(group_from_gid(entry->fts_statb.st_gid, 1) ? 1 : 0);
58340947Sbostic }
58440947Sbostic 
58540947Sbostic PLAN *
58640947Sbostic c_nogroup()
58740947Sbostic {
58840947Sbostic 	PLAN *new;
58940947Sbostic 
59040947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
59140947Sbostic 
592*49864Sbostic 	NEW(N_NOGROUP, f_nogroup);
59340947Sbostic 	return(new);
59440947Sbostic }
59540947Sbostic 
59640947Sbostic /*
59740947Sbostic  * -nouser functions --
59840947Sbostic  *
59940947Sbostic  *	True if file belongs to a user ID for which the equivalent
60040947Sbostic  *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
60140947Sbostic  */
60240947Sbostic /* ARGSUSED */
60340947Sbostic f_nouser(plan, entry)
60440947Sbostic 	PLAN *plan;
60540947Sbostic 	FTSENT *entry;
60640947Sbostic {
60745615Sbostic 	char *user_from_uid();
60845615Sbostic 
60945615Sbostic 	return(user_from_uid(entry->fts_statb.st_uid, 1) ? 1 : 0);
61040947Sbostic }
61140947Sbostic 
61240947Sbostic PLAN *
61340947Sbostic c_nouser()
61440947Sbostic {
61540947Sbostic 	PLAN *new;
61640947Sbostic 
61740947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
61840947Sbostic 
619*49864Sbostic 	NEW(N_NOUSER, f_nouser);
62040947Sbostic 	return(new);
62140947Sbostic }
62240947Sbostic 
62340947Sbostic /*
62440947Sbostic  * -perm functions --
62540947Sbostic  *
62640947Sbostic  *	The mode argument is used to represent file mode bits.  If it starts
62740947Sbostic  *	with a leading digit, it's treated as an octal mode, otherwise as a
62840947Sbostic  *	symbolic mode.
62940947Sbostic  */
63040947Sbostic f_perm(plan, entry)
63140947Sbostic 	PLAN *plan;
63240947Sbostic 	FTSENT *entry;
63340947Sbostic {
63440947Sbostic 	mode_t mode;
63540947Sbostic 
63642255Sbostic 	mode = entry->fts_statb.st_mode &
63740947Sbostic 	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
63840947Sbostic 	if (plan->flags)
63940947Sbostic 		return((plan->m_data | mode) == mode);
64040947Sbostic 	else
64140947Sbostic 		return(mode == plan->m_data);
64240947Sbostic 	/* NOTREACHED */
64340947Sbostic }
64440947Sbostic 
64540947Sbostic PLAN *
64640947Sbostic c_perm(perm)
64740947Sbostic 	char *perm;
64840947Sbostic {
64940947Sbostic 	PLAN *new;
65047192Sbostic 	mode_t *set;
65145615Sbostic 	void bad_arg();
65240947Sbostic 
65340947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
65440947Sbostic 
655*49864Sbostic 	NEW(N_PERM, f_perm);
65640947Sbostic 
65740947Sbostic 	if (*perm == '-') {
65840947Sbostic 		new->flags = 1;
65940947Sbostic 		++perm;
66040947Sbostic 	}
66140947Sbostic 
66244764Strent 	if ((set = setmode(perm)) == NULL)
66340947Sbostic 		bad_arg("-perm", "illegal mode string");
66440947Sbostic 
66544764Strent 	new->m_data = getmode(set, 0);
66640947Sbostic 	return(new);
66740947Sbostic }
66840947Sbostic 
66940947Sbostic /*
67040947Sbostic  * -print functions --
67140947Sbostic  *
67240947Sbostic  *	Always true, causes the current pathame to be written to
67340947Sbostic  *	standard output.
67440947Sbostic  */
67540947Sbostic /* ARGSUSED */
67640947Sbostic f_print(plan, entry)
67740947Sbostic 	PLAN *plan;
67840947Sbostic 	FTSENT *entry;
67940947Sbostic {
68042255Sbostic 	(void)printf("%s\n", entry->fts_path);
68142255Sbostic 	return(1);
68240947Sbostic }
68340947Sbostic 
68440947Sbostic PLAN *
68540947Sbostic c_print()
68640947Sbostic {
68740947Sbostic 	PLAN *new;
68840947Sbostic 
68945615Sbostic 	isoutput = 1;
69040947Sbostic 
691*49864Sbostic 	NEW(N_PRINT, f_print);
69240947Sbostic 	return(new);
69340947Sbostic }
69440947Sbostic 
69540947Sbostic /*
69640947Sbostic  * -prune functions --
69740947Sbostic  *
69840947Sbostic  *	Prune a portion of the hierarchy.
69940947Sbostic  */
70040947Sbostic /* ARGSUSED */
70140947Sbostic f_prune(plan, entry)
70240947Sbostic 	PLAN *plan;
70340947Sbostic 	FTSENT *entry;
70440947Sbostic {
70540947Sbostic 	extern FTS *tree;
70640947Sbostic 
70745615Sbostic 	if (fts_set(tree, entry, FTS_SKIP)) {
70847192Sbostic 		error(entry->fts_path, errno);
70940947Sbostic 		exit(1);
71040947Sbostic 	}
71142255Sbostic 	return(1);
71240947Sbostic }
71340947Sbostic 
71440947Sbostic PLAN *
71540947Sbostic c_prune()
71640947Sbostic {
71740947Sbostic 	PLAN *new;
71840947Sbostic 
719*49864Sbostic 	NEW(N_PRUNE, f_prune);
72040947Sbostic 	return(new);
72140947Sbostic }
72240947Sbostic 
72340947Sbostic /*
72440947Sbostic  * -size n[c] functions --
72540947Sbostic  *
72640947Sbostic  *	True if the file size in bytes, divided by an implementation defined
72740947Sbostic  *	value and rounded up to the next integer, is n.  If n is followed by
72840947Sbostic  *	a c, the size is in bytes.
72940947Sbostic  */
73040947Sbostic #define	FIND_SIZE	512
73140947Sbostic static int divsize = 1;
73240947Sbostic 
73340947Sbostic f_size(plan, entry)
73440947Sbostic 	PLAN *plan;
73540947Sbostic 	FTSENT *entry;
73640947Sbostic {
73740947Sbostic 	off_t size;
73840947Sbostic 
73942255Sbostic 	size = divsize ? (entry->fts_statb.st_size + FIND_SIZE - 1) /
74042255Sbostic 	    FIND_SIZE : entry->fts_statb.st_size;
74140947Sbostic 	COMPARE(size, plan->o_data);
74240947Sbostic }
74340947Sbostic 
74440947Sbostic PLAN *
74540947Sbostic c_size(arg)
74640947Sbostic 	char *arg;
74740947Sbostic {
74840947Sbostic 	PLAN *new;
74940947Sbostic 	char endch;
75040947Sbostic 
75140947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
75240947Sbostic 
753*49864Sbostic 	NEW(N_SIZE, f_size);
75440947Sbostic 	new->o_data = find_parsenum(new, "-size", arg, &endch);
75540947Sbostic 	if (endch == 'c')
75640947Sbostic 		divsize = 0;
75740947Sbostic 	return(new);
75840947Sbostic }
75940947Sbostic 
76040947Sbostic /*
76140947Sbostic  * -type c functions --
76240947Sbostic  *
76340947Sbostic  *	True if the type of the file is c, where c is b, c, d, p, or f for
76440947Sbostic  *	block special file, character special file, directory, FIFO, or
76540947Sbostic  *	regular file, respectively.
76640947Sbostic  */
76740947Sbostic f_type(plan, entry)
76840947Sbostic 	PLAN *plan;
76940947Sbostic 	FTSENT *entry;
77040947Sbostic {
77144764Strent 	return((entry->fts_statb.st_mode & S_IFMT) == plan->m_data);
77240947Sbostic }
77340947Sbostic 
77440947Sbostic PLAN *
77540947Sbostic c_type(typestring)
77640947Sbostic 	char *typestring;
77740947Sbostic {
77840947Sbostic 	PLAN *new;
77940947Sbostic 	mode_t  mask;
78045615Sbostic 	void bad_arg();
78140947Sbostic 
78240947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
78340947Sbostic 
78440947Sbostic 	switch (typestring[0]) {
78540947Sbostic 	case 'b':
78640947Sbostic 		mask = S_IFBLK;
78740947Sbostic 		break;
78840947Sbostic 	case 'c':
78940947Sbostic 		mask = S_IFCHR;
79040947Sbostic 		break;
79140947Sbostic 	case 'd':
79240947Sbostic 		mask = S_IFDIR;
79340947Sbostic 		break;
79440947Sbostic 	case 'f':
79540947Sbostic 		mask = S_IFREG;
79640947Sbostic 		break;
79740947Sbostic 	case 'l':
79840947Sbostic 		mask = S_IFLNK;
79940947Sbostic 		break;
80040947Sbostic 	case 'p':
80140947Sbostic 		mask = S_IFIFO;
80240947Sbostic 		break;
80340947Sbostic 	case 's':
80440947Sbostic 		mask = S_IFSOCK;
80540947Sbostic 		break;
80640947Sbostic 	default:
80740947Sbostic 		bad_arg("-type", "unknown type");
80840947Sbostic 	}
80940947Sbostic 
810*49864Sbostic 	NEW(N_TYPE, f_type);
81140947Sbostic 	new->m_data = mask;
81240947Sbostic 	return(new);
81340947Sbostic }
81440947Sbostic 
81540947Sbostic /*
81640947Sbostic  * -user uname functions --
81740947Sbostic  *
81840947Sbostic  *	True if the file belongs to the user uname.  If uname is numeric and
81940947Sbostic  *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
82040947Sbostic  *	return a valid user name, uname is taken as a user ID.
82140947Sbostic  */
82240947Sbostic f_user(plan, entry)
82340947Sbostic 	PLAN *plan;
82440947Sbostic 	FTSENT *entry;
82540947Sbostic {
82642255Sbostic 	return(entry->fts_statb.st_uid == plan->u_data);
82740947Sbostic }
82840947Sbostic 
82940947Sbostic PLAN *
83040947Sbostic c_user(username)
83140947Sbostic 	char *username;
83240947Sbostic {
83340947Sbostic 	PLAN *new;
83440947Sbostic 	struct passwd *p;
83540947Sbostic 	uid_t uid;
83645615Sbostic 	void bad_arg();
83740947Sbostic 
83840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
83940947Sbostic 
84040947Sbostic 	p = getpwnam(username);
84140947Sbostic 	if (p == NULL) {
84240947Sbostic 		uid = atoi(username);
84340947Sbostic 		if (uid == 0 && username[0] != '0')
84440947Sbostic 			bad_arg("-user", "no such user");
84540947Sbostic 	} else
84640947Sbostic 		uid = p->pw_uid;
84740947Sbostic 
848*49864Sbostic 	NEW(N_USER, f_user);
84940947Sbostic 	new->u_data = uid;
85040947Sbostic 	return(new);
85140947Sbostic }
85240947Sbostic 
85340947Sbostic /*
85440947Sbostic  * -xdev functions --
85540947Sbostic  *
85640947Sbostic  *	Always true, causes find not to decend past directories that have a
85740947Sbostic  *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
85840947Sbostic  */
85940947Sbostic PLAN *
86040947Sbostic c_xdev()
86140947Sbostic {
86240947Sbostic 	PLAN *new;
86340947Sbostic 
86442275Sbostic 	ftsoptions |= FTS_XDEV;
86540947Sbostic 
866*49864Sbostic 	NEW(N_XDEV, f_always_true);
86740947Sbostic 	return(new);
86840947Sbostic }
86940947Sbostic 
87040947Sbostic /*
87140947Sbostic  * ( expression ) functions --
87240947Sbostic  *
87340947Sbostic  *	True if expression is true.
87440947Sbostic  */
87540947Sbostic f_expr(plan, entry)
87640947Sbostic 	PLAN *plan;
87740947Sbostic 	FTSENT *entry;
87840947Sbostic {
87940947Sbostic 	register PLAN *p;
88040947Sbostic 	register int state;
88140947Sbostic 
88240947Sbostic 	for (p = plan->p_data[0];
88340947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
88440947Sbostic 	return(state);
88540947Sbostic }
88640947Sbostic 
88740947Sbostic /*
888*49864Sbostic  * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
88940947Sbostic  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
890*49864Sbostic  * to a N_EXPR node containing the expression and the ')' node is discarded.
89140947Sbostic  */
89240947Sbostic PLAN *
89340947Sbostic c_openparen()
89440947Sbostic {
89540947Sbostic 	PLAN *new;
89640947Sbostic 
897*49864Sbostic 	NEW(N_OPENPAREN, (int (*)())-1);
89840947Sbostic 	return(new);
89940947Sbostic }
90040947Sbostic 
90140947Sbostic PLAN *
90240947Sbostic c_closeparen()
90340947Sbostic {
90440947Sbostic 	PLAN *new;
90540947Sbostic 
906*49864Sbostic 	NEW(N_CLOSEPAREN, (int (*)())-1);
90740947Sbostic 	return(new);
90840947Sbostic }
90940947Sbostic 
91040947Sbostic /*
91140947Sbostic  * -mtime n functions --
91240947Sbostic  *
91340947Sbostic  *	True if the difference between the file modification time and the
91440947Sbostic  *	current time is n 24 hour periods.
91540947Sbostic  */
91640947Sbostic f_mtime(plan, entry)
91740947Sbostic 	PLAN *plan;
91840947Sbostic 	FTSENT *entry;
91940947Sbostic {
92040947Sbostic 	extern time_t now;
92140947Sbostic 
92242255Sbostic 	COMPARE((now - entry->fts_statb.st_mtime + SECSPERDAY - 1) /
92342255Sbostic 	    SECSPERDAY, plan->t_data);
92440947Sbostic }
92540947Sbostic 
92640947Sbostic PLAN *
92740947Sbostic c_mtime(arg)
92840947Sbostic 	char *arg;
92940947Sbostic {
93040947Sbostic 	PLAN *new;
93140947Sbostic 
93240947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
93340947Sbostic 
934*49864Sbostic 	NEW(N_MTIME, f_mtime);
93540947Sbostic 	new->t_data = find_parsenum(new, "-mtime", arg, (char *)NULL);
93640947Sbostic 	return(new);
93740947Sbostic }
93840947Sbostic 
93940947Sbostic /*
94040947Sbostic  * ! expression functions --
94140947Sbostic  *
94240947Sbostic  *	Negation of a primary; the unary NOT operator.
94340947Sbostic  */
94440947Sbostic f_not(plan, entry)
94540947Sbostic 	PLAN *plan;
94640947Sbostic 	FTSENT *entry;
94740947Sbostic {
94840947Sbostic 	register PLAN *p;
94940947Sbostic 	register int state;
95040947Sbostic 
95140947Sbostic 	for (p = plan->p_data[0];
95240947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
95340947Sbostic 	return(!state);
95440947Sbostic }
95540947Sbostic 
95640947Sbostic PLAN *
95740947Sbostic c_not()
95840947Sbostic {
95940947Sbostic 	PLAN *new;
96040947Sbostic 
961*49864Sbostic 	NEW(N_NOT, f_not);
96240947Sbostic 	return(new);
96340947Sbostic }
96440947Sbostic 
96540947Sbostic /*
96640947Sbostic  * expression -o expression functions --
96740947Sbostic  *
96840947Sbostic  *	Alternation of primaries; the OR operator.  The second expression is
96940947Sbostic  * not evaluated if the first expression is true.
97040947Sbostic  */
97140947Sbostic f_or(plan, entry)
97240947Sbostic 	PLAN *plan;
97340947Sbostic 	FTSENT *entry;
97440947Sbostic {
97540947Sbostic 	register PLAN *p;
97640947Sbostic 	register int state;
97740947Sbostic 
97840947Sbostic 	for (p = plan->p_data[0];
97940947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
98040947Sbostic 
98140947Sbostic 	if (state)
98242255Sbostic 		return(1);
98340947Sbostic 
98440947Sbostic 	for (p = plan->p_data[1];
98540947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
98640947Sbostic 	return(state);
98740947Sbostic }
98840947Sbostic 
98940947Sbostic PLAN *
99040947Sbostic c_or()
99140947Sbostic {
99240947Sbostic 	PLAN *new;
99340947Sbostic 
994*49864Sbostic 	NEW(N_OR, f_or);
99540947Sbostic 	return(new);
99640947Sbostic }
997