xref: /csrg-svn/usr.bin/find/function.c (revision 55710)
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*55710Sbostic static char sccsid[] = "@(#)function.c	5.23 (Berkeley) 07/27/92";
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 
2147192Sbostic #include <errno.h>
2240947Sbostic #include <grp.h>
2340947Sbostic #include <pwd.h>
2440947Sbostic #include <fts.h>
2554558Sbostic #include <fnmatch.h>
2640947Sbostic #include <unistd.h>
2740947Sbostic #include <tzfile.h>
2840947Sbostic #include <stdio.h>
2947192Sbostic #include <stdlib.h>
3042047Sbostic #include <string.h>
3140947Sbostic #include "find.h"
3240947Sbostic 
3340947Sbostic #define	COMPARE(a, b) { \
3440947Sbostic 	switch(plan->flags) { \
3550437Sbostic 	case F_EQUAL: \
3640947Sbostic 		return(a == b); \
3750437Sbostic 	case F_LESSTHAN: \
3840947Sbostic 		return(a < b); \
3950437Sbostic 	case F_GREATER: \
4040947Sbostic 		return(a > b); \
4140947Sbostic 	} \
4242255Sbostic 	return(0); \
4340947Sbostic }
4440947Sbostic 
4549868Sbostic static PLAN *palloc __P((enum ntype, int (*)()));
4640947Sbostic 
4740947Sbostic /*
4840947Sbostic  * find_parsenum --
4940947Sbostic  *	Parse a string of the form [+-]# and return the value.
5040947Sbostic  */
51*55710Sbostic static long
52*55710Sbostic find_parsenum(plan, option, str, nosign, endch)
5340947Sbostic 	PLAN *plan;
5440947Sbostic 	char *option, *str, *endch;
55*55710Sbostic 	int nosign;
5640947Sbostic {
5740947Sbostic 	long value;
5840947Sbostic 	char *endchar;		/* pointer to character ending conversion */
5940947Sbostic 
6040947Sbostic 	/* determine comparison from leading + or - */
6140947Sbostic 	switch(*str) {
6240947Sbostic 	case '+':
63*55710Sbostic 		if (nosign)
64*55710Sbostic 			err("%s: %s", option, "signed value not permitted");
6540947Sbostic 		++str;
6650437Sbostic 		plan->flags = F_GREATER;
6740947Sbostic 		break;
6840947Sbostic 	case '-':
69*55710Sbostic 		if (nosign)
70*55710Sbostic 			err("%s: %s", option, "signed value not permitted");
7140947Sbostic 		++str;
7250437Sbostic 		plan->flags = F_LESSTHAN;
7340947Sbostic 		break;
7440947Sbostic 	default:
7550437Sbostic 		plan->flags = F_EQUAL;
7640947Sbostic 		break;
7740947Sbostic 	}
7840947Sbostic 
7940947Sbostic 	/*
80*55710Sbostic 	 * Convert the string with strtol().  Note, if strtol() returns zero
8140947Sbostic 	 * and endchar points to the beginning of the string we know we have
8240947Sbostic 	 * a syntax error.
8340947Sbostic 	 */
8440947Sbostic 	value = strtol(str, &endchar, 10);
85*55710Sbostic 	if (value == 0 && endchar == str)
8649868Sbostic 		err("%s: %s", option, "illegal numeric value");
87*55710Sbostic 	if (endchar[0] && (endch == NULL || endchar[0] != *endch))
88*55710Sbostic 		err("%s: %s", option, "illegal trailing character");
8940947Sbostic 	if (endch)
9040947Sbostic 		*endch = endchar[0];
9140947Sbostic 	return(value);
9240947Sbostic }
9340947Sbostic 
9440947Sbostic /*
9540947Sbostic  * -atime n functions --
9640947Sbostic  *
9740947Sbostic  *	True if the difference between the file access time and the
9840947Sbostic  *	current time is n 24 hour periods.
9940947Sbostic  *
10040947Sbostic  */
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);
120*55710Sbostic 	new->t_data = find_parsenum(new, "-atime", arg, 0, NULL);
12140947Sbostic 	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  */
12940947Sbostic f_ctime(plan, entry)
13040947Sbostic 	PLAN *plan;
13140947Sbostic 	FTSENT *entry;
13240947Sbostic {
13340947Sbostic 	extern time_t now;
13440947Sbostic 
13552239Sbostic 	COMPARE((now - entry->fts_statp->st_ctime +
13642255Sbostic 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
13740947Sbostic }
13840947Sbostic 
13940947Sbostic PLAN *
14040947Sbostic c_ctime(arg)
14140947Sbostic 	char *arg;
14240947Sbostic {
14340947Sbostic 	PLAN *new;
14440947Sbostic 
14540947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
14640947Sbostic 
14749868Sbostic 	new = palloc(N_CTIME, f_ctime);
148*55710Sbostic 	new->t_data = find_parsenum(new, "-ctime", arg, 0, NULL);
14940947Sbostic 	return(new);
15040947Sbostic }
15140947Sbostic 
15240947Sbostic /*
15340947Sbostic  * -depth functions --
15440947Sbostic  *
15540947Sbostic  *	Always true, causes descent of the directory hierarchy to be done
15640947Sbostic  *	so that all entries in a directory are acted on before the directory
15740947Sbostic  *	itself.
15840947Sbostic  */
15940947Sbostic /* ARGSUSED */
16040947Sbostic f_always_true(plan, entry)
16140947Sbostic 	PLAN *plan;
16240947Sbostic 	FTSENT *entry;
16340947Sbostic {
16442255Sbostic 	return(1);
16540947Sbostic }
16640947Sbostic 
16740947Sbostic PLAN *
16840947Sbostic c_depth()
16940947Sbostic {
17045615Sbostic 	isdepth = 1;
17140947Sbostic 
17249868Sbostic 	return(palloc(N_DEPTH, f_always_true));
17340947Sbostic }
17440947Sbostic 
17540947Sbostic /*
17640947Sbostic  * [-exec | -ok] utility [arg ... ] ; functions --
17740947Sbostic  *
17840947Sbostic  *	True if the executed utility returns a zero value as exit status.
17940947Sbostic  *	The end of the primary expression is delimited by a semicolon.  If
18040947Sbostic  *	"{}" occurs anywhere, it gets replaced by the current pathname.
18140947Sbostic  *	The current directory for the execution of utility is the same as
18240947Sbostic  *	the current directory when the find utility was started.
18340947Sbostic  *
18440947Sbostic  *	The primary -ok is different in that it requests affirmation of the
18540947Sbostic  *	user before executing the utility.
18640947Sbostic  */
18740947Sbostic f_exec(plan, entry)
18840947Sbostic 	register PLAN *plan;
18940947Sbostic 	FTSENT *entry;
19040947Sbostic {
19149869Sbostic 	extern int dotfd;
19240947Sbostic 	register int cnt;
19347192Sbostic 	pid_t pid;
19447192Sbostic 	int status;
19540947Sbostic 
19640947Sbostic 	for (cnt = 0; plan->e_argv[cnt]; ++cnt)
19740947Sbostic 		if (plan->e_len[cnt])
19845616Sbostic 			brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
19949869Sbostic 			    entry->fts_path, plan->e_len[cnt]);
20040947Sbostic 
20150437Sbostic 	if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
20242255Sbostic 		return(0);
20340947Sbostic 
20440947Sbostic 	switch(pid = vfork()) {
20540947Sbostic 	case -1:
20649868Sbostic 		err("fork: %s", strerror(errno));
20740947Sbostic 		/* NOTREACHED */
20840947Sbostic 	case 0:
20949869Sbostic 		if (fchdir(dotfd)) {
21049869Sbostic 			(void)fprintf(stderr,
21149869Sbostic 			    "find: chdir: %s\n", strerror(errno));
21249869Sbostic 			_exit(1);
21349869Sbostic 		}
21440947Sbostic 		execvp(plan->e_argv[0], plan->e_argv);
21549869Sbostic 		(void)fprintf(stderr,
21649869Sbostic 		    "find: %s: %s\n", plan->e_argv[0], strerror(errno));
21749869Sbostic 		_exit(1);
21840947Sbostic 	}
21947192Sbostic 	pid = waitpid(pid, &status, 0);
22047192Sbostic 	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)
24749868Sbostic 			err("%s: %s",
24849868Sbostic 			    isok ? "-ok" : "-exec", "no terminating \";\"");
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;
27440947Sbostic 	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 
28949868Sbostic 	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  */
29740947Sbostic f_fstype(plan, entry)
29840947Sbostic 	PLAN *plan;
29940947Sbostic 	FTSENT *entry;
30040947Sbostic {
30142275Sbostic 	static dev_t curdev;	/* need a guaranteed illegal dev value */
30242275Sbostic 	static int first = 1;
30340947Sbostic 	struct statfs sb;
30442255Sbostic 	static short val;
30545626Sbostic 	char *p, save[2];
30640947Sbostic 
30750437Sbostic 	/* Only check when we cross mount point. */
30852239Sbostic 	if (first || curdev != entry->fts_statp->st_dev) {
30952239Sbostic 		curdev = entry->fts_statp->st_dev;
31045626Sbostic 
31145626Sbostic 		/*
31245626Sbostic 		 * Statfs follows symlinks; find wants the link's file system,
31345626Sbostic 		 * not where it points.
31445626Sbostic 		 */
31545626Sbostic 		if (entry->fts_info == FTS_SL ||
31645626Sbostic 		    entry->fts_info == FTS_SLNONE) {
31745626Sbostic 			if (p = rindex(entry->fts_accpath, '/'))
31845626Sbostic 				++p;
31945626Sbostic 			else
32045626Sbostic 				p = entry->fts_accpath;
32145626Sbostic 			save[0] = p[0];
32245626Sbostic 			p[0] = '.';
32345626Sbostic 			save[1] = p[1];
32445626Sbostic 			p[1] = '\0';
32545626Sbostic 
32645626Sbostic 		} else
32745626Sbostic 			p = NULL;
32845626Sbostic 
32949868Sbostic 		if (statfs(entry->fts_accpath, &sb))
33049868Sbostic 			err("%s: %s", entry->fts_accpath, strerror(errno));
33145626Sbostic 
33245626Sbostic 		if (p) {
33345626Sbostic 			p[0] = save[0];
33445626Sbostic 			p[1] = save[1];
33545626Sbostic 		}
33645626Sbostic 
33742275Sbostic 		first = 0;
33850437Sbostic 		switch(plan->flags) {
33950437Sbostic 		case F_MTFLAG:
34050437Sbostic 			val = sb.f_flags;
34150437Sbostic 			break;
34250437Sbostic 		case F_MTTYPE:
34350437Sbostic 			val = sb.f_type;
34450437Sbostic 			break;
34550437Sbostic 		}
34640947Sbostic 	}
34750437Sbostic 	switch(plan->flags) {
34850437Sbostic 	case F_MTFLAG:
34950437Sbostic 		return(val & plan->mt_data);
35050437Sbostic 	case F_MTTYPE:
35150437Sbostic 		return(val == plan->mt_data);
35250437Sbostic 	}
35340947Sbostic }
35440947Sbostic 
35540947Sbostic PLAN *
35640947Sbostic c_fstype(arg)
35740947Sbostic 	char *arg;
35840947Sbostic {
35940947Sbostic 	register PLAN *new;
36040947Sbostic 
36140947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
36240947Sbostic 
36349868Sbostic 	new = palloc(N_FSTYPE, f_fstype);
36440947Sbostic 	switch(*arg) {
36542255Sbostic 	case 'l':
36642255Sbostic 		if (!strcmp(arg, "local")) {
36750437Sbostic 			new->flags = F_MTFLAG;
36850437Sbostic 			new->mt_data = MNT_LOCAL;
36942255Sbostic 			return(new);
37042255Sbostic 		}
37142255Sbostic 		break;
37240947Sbostic 	case 'm':
37340947Sbostic 		if (!strcmp(arg, "mfs")) {
37450437Sbostic 			new->flags = F_MTTYPE;
37550437Sbostic 			new->mt_data = MOUNT_MFS;
37640947Sbostic 			return(new);
37740947Sbostic 		}
37840947Sbostic 		break;
37940947Sbostic 	case 'n':
38040947Sbostic 		if (!strcmp(arg, "nfs")) {
38150437Sbostic 			new->flags = F_MTTYPE;
38250437Sbostic 			new->mt_data = MOUNT_NFS;
38340947Sbostic 			return(new);
38440947Sbostic 		}
38540947Sbostic 		break;
38640947Sbostic 	case 'p':
38740947Sbostic 		if (!strcmp(arg, "pc")) {
38850437Sbostic 			new->flags = F_MTTYPE;
38950437Sbostic 			new->mt_data = MOUNT_PC;
39040947Sbostic 			return(new);
39140947Sbostic 		}
39240947Sbostic 		break;
39350437Sbostic 	case 'r':
39450437Sbostic 		if (!strcmp(arg, "rdonly")) {
39550437Sbostic 			new->flags = F_MTFLAG;
39650437Sbostic 			new->mt_data = MNT_RDONLY;
39750437Sbostic 			return(new);
39850437Sbostic 		}
39950437Sbostic 		break;
40040947Sbostic 	case 'u':
40140947Sbostic 		if (!strcmp(arg, "ufs")) {
40250437Sbostic 			new->flags = F_MTTYPE;
40350437Sbostic 			new->mt_data = MOUNT_UFS;
40440947Sbostic 			return(new);
40540947Sbostic 		}
40640947Sbostic 		break;
40740947Sbostic 	}
40849868Sbostic 	err("unknown file type %s", arg);
40940947Sbostic 	/* NOTREACHED */
41040947Sbostic }
41140947Sbostic 
41240947Sbostic /*
41340947Sbostic  * -group gname functions --
41440947Sbostic  *
41540947Sbostic  *	True if the file belongs to the group gname.  If gname is numeric and
41640947Sbostic  *	an equivalent of the getgrnam() function does not return a valid group
41740947Sbostic  *	name, gname is taken as a group ID.
41840947Sbostic  */
41940947Sbostic f_group(plan, entry)
42040947Sbostic 	PLAN *plan;
42140947Sbostic 	FTSENT *entry;
42240947Sbostic {
42352239Sbostic 	return(entry->fts_statp->st_gid == plan->g_data);
42440947Sbostic }
42540947Sbostic 
42640947Sbostic PLAN *
42740947Sbostic c_group(gname)
42840947Sbostic 	char *gname;
42940947Sbostic {
43040947Sbostic 	PLAN *new;
43140947Sbostic 	struct group *g;
43240947Sbostic 	gid_t gid;
43340947Sbostic 
43440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
43540947Sbostic 
43640947Sbostic 	g = getgrnam(gname);
43740947Sbostic 	if (g == NULL) {
43840947Sbostic 		gid = atoi(gname);
43940947Sbostic 		if (gid == 0 && gname[0] != '0')
44049868Sbostic 			err("%s: %s", "-group", "no such group");
44140947Sbostic 	} else
44240947Sbostic 		gid = g->gr_gid;
44340947Sbostic 
44449868Sbostic 	new = palloc(N_GROUP, f_group);
44540947Sbostic 	new->g_data = gid;
44640947Sbostic 	return(new);
44740947Sbostic }
44840947Sbostic 
44940947Sbostic /*
45040947Sbostic  * -inum n functions --
45140947Sbostic  *
45240947Sbostic  *	True if the file has inode # n.
45340947Sbostic  */
45440947Sbostic f_inum(plan, entry)
45540947Sbostic 	PLAN *plan;
45640947Sbostic 	FTSENT *entry;
45740947Sbostic {
45852239Sbostic 	COMPARE(entry->fts_statp->st_ino, plan->i_data);
45940947Sbostic }
46040947Sbostic 
46140947Sbostic PLAN *
46240947Sbostic c_inum(arg)
46340947Sbostic 	char *arg;
46440947Sbostic {
46540947Sbostic 	PLAN *new;
46640947Sbostic 
46740947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
46840947Sbostic 
46949868Sbostic 	new = palloc(N_INUM, f_inum);
470*55710Sbostic 	new->i_data = find_parsenum(new, "-inum", arg, 1, NULL);
47140947Sbostic 	return(new);
47240947Sbostic }
47340947Sbostic 
47440947Sbostic /*
47540947Sbostic  * -links n functions --
47640947Sbostic  *
47740947Sbostic  *	True if the file has n links.
47840947Sbostic  */
47940947Sbostic f_links(plan, entry)
48040947Sbostic 	PLAN *plan;
48140947Sbostic 	FTSENT *entry;
48240947Sbostic {
48352239Sbostic 	COMPARE(entry->fts_statp->st_nlink, plan->l_data);
48440947Sbostic }
48540947Sbostic 
48640947Sbostic PLAN *
48740947Sbostic c_links(arg)
48840947Sbostic 	char *arg;
48940947Sbostic {
49040947Sbostic 	PLAN *new;
49140947Sbostic 
49240947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
49340947Sbostic 
49449868Sbostic 	new = palloc(N_LINKS, f_links);
495*55710Sbostic 	new->l_data = (nlink_t)find_parsenum(new, "-links", arg, 1, NULL);
49640947Sbostic 	return(new);
49740947Sbostic }
49840947Sbostic 
49940947Sbostic /*
50040947Sbostic  * -ls functions --
50140947Sbostic  *
50240947Sbostic  *	Always true - prints the current entry to stdout in "ls" format.
50340947Sbostic  */
50440947Sbostic /* ARGSUSED */
50540947Sbostic f_ls(plan, entry)
50640947Sbostic 	PLAN *plan;
50740947Sbostic 	FTSENT *entry;
50840947Sbostic {
50952239Sbostic 	printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
51042255Sbostic 	return(1);
51140947Sbostic }
51240947Sbostic 
51340947Sbostic PLAN *
51440947Sbostic c_ls()
51540947Sbostic {
51640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
51745615Sbostic 	isoutput = 1;
51840947Sbostic 
51949868Sbostic 	return(palloc(N_LS, f_ls));
52040947Sbostic }
52140947Sbostic 
52240947Sbostic /*
52350437Sbostic  * -mtime n functions --
52450437Sbostic  *
52550437Sbostic  *	True if the difference between the file modification time and the
52650437Sbostic  *	current time is n 24 hour periods.
52750437Sbostic  */
52850437Sbostic f_mtime(plan, entry)
52950437Sbostic 	PLAN *plan;
53050437Sbostic 	FTSENT *entry;
53150437Sbostic {
53250437Sbostic 	extern time_t now;
53350437Sbostic 
53452239Sbostic 	COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
53550437Sbostic 	    SECSPERDAY, plan->t_data);
53650437Sbostic }
53750437Sbostic 
53850437Sbostic PLAN *
53950437Sbostic c_mtime(arg)
54050437Sbostic 	char *arg;
54150437Sbostic {
54250437Sbostic 	PLAN *new;
54350437Sbostic 
54450437Sbostic 	ftsoptions &= ~FTS_NOSTAT;
54550437Sbostic 
54650437Sbostic 	new = palloc(N_MTIME, f_mtime);
547*55710Sbostic 	new->t_data = find_parsenum(new, "-mtime", arg, 0, NULL);
54850437Sbostic 	return(new);
54950437Sbostic }
55050437Sbostic 
55150437Sbostic /*
55240947Sbostic  * -name functions --
55340947Sbostic  *
55440947Sbostic  *	True if the basename of the filename being examined
55540947Sbostic  *	matches pattern using Pattern Matching Notation S3.14
55640947Sbostic  */
55740947Sbostic f_name(plan, entry)
55840947Sbostic 	PLAN *plan;
55940947Sbostic 	FTSENT *entry;
56040947Sbostic {
56154555Sbostic 	return(!fnmatch(plan->c_data, entry->fts_name, 0));
56240947Sbostic }
56340947Sbostic 
56440947Sbostic PLAN *
56540947Sbostic c_name(pattern)
56640947Sbostic 	char *pattern;
56740947Sbostic {
56840947Sbostic 	PLAN *new;
56940947Sbostic 
57049868Sbostic 	new = palloc(N_NAME, f_name);
57140947Sbostic 	new->c_data = pattern;
57240947Sbostic 	return(new);
57340947Sbostic }
57440947Sbostic 
57540947Sbostic /*
57640947Sbostic  * -newer file functions --
57740947Sbostic  *
57840947Sbostic  *	True if the current file has been modified more recently
57940947Sbostic  *	then the modification time of the file named by the pathname
58040947Sbostic  *	file.
58140947Sbostic  */
58240947Sbostic f_newer(plan, entry)
58340947Sbostic 	PLAN *plan;
58440947Sbostic 	FTSENT *entry;
58540947Sbostic {
58652239Sbostic 	return(entry->fts_statp->st_mtime > plan->t_data);
58740947Sbostic }
58840947Sbostic 
58940947Sbostic PLAN *
59040947Sbostic c_newer(filename)
59140947Sbostic 	char *filename;
59240947Sbostic {
59340947Sbostic 	PLAN *new;
59440947Sbostic 	struct stat sb;
59540947Sbostic 
59640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
59740947Sbostic 
59849868Sbostic 	if (stat(filename, &sb))
59949868Sbostic 		err("%s: %s", filename, strerror(errno));
60049868Sbostic 	new = palloc(N_NEWER, f_newer);
60140947Sbostic 	new->t_data = sb.st_mtime;
60240947Sbostic 	return(new);
60340947Sbostic }
60440947Sbostic 
60540947Sbostic /*
60640947Sbostic  * -nogroup functions --
60740947Sbostic  *
60840947Sbostic  *	True if file belongs to a user ID for which the equivalent
60940947Sbostic  *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
61040947Sbostic  */
61140947Sbostic /* ARGSUSED */
61240947Sbostic f_nogroup(plan, entry)
61340947Sbostic 	PLAN *plan;
61440947Sbostic 	FTSENT *entry;
61540947Sbostic {
61645615Sbostic 	char *group_from_gid();
61745615Sbostic 
61852239Sbostic 	return(group_from_gid(entry->fts_statp->st_gid, 1) ? 1 : 0);
61940947Sbostic }
62040947Sbostic 
62140947Sbostic PLAN *
62240947Sbostic c_nogroup()
62340947Sbostic {
62440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
62540947Sbostic 
62649868Sbostic 	return(palloc(N_NOGROUP, f_nogroup));
62740947Sbostic }
62840947Sbostic 
62940947Sbostic /*
63040947Sbostic  * -nouser functions --
63140947Sbostic  *
63240947Sbostic  *	True if file belongs to a user ID for which the equivalent
63340947Sbostic  *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
63440947Sbostic  */
63540947Sbostic /* ARGSUSED */
63640947Sbostic f_nouser(plan, entry)
63740947Sbostic 	PLAN *plan;
63840947Sbostic 	FTSENT *entry;
63940947Sbostic {
64045615Sbostic 	char *user_from_uid();
64145615Sbostic 
64252239Sbostic 	return(user_from_uid(entry->fts_statp->st_uid, 1) ? 1 : 0);
64340947Sbostic }
64440947Sbostic 
64540947Sbostic PLAN *
64640947Sbostic c_nouser()
64740947Sbostic {
64840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
64940947Sbostic 
65049868Sbostic 	return(palloc(N_NOUSER, f_nouser));
65140947Sbostic }
65240947Sbostic 
65340947Sbostic /*
65450437Sbostic  * -path functions --
65550437Sbostic  *
65650437Sbostic  *	True if the path of the filename being examined
65750437Sbostic  *	matches pattern using Pattern Matching Notation S3.14
65850437Sbostic  */
65950437Sbostic f_path(plan, entry)
66050437Sbostic 	PLAN *plan;
66150437Sbostic 	FTSENT *entry;
66250437Sbostic {
66354555Sbostic 	return(!fnmatch(plan->c_data, entry->fts_path, 0));
66450437Sbostic }
66550437Sbostic 
66650437Sbostic PLAN *
66750437Sbostic c_path(pattern)
66850437Sbostic 	char *pattern;
66950437Sbostic {
67050437Sbostic 	PLAN *new;
67150437Sbostic 
67250437Sbostic 	new = palloc(N_NAME, f_path);
67350437Sbostic 	new->c_data = pattern;
67450437Sbostic 	return(new);
67550437Sbostic }
67650437Sbostic 
67750437Sbostic /*
67840947Sbostic  * -perm functions --
67940947Sbostic  *
68040947Sbostic  *	The mode argument is used to represent file mode bits.  If it starts
68140947Sbostic  *	with a leading digit, it's treated as an octal mode, otherwise as a
68240947Sbostic  *	symbolic mode.
68340947Sbostic  */
68440947Sbostic f_perm(plan, entry)
68540947Sbostic 	PLAN *plan;
68640947Sbostic 	FTSENT *entry;
68740947Sbostic {
68840947Sbostic 	mode_t mode;
68940947Sbostic 
69052239Sbostic 	mode = entry->fts_statp->st_mode &
69140947Sbostic 	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
69250437Sbostic 	if (plan->flags == F_ATLEAST)
69340947Sbostic 		return((plan->m_data | mode) == mode);
69440947Sbostic 	else
69540947Sbostic 		return(mode == plan->m_data);
69640947Sbostic 	/* NOTREACHED */
69740947Sbostic }
69840947Sbostic 
69940947Sbostic PLAN *
70040947Sbostic c_perm(perm)
70140947Sbostic 	char *perm;
70240947Sbostic {
70340947Sbostic 	PLAN *new;
70447192Sbostic 	mode_t *set;
70540947Sbostic 
70640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
70740947Sbostic 
70849868Sbostic 	new = palloc(N_PERM, f_perm);
70940947Sbostic 
71040947Sbostic 	if (*perm == '-') {
71150437Sbostic 		new->flags = F_ATLEAST;
71240947Sbostic 		++perm;
71340947Sbostic 	}
71440947Sbostic 
71544764Strent 	if ((set = setmode(perm)) == NULL)
71649868Sbostic 		err("%s: %s", "-perm", "illegal mode string");
71740947Sbostic 
71844764Strent 	new->m_data = getmode(set, 0);
71940947Sbostic 	return(new);
72040947Sbostic }
72140947Sbostic 
72240947Sbostic /*
72340947Sbostic  * -print functions --
72440947Sbostic  *
72540947Sbostic  *	Always true, causes the current pathame to be written to
72640947Sbostic  *	standard output.
72740947Sbostic  */
72840947Sbostic /* ARGSUSED */
72940947Sbostic f_print(plan, entry)
73040947Sbostic 	PLAN *plan;
73140947Sbostic 	FTSENT *entry;
73240947Sbostic {
73342255Sbostic 	(void)printf("%s\n", entry->fts_path);
73442255Sbostic 	return(1);
73540947Sbostic }
73640947Sbostic 
73740947Sbostic PLAN *
73840947Sbostic c_print()
73940947Sbostic {
74045615Sbostic 	isoutput = 1;
74140947Sbostic 
74249868Sbostic 	return(palloc(N_PRINT, f_print));
74340947Sbostic }
74440947Sbostic 
74540947Sbostic /*
74640947Sbostic  * -prune functions --
74740947Sbostic  *
74840947Sbostic  *	Prune a portion of the hierarchy.
74940947Sbostic  */
75040947Sbostic /* ARGSUSED */
75140947Sbostic f_prune(plan, entry)
75240947Sbostic 	PLAN *plan;
75340947Sbostic 	FTSENT *entry;
75440947Sbostic {
75540947Sbostic 	extern FTS *tree;
75640947Sbostic 
75749868Sbostic 	if (fts_set(tree, entry, FTS_SKIP))
75849868Sbostic 		err("%s: %s", entry->fts_path, strerror(errno));
75942255Sbostic 	return(1);
76040947Sbostic }
76140947Sbostic 
76240947Sbostic PLAN *
76340947Sbostic c_prune()
76440947Sbostic {
76549868Sbostic 	return(palloc(N_PRUNE, f_prune));
76640947Sbostic }
76740947Sbostic 
76840947Sbostic /*
76940947Sbostic  * -size n[c] functions --
77040947Sbostic  *
77140947Sbostic  *	True if the file size in bytes, divided by an implementation defined
77240947Sbostic  *	value and rounded up to the next integer, is n.  If n is followed by
77340947Sbostic  *	a c, the size is in bytes.
77440947Sbostic  */
77540947Sbostic #define	FIND_SIZE	512
77640947Sbostic static int divsize = 1;
77740947Sbostic 
77840947Sbostic f_size(plan, entry)
77940947Sbostic 	PLAN *plan;
78040947Sbostic 	FTSENT *entry;
78140947Sbostic {
78240947Sbostic 	off_t size;
78340947Sbostic 
78452239Sbostic 	size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
78552239Sbostic 	    FIND_SIZE : entry->fts_statp->st_size;
78640947Sbostic 	COMPARE(size, plan->o_data);
78740947Sbostic }
78840947Sbostic 
78940947Sbostic PLAN *
79040947Sbostic c_size(arg)
79140947Sbostic 	char *arg;
79240947Sbostic {
79340947Sbostic 	PLAN *new;
79440947Sbostic 	char endch;
79540947Sbostic 
79640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
79740947Sbostic 
79849868Sbostic 	new = palloc(N_SIZE, f_size);
799*55710Sbostic 	endch = 'c';
800*55710Sbostic 	new->o_data = find_parsenum(new, "-size", arg, 1, &endch);
80140947Sbostic 	if (endch == 'c')
80240947Sbostic 		divsize = 0;
80340947Sbostic 	return(new);
80440947Sbostic }
80540947Sbostic 
80640947Sbostic /*
80740947Sbostic  * -type c functions --
80840947Sbostic  *
80940947Sbostic  *	True if the type of the file is c, where c is b, c, d, p, or f for
81040947Sbostic  *	block special file, character special file, directory, FIFO, or
81140947Sbostic  *	regular file, respectively.
81240947Sbostic  */
81340947Sbostic f_type(plan, entry)
81440947Sbostic 	PLAN *plan;
81540947Sbostic 	FTSENT *entry;
81640947Sbostic {
81752239Sbostic 	return((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
81840947Sbostic }
81940947Sbostic 
82040947Sbostic PLAN *
82140947Sbostic c_type(typestring)
82240947Sbostic 	char *typestring;
82340947Sbostic {
82440947Sbostic 	PLAN *new;
82540947Sbostic 	mode_t  mask;
82640947Sbostic 
82740947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
82840947Sbostic 
82940947Sbostic 	switch (typestring[0]) {
83040947Sbostic 	case 'b':
83140947Sbostic 		mask = S_IFBLK;
83240947Sbostic 		break;
83340947Sbostic 	case 'c':
83440947Sbostic 		mask = S_IFCHR;
83540947Sbostic 		break;
83640947Sbostic 	case 'd':
83740947Sbostic 		mask = S_IFDIR;
83840947Sbostic 		break;
83940947Sbostic 	case 'f':
84040947Sbostic 		mask = S_IFREG;
84140947Sbostic 		break;
84240947Sbostic 	case 'l':
84340947Sbostic 		mask = S_IFLNK;
84440947Sbostic 		break;
84540947Sbostic 	case 'p':
84640947Sbostic 		mask = S_IFIFO;
84740947Sbostic 		break;
84840947Sbostic 	case 's':
84940947Sbostic 		mask = S_IFSOCK;
85040947Sbostic 		break;
85140947Sbostic 	default:
85249868Sbostic 		err("%s: %s", "-type", "unknown type");
85340947Sbostic 	}
85440947Sbostic 
85549868Sbostic 	new = palloc(N_TYPE, f_type);
85640947Sbostic 	new->m_data = mask;
85740947Sbostic 	return(new);
85840947Sbostic }
85940947Sbostic 
86040947Sbostic /*
86140947Sbostic  * -user uname functions --
86240947Sbostic  *
86340947Sbostic  *	True if the file belongs to the user uname.  If uname is numeric and
86440947Sbostic  *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
86540947Sbostic  *	return a valid user name, uname is taken as a user ID.
86640947Sbostic  */
86740947Sbostic f_user(plan, entry)
86840947Sbostic 	PLAN *plan;
86940947Sbostic 	FTSENT *entry;
87040947Sbostic {
87152239Sbostic 	return(entry->fts_statp->st_uid == plan->u_data);
87240947Sbostic }
87340947Sbostic 
87440947Sbostic PLAN *
87540947Sbostic c_user(username)
87640947Sbostic 	char *username;
87740947Sbostic {
87840947Sbostic 	PLAN *new;
87940947Sbostic 	struct passwd *p;
88040947Sbostic 	uid_t uid;
88140947Sbostic 
88240947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
88340947Sbostic 
88440947Sbostic 	p = getpwnam(username);
88540947Sbostic 	if (p == NULL) {
88640947Sbostic 		uid = atoi(username);
88740947Sbostic 		if (uid == 0 && username[0] != '0')
88849868Sbostic 			err("%s: %s", "-user", "no such user");
88940947Sbostic 	} else
89040947Sbostic 		uid = p->pw_uid;
89140947Sbostic 
89249868Sbostic 	new = palloc(N_USER, f_user);
89340947Sbostic 	new->u_data = uid;
89440947Sbostic 	return(new);
89540947Sbostic }
89640947Sbostic 
89740947Sbostic /*
89840947Sbostic  * -xdev functions --
89940947Sbostic  *
90040947Sbostic  *	Always true, causes find not to decend past directories that have a
90140947Sbostic  *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
90240947Sbostic  */
90340947Sbostic PLAN *
90440947Sbostic c_xdev()
90540947Sbostic {
90642275Sbostic 	ftsoptions |= FTS_XDEV;
90740947Sbostic 
90849868Sbostic 	return(palloc(N_XDEV, f_always_true));
90940947Sbostic }
91040947Sbostic 
91140947Sbostic /*
91240947Sbostic  * ( expression ) functions --
91340947Sbostic  *
91440947Sbostic  *	True if expression is true.
91540947Sbostic  */
91640947Sbostic f_expr(plan, entry)
91740947Sbostic 	PLAN *plan;
91840947Sbostic 	FTSENT *entry;
91940947Sbostic {
92040947Sbostic 	register PLAN *p;
92140947Sbostic 	register int state;
92240947Sbostic 
92340947Sbostic 	for (p = plan->p_data[0];
92440947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
92540947Sbostic 	return(state);
92640947Sbostic }
92740947Sbostic 
92840947Sbostic /*
92949864Sbostic  * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
93040947Sbostic  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
93149864Sbostic  * to a N_EXPR node containing the expression and the ')' node is discarded.
93240947Sbostic  */
93340947Sbostic PLAN *
93440947Sbostic c_openparen()
93540947Sbostic {
93649868Sbostic 	return(palloc(N_OPENPAREN, (int (*)())-1));
93740947Sbostic }
93840947Sbostic 
93940947Sbostic PLAN *
94040947Sbostic c_closeparen()
94140947Sbostic {
94249868Sbostic 	return(palloc(N_CLOSEPAREN, (int (*)())-1));
94340947Sbostic }
94440947Sbostic 
94540947Sbostic /*
94640947Sbostic  * ! expression functions --
94740947Sbostic  *
94840947Sbostic  *	Negation of a primary; the unary NOT operator.
94940947Sbostic  */
95040947Sbostic f_not(plan, entry)
95140947Sbostic 	PLAN *plan;
95240947Sbostic 	FTSENT *entry;
95340947Sbostic {
95440947Sbostic 	register PLAN *p;
95540947Sbostic 	register int state;
95640947Sbostic 
95740947Sbostic 	for (p = plan->p_data[0];
95840947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
95940947Sbostic 	return(!state);
96040947Sbostic }
96140947Sbostic 
96240947Sbostic PLAN *
96340947Sbostic c_not()
96440947Sbostic {
96549868Sbostic 	return(palloc(N_NOT, f_not));
96640947Sbostic }
96740947Sbostic 
96840947Sbostic /*
96940947Sbostic  * expression -o expression functions --
97040947Sbostic  *
97140947Sbostic  *	Alternation of primaries; the OR operator.  The second expression is
97240947Sbostic  * not evaluated if the first expression is true.
97340947Sbostic  */
97440947Sbostic f_or(plan, entry)
97540947Sbostic 	PLAN *plan;
97640947Sbostic 	FTSENT *entry;
97740947Sbostic {
97840947Sbostic 	register PLAN *p;
97940947Sbostic 	register int state;
98040947Sbostic 
98140947Sbostic 	for (p = plan->p_data[0];
98240947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
98340947Sbostic 
98440947Sbostic 	if (state)
98542255Sbostic 		return(1);
98640947Sbostic 
98740947Sbostic 	for (p = plan->p_data[1];
98840947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
98940947Sbostic 	return(state);
99040947Sbostic }
99140947Sbostic 
99240947Sbostic PLAN *
99340947Sbostic c_or()
99440947Sbostic {
99549868Sbostic 	return(palloc(N_OR, f_or));
99649868Sbostic }
99749868Sbostic 
99849868Sbostic static PLAN *
99949868Sbostic palloc(t, f)
100049868Sbostic 	enum ntype t;
100149868Sbostic 	int (*f)();
100249868Sbostic {
100340947Sbostic 	PLAN *new;
100440947Sbostic 
100549868Sbostic 	if (new = malloc(sizeof(PLAN))) {
100649868Sbostic 		new->type = t;
100749868Sbostic 		new->eval = f;
100849868Sbostic 		new->flags = 0;
100949868Sbostic 		new->next = NULL;
101049868Sbostic 		return(new);
101149868Sbostic 	}
101249868Sbostic 	err("%s", strerror(errno));
101349868Sbostic 	/* NOTREACHED */
101440947Sbostic }
1015