xref: /csrg-svn/usr.bin/find/function.c (revision 49869)
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*49869Sbostic static char sccsid[] = "@(#)function.c	5.17 (Berkeley) 05/24/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 
4649868Sbostic static PLAN *palloc __P((enum ntype, int (*)()));
4740947Sbostic 
4840947Sbostic /*
4940947Sbostic  * find_parsenum --
5040947Sbostic  *	Parse a string of the form [+-]# and return the value.
5140947Sbostic  */
5240947Sbostic long
5340947Sbostic find_parsenum(plan, option, str, endch)
5440947Sbostic 	PLAN *plan;
5540947Sbostic 	char *option, *str, *endch;
5640947Sbostic {
5740947Sbostic 	long value;
5840947Sbostic 	char *endchar;		/* pointer to character ending conversion */
5940947Sbostic 
6040947Sbostic 	/* determine comparison from leading + or - */
6140947Sbostic 	switch(*str) {
6240947Sbostic 	case '+':
6340947Sbostic 		++str;
6440947Sbostic 		plan->flags = FIND_GREATER;
6540947Sbostic 		break;
6640947Sbostic 	case '-':
6740947Sbostic 		++str;
6840947Sbostic 		plan->flags = FIND_LESSTHAN;
6940947Sbostic 		break;
7040947Sbostic 	default:
7140947Sbostic 		plan->flags = FIND_EQUAL;
7240947Sbostic 		break;
7340947Sbostic 	}
7440947Sbostic 
7540947Sbostic 	/*
7640947Sbostic 	 * convert the string with strtol().  Note, if strtol() returns zero
7740947Sbostic 	 * and endchar points to the beginning of the string we know we have
7840947Sbostic 	 * a syntax error.
7940947Sbostic 	 */
8040947Sbostic 	value = strtol(str, &endchar, 10);
8140947Sbostic 	if (!value && endchar == str ||
8240947Sbostic 	    endchar[0] && (!endch || endchar[0] != *endch))
8349868Sbostic 		err("%s: %s", option, "illegal numeric value");
8440947Sbostic 	if (endch)
8540947Sbostic 		*endch = endchar[0];
8640947Sbostic 	return(value);
8740947Sbostic }
8840947Sbostic 
8940947Sbostic /*
9040947Sbostic  * -atime n functions --
9140947Sbostic  *
9240947Sbostic  *	True if the difference between the file access time and the
9340947Sbostic  *	current time is n 24 hour periods.
9440947Sbostic  *
9540947Sbostic  */
9640947Sbostic f_atime(plan, entry)
9740947Sbostic 	PLAN *plan;
9840947Sbostic 	FTSENT *entry;
9940947Sbostic {
10040947Sbostic 	extern time_t now;
10140947Sbostic 
10242255Sbostic 	COMPARE((now - entry->fts_statb.st_atime +
10342255Sbostic 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
10440947Sbostic }
10540947Sbostic 
10640947Sbostic PLAN *
10740947Sbostic c_atime(arg)
10840947Sbostic 	char *arg;
10940947Sbostic {
11040947Sbostic 	PLAN *new;
11140947Sbostic 
11240947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
11340947Sbostic 
11449868Sbostic 	new = palloc(N_ATIME, f_atime);
11549868Sbostic 	new->t_data = find_parsenum(new, "-atime", arg, NULL);
11640947Sbostic 	return(new);
11740947Sbostic }
11840947Sbostic /*
11940947Sbostic  * -ctime n functions --
12040947Sbostic  *
12140947Sbostic  *	True if the difference between the last change of file
12240947Sbostic  *	status information and the current time is n 24 hour periods.
12340947Sbostic  */
12440947Sbostic f_ctime(plan, entry)
12540947Sbostic 	PLAN *plan;
12640947Sbostic 	FTSENT *entry;
12740947Sbostic {
12840947Sbostic 	extern time_t now;
12940947Sbostic 
13042255Sbostic 	COMPARE((now - entry->fts_statb.st_ctime +
13142255Sbostic 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
13240947Sbostic }
13340947Sbostic 
13440947Sbostic PLAN *
13540947Sbostic c_ctime(arg)
13640947Sbostic 	char *arg;
13740947Sbostic {
13840947Sbostic 	PLAN *new;
13940947Sbostic 
14040947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
14140947Sbostic 
14249868Sbostic 	new = palloc(N_CTIME, f_ctime);
14340947Sbostic 	new->t_data = find_parsenum(new, "-ctime", arg, (char *)NULL);
14440947Sbostic 	return(new);
14540947Sbostic }
14640947Sbostic 
14740947Sbostic /*
14840947Sbostic  * -depth functions --
14940947Sbostic  *
15040947Sbostic  *	Always true, causes descent of the directory hierarchy to be done
15140947Sbostic  *	so that all entries in a directory are acted on before the directory
15240947Sbostic  *	itself.
15340947Sbostic  */
15440947Sbostic /* ARGSUSED */
15540947Sbostic f_always_true(plan, entry)
15640947Sbostic 	PLAN *plan;
15740947Sbostic 	FTSENT *entry;
15840947Sbostic {
15942255Sbostic 	return(1);
16040947Sbostic }
16140947Sbostic 
16240947Sbostic PLAN *
16340947Sbostic c_depth()
16440947Sbostic {
16545615Sbostic 	isdepth = 1;
16640947Sbostic 
16749868Sbostic 	return(palloc(N_DEPTH, f_always_true));
16840947Sbostic }
16940947Sbostic 
17040947Sbostic /*
17140947Sbostic  * [-exec | -ok] utility [arg ... ] ; functions --
17240947Sbostic  *
17340947Sbostic  *	True if the executed utility returns a zero value as exit status.
17440947Sbostic  *	The end of the primary expression is delimited by a semicolon.  If
17540947Sbostic  *	"{}" occurs anywhere, it gets replaced by the current pathname.
17640947Sbostic  *	The current directory for the execution of utility is the same as
17740947Sbostic  *	the current directory when the find utility was started.
17840947Sbostic  *
17940947Sbostic  *	The primary -ok is different in that it requests affirmation of the
18040947Sbostic  *	user before executing the utility.
18140947Sbostic  */
18240947Sbostic f_exec(plan, entry)
18340947Sbostic 	register PLAN *plan;
18440947Sbostic 	FTSENT *entry;
18540947Sbostic {
186*49869Sbostic 	extern int dotfd;
18740947Sbostic 	register int cnt;
18847192Sbostic 	pid_t pid;
18947192Sbostic 	int status;
19040947Sbostic 
19140947Sbostic 	for (cnt = 0; plan->e_argv[cnt]; ++cnt)
19240947Sbostic 		if (plan->e_len[cnt])
19345616Sbostic 			brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
194*49869Sbostic 			    entry->fts_path, plan->e_len[cnt]);
19540947Sbostic 
19645616Sbostic 	if (plan->flags && !queryuser(plan->e_argv))
19742255Sbostic 		return(0);
19840947Sbostic 
19940947Sbostic 	switch(pid = vfork()) {
20040947Sbostic 	case -1:
20149868Sbostic 		err("fork: %s", strerror(errno));
20240947Sbostic 		/* NOTREACHED */
20340947Sbostic 	case 0:
204*49869Sbostic 		if (fchdir(dotfd)) {
205*49869Sbostic 			(void)fprintf(stderr,
206*49869Sbostic 			    "find: chdir: %s\n", strerror(errno));
207*49869Sbostic 			_exit(1);
208*49869Sbostic 		}
20940947Sbostic 		execvp(plan->e_argv[0], plan->e_argv);
210*49869Sbostic 		(void)fprintf(stderr,
211*49869Sbostic 		    "find: %s: %s\n", plan->e_argv[0], strerror(errno));
212*49869Sbostic 		_exit(1);
21340947Sbostic 	}
21447192Sbostic 	pid = waitpid(pid, &status, 0);
21547192Sbostic 	return(pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
21640947Sbostic }
21740947Sbostic 
21840947Sbostic /*
21940947Sbostic  * c_exec --
22040947Sbostic  *	build three parallel arrays, one with pointers to the strings passed
22140947Sbostic  *	on the command line, one with (possibly duplicated) pointers to the
22240947Sbostic  *	argv array, and one with integer values that are lengths of the
22340947Sbostic  *	strings, but also flags meaning that the string has to be massaged.
22440947Sbostic  */
22540947Sbostic PLAN *
22640947Sbostic c_exec(argvp, isok)
22740947Sbostic 	char ***argvp;
22840947Sbostic 	int isok;
22940947Sbostic {
23040947Sbostic 	PLAN *new;			/* node returned */
23140947Sbostic 	register int cnt;
23240947Sbostic 	register char **argv, **ap, *p;
23340947Sbostic 
23447192Sbostic 	isoutput = 1;
23540947Sbostic 
23649868Sbostic 	new = palloc(N_EXEC, f_exec);
23740947Sbostic 	new->flags = isok;
23840947Sbostic 
23940947Sbostic 	for (ap = argv = *argvp;; ++ap) {
24040947Sbostic 		if (!*ap)
24149868Sbostic 			err("%s: %s",
24249868Sbostic 			    isok ? "-ok" : "-exec", "no terminating \";\"");
24340947Sbostic 		if (**ap == ';')
24440947Sbostic 			break;
24540947Sbostic 	}
24640947Sbostic 
24740947Sbostic 	cnt = ap - *argvp + 1;
24840947Sbostic 	new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
24940947Sbostic 	new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
25045868Sbostic 	new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
25140947Sbostic 
25240947Sbostic 	for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
25340947Sbostic 		new->e_orig[cnt] = *argv;
25440947Sbostic 		for (p = *argv; *p; ++p)
25540947Sbostic 			if (p[0] == '{' && p[1] == '}') {
25645868Sbostic 				new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
25745868Sbostic 				new->e_len[cnt] = MAXPATHLEN;
25840947Sbostic 				break;
25940947Sbostic 			}
26040947Sbostic 		if (!*p) {
26140947Sbostic 			new->e_argv[cnt] = *argv;
26240947Sbostic 			new->e_len[cnt] = 0;
26340947Sbostic 		}
26440947Sbostic 	}
26540947Sbostic 	new->e_argv[cnt] = new->e_orig[cnt] = NULL;
26640947Sbostic 
26740947Sbostic 	*argvp = argv + 1;
26840947Sbostic 	return(new);
26940947Sbostic }
27040947Sbostic 
27140947Sbostic /*
27240947Sbostic  * -follow functions --
27340947Sbostic  *
27440947Sbostic  *	Always true, causes symbolic links to be followed on a global
27540947Sbostic  *	basis.
27640947Sbostic  */
27740947Sbostic PLAN *
27840947Sbostic c_follow()
27940947Sbostic {
28040947Sbostic 	ftsoptions &= ~FTS_PHYSICAL;
28140947Sbostic 	ftsoptions |= FTS_LOGICAL;
28240947Sbostic 
28349868Sbostic 	return(palloc(N_FOLLOW, f_always_true));
28440947Sbostic }
28540947Sbostic 
28640947Sbostic /*
28740947Sbostic  * -fstype functions --
28840947Sbostic  *
28940947Sbostic  *	True if the file is of a certain type.
29040947Sbostic  */
29140947Sbostic f_fstype(plan, entry)
29240947Sbostic 	PLAN *plan;
29340947Sbostic 	FTSENT *entry;
29440947Sbostic {
29542275Sbostic 	static dev_t curdev;	/* need a guaranteed illegal dev value */
29642275Sbostic 	static int first = 1;
29740947Sbostic 	struct statfs sb;
29842255Sbostic 	static short val;
29945626Sbostic 	char *p, save[2];
30040947Sbostic 
30140947Sbostic 	/* only check when we cross mount point */
30242275Sbostic 	if (first || curdev != entry->fts_statb.st_dev) {
30343856Sbostic 		curdev = entry->fts_statb.st_dev;
30445626Sbostic 
30545626Sbostic 		/*
30645626Sbostic 		 * Statfs follows symlinks; find wants the link's file system,
30745626Sbostic 		 * not where it points.
30845626Sbostic 		 */
30945626Sbostic 		if (entry->fts_info == FTS_SL ||
31045626Sbostic 		    entry->fts_info == FTS_SLNONE) {
31145626Sbostic 			if (p = rindex(entry->fts_accpath, '/'))
31245626Sbostic 				++p;
31345626Sbostic 			else
31445626Sbostic 				p = entry->fts_accpath;
31545626Sbostic 			save[0] = p[0];
31645626Sbostic 			p[0] = '.';
31745626Sbostic 			save[1] = p[1];
31845626Sbostic 			p[1] = '\0';
31945626Sbostic 
32045626Sbostic 		} else
32145626Sbostic 			p = NULL;
32245626Sbostic 
32349868Sbostic 		if (statfs(entry->fts_accpath, &sb))
32449868Sbostic 			err("%s: %s", entry->fts_accpath, strerror(errno));
32545626Sbostic 
32645626Sbostic 		if (p) {
32745626Sbostic 			p[0] = save[0];
32845626Sbostic 			p[1] = save[1];
32945626Sbostic 		}
33045626Sbostic 
33142275Sbostic 		first = 0;
33242255Sbostic 		val = plan->flags == MOUNT_NONE ? sb.f_flags : sb.f_type;
33340947Sbostic 	}
33442255Sbostic 	return(plan->flags == MOUNT_NONE ?
33542255Sbostic 	    val & MNT_LOCAL : val == plan->flags);
33640947Sbostic }
33740947Sbostic 
33840947Sbostic PLAN *
33940947Sbostic c_fstype(arg)
34040947Sbostic 	char *arg;
34140947Sbostic {
34240947Sbostic 	register PLAN *new;
34340947Sbostic 
34440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
34540947Sbostic 
34649868Sbostic 	new = palloc(N_FSTYPE, f_fstype);
34740947Sbostic 	switch(*arg) {
34842255Sbostic 	case 'l':
34942255Sbostic 		if (!strcmp(arg, "local")) {
35042255Sbostic 			new->flags = MOUNT_NONE;
35142255Sbostic 			return(new);
35242255Sbostic 		}
35342255Sbostic 		break;
35440947Sbostic 	case 'm':
35540947Sbostic 		if (!strcmp(arg, "mfs")) {
35640947Sbostic 			new->flags = MOUNT_MFS;
35740947Sbostic 			return(new);
35840947Sbostic 		}
35940947Sbostic 		break;
36040947Sbostic 	case 'n':
36140947Sbostic 		if (!strcmp(arg, "nfs")) {
36240947Sbostic 			new->flags = MOUNT_NFS;
36340947Sbostic 			return(new);
36440947Sbostic 		}
36540947Sbostic 		break;
36640947Sbostic 	case 'p':
36740947Sbostic 		if (!strcmp(arg, "pc")) {
36840947Sbostic 			new->flags = MOUNT_PC;
36940947Sbostic 			return(new);
37040947Sbostic 		}
37140947Sbostic 		break;
37240947Sbostic 	case 'u':
37340947Sbostic 		if (!strcmp(arg, "ufs")) {
37440947Sbostic 			new->flags = MOUNT_UFS;
37540947Sbostic 			return(new);
37640947Sbostic 		}
37740947Sbostic 		break;
37840947Sbostic 	}
37949868Sbostic 	err("unknown file type %s", arg);
38040947Sbostic 	/* NOTREACHED */
38140947Sbostic }
38240947Sbostic 
38340947Sbostic /*
38440947Sbostic  * -group gname functions --
38540947Sbostic  *
38640947Sbostic  *	True if the file belongs to the group gname.  If gname is numeric and
38740947Sbostic  *	an equivalent of the getgrnam() function does not return a valid group
38840947Sbostic  *	name, gname is taken as a group ID.
38940947Sbostic  */
39040947Sbostic f_group(plan, entry)
39140947Sbostic 	PLAN *plan;
39240947Sbostic 	FTSENT *entry;
39340947Sbostic {
39442255Sbostic 	return(entry->fts_statb.st_gid == plan->g_data);
39540947Sbostic }
39640947Sbostic 
39740947Sbostic PLAN *
39840947Sbostic c_group(gname)
39940947Sbostic 	char *gname;
40040947Sbostic {
40140947Sbostic 	PLAN *new;
40240947Sbostic 	struct group *g;
40340947Sbostic 	gid_t gid;
40440947Sbostic 
40540947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
40640947Sbostic 
40740947Sbostic 	g = getgrnam(gname);
40840947Sbostic 	if (g == NULL) {
40940947Sbostic 		gid = atoi(gname);
41040947Sbostic 		if (gid == 0 && gname[0] != '0')
41149868Sbostic 			err("%s: %s", "-group", "no such group");
41240947Sbostic 	} else
41340947Sbostic 		gid = g->gr_gid;
41440947Sbostic 
41549868Sbostic 	new = palloc(N_GROUP, f_group);
41640947Sbostic 	new->g_data = gid;
41740947Sbostic 	return(new);
41840947Sbostic }
41940947Sbostic 
42040947Sbostic /*
42140947Sbostic  * -inum n functions --
42240947Sbostic  *
42340947Sbostic  *	True if the file has inode # n.
42440947Sbostic  */
42540947Sbostic f_inum(plan, entry)
42640947Sbostic 	PLAN *plan;
42740947Sbostic 	FTSENT *entry;
42840947Sbostic {
42942255Sbostic 	COMPARE(entry->fts_statb.st_ino, plan->i_data);
43040947Sbostic }
43140947Sbostic 
43240947Sbostic PLAN *
43340947Sbostic c_inum(arg)
43440947Sbostic 	char *arg;
43540947Sbostic {
43640947Sbostic 	PLAN *new;
43740947Sbostic 
43840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
43940947Sbostic 
44049868Sbostic 	new = palloc(N_INUM, f_inum);
44140947Sbostic 	new->i_data = find_parsenum(new, "-inum", arg, (char *)NULL);
44240947Sbostic 	return(new);
44340947Sbostic }
44440947Sbostic 
44540947Sbostic /*
44640947Sbostic  * -links n functions --
44740947Sbostic  *
44840947Sbostic  *	True if the file has n links.
44940947Sbostic  */
45040947Sbostic f_links(plan, entry)
45140947Sbostic 	PLAN *plan;
45240947Sbostic 	FTSENT *entry;
45340947Sbostic {
45442255Sbostic 	COMPARE(entry->fts_statb.st_nlink, plan->l_data);
45540947Sbostic }
45640947Sbostic 
45740947Sbostic PLAN *
45840947Sbostic c_links(arg)
45940947Sbostic 	char *arg;
46040947Sbostic {
46140947Sbostic 	PLAN *new;
46240947Sbostic 
46340947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
46440947Sbostic 
46549868Sbostic 	new = palloc(N_LINKS, f_links);
46645616Sbostic 	new->l_data = (nlink_t)find_parsenum(new, "-links", arg, (char *)NULL);
46740947Sbostic 	return(new);
46840947Sbostic }
46940947Sbostic 
47040947Sbostic /*
47140947Sbostic  * -ls functions --
47240947Sbostic  *
47340947Sbostic  *	Always true - prints the current entry to stdout in "ls" format.
47440947Sbostic  */
47540947Sbostic /* ARGSUSED */
47640947Sbostic f_ls(plan, entry)
47740947Sbostic 	PLAN *plan;
47840947Sbostic 	FTSENT *entry;
47940947Sbostic {
48042255Sbostic 	printlong(entry->fts_path, entry->fts_accpath, &entry->fts_statb);
48142255Sbostic 	return(1);
48240947Sbostic }
48340947Sbostic 
48440947Sbostic PLAN *
48540947Sbostic c_ls()
48640947Sbostic {
48740947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
48845615Sbostic 	isoutput = 1;
48940947Sbostic 
49049868Sbostic 	return(palloc(N_LS, f_ls));
49140947Sbostic }
49240947Sbostic 
49340947Sbostic /*
49440947Sbostic  * -name functions --
49540947Sbostic  *
49640947Sbostic  *	True if the basename of the filename being examined
49740947Sbostic  *	matches pattern using Pattern Matching Notation S3.14
49840947Sbostic  */
49940947Sbostic f_name(plan, entry)
50040947Sbostic 	PLAN *plan;
50140947Sbostic 	FTSENT *entry;
50240947Sbostic {
50342255Sbostic 	return(fnmatch(plan->c_data, entry->fts_name, FNM_QUOTE));
50440947Sbostic }
50540947Sbostic 
50640947Sbostic PLAN *
50740947Sbostic c_name(pattern)
50840947Sbostic 	char *pattern;
50940947Sbostic {
51040947Sbostic 	PLAN *new;
51140947Sbostic 
51249868Sbostic 	new = palloc(N_NAME, f_name);
51340947Sbostic 	new->c_data = pattern;
51440947Sbostic 	return(new);
51540947Sbostic }
51640947Sbostic 
51740947Sbostic /*
51840947Sbostic  * -newer file functions --
51940947Sbostic  *
52040947Sbostic  *	True if the current file has been modified more recently
52140947Sbostic  *	then the modification time of the file named by the pathname
52240947Sbostic  *	file.
52340947Sbostic  */
52440947Sbostic f_newer(plan, entry)
52540947Sbostic 	PLAN *plan;
52640947Sbostic 	FTSENT *entry;
52740947Sbostic {
52842255Sbostic 	return(entry->fts_statb.st_mtime > plan->t_data);
52940947Sbostic }
53040947Sbostic 
53140947Sbostic PLAN *
53240947Sbostic c_newer(filename)
53340947Sbostic 	char *filename;
53440947Sbostic {
53540947Sbostic 	PLAN *new;
53640947Sbostic 	struct stat sb;
53740947Sbostic 
53840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
53940947Sbostic 
54049868Sbostic 	if (stat(filename, &sb))
54149868Sbostic 		err("%s: %s", filename, strerror(errno));
54249868Sbostic 	new = palloc(N_NEWER, f_newer);
54340947Sbostic 	new->t_data = sb.st_mtime;
54440947Sbostic 	return(new);
54540947Sbostic }
54640947Sbostic 
54740947Sbostic /*
54840947Sbostic  * -nogroup functions --
54940947Sbostic  *
55040947Sbostic  *	True if file belongs to a user ID for which the equivalent
55140947Sbostic  *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
55240947Sbostic  */
55340947Sbostic /* ARGSUSED */
55440947Sbostic f_nogroup(plan, entry)
55540947Sbostic 	PLAN *plan;
55640947Sbostic 	FTSENT *entry;
55740947Sbostic {
55845615Sbostic 	char *group_from_gid();
55945615Sbostic 
56045615Sbostic 	return(group_from_gid(entry->fts_statb.st_gid, 1) ? 1 : 0);
56140947Sbostic }
56240947Sbostic 
56340947Sbostic PLAN *
56440947Sbostic c_nogroup()
56540947Sbostic {
56640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
56740947Sbostic 
56849868Sbostic 	return(palloc(N_NOGROUP, f_nogroup));
56940947Sbostic }
57040947Sbostic 
57140947Sbostic /*
57240947Sbostic  * -nouser functions --
57340947Sbostic  *
57440947Sbostic  *	True if file belongs to a user ID for which the equivalent
57540947Sbostic  *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
57640947Sbostic  */
57740947Sbostic /* ARGSUSED */
57840947Sbostic f_nouser(plan, entry)
57940947Sbostic 	PLAN *plan;
58040947Sbostic 	FTSENT *entry;
58140947Sbostic {
58245615Sbostic 	char *user_from_uid();
58345615Sbostic 
58445615Sbostic 	return(user_from_uid(entry->fts_statb.st_uid, 1) ? 1 : 0);
58540947Sbostic }
58640947Sbostic 
58740947Sbostic PLAN *
58840947Sbostic c_nouser()
58940947Sbostic {
59040947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
59140947Sbostic 
59249868Sbostic 	return(palloc(N_NOUSER, f_nouser));
59340947Sbostic }
59440947Sbostic 
59540947Sbostic /*
59640947Sbostic  * -perm functions --
59740947Sbostic  *
59840947Sbostic  *	The mode argument is used to represent file mode bits.  If it starts
59940947Sbostic  *	with a leading digit, it's treated as an octal mode, otherwise as a
60040947Sbostic  *	symbolic mode.
60140947Sbostic  */
60240947Sbostic f_perm(plan, entry)
60340947Sbostic 	PLAN *plan;
60440947Sbostic 	FTSENT *entry;
60540947Sbostic {
60640947Sbostic 	mode_t mode;
60740947Sbostic 
60842255Sbostic 	mode = entry->fts_statb.st_mode &
60940947Sbostic 	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
61040947Sbostic 	if (plan->flags)
61140947Sbostic 		return((plan->m_data | mode) == mode);
61240947Sbostic 	else
61340947Sbostic 		return(mode == plan->m_data);
61440947Sbostic 	/* NOTREACHED */
61540947Sbostic }
61640947Sbostic 
61740947Sbostic PLAN *
61840947Sbostic c_perm(perm)
61940947Sbostic 	char *perm;
62040947Sbostic {
62140947Sbostic 	PLAN *new;
62247192Sbostic 	mode_t *set;
62340947Sbostic 
62440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
62540947Sbostic 
62649868Sbostic 	new = palloc(N_PERM, f_perm);
62740947Sbostic 
62840947Sbostic 	if (*perm == '-') {
62940947Sbostic 		new->flags = 1;
63040947Sbostic 		++perm;
63140947Sbostic 	}
63240947Sbostic 
63344764Strent 	if ((set = setmode(perm)) == NULL)
63449868Sbostic 		err("%s: %s", "-perm", "illegal mode string");
63540947Sbostic 
63644764Strent 	new->m_data = getmode(set, 0);
63740947Sbostic 	return(new);
63840947Sbostic }
63940947Sbostic 
64040947Sbostic /*
64140947Sbostic  * -print functions --
64240947Sbostic  *
64340947Sbostic  *	Always true, causes the current pathame to be written to
64440947Sbostic  *	standard output.
64540947Sbostic  */
64640947Sbostic /* ARGSUSED */
64740947Sbostic f_print(plan, entry)
64840947Sbostic 	PLAN *plan;
64940947Sbostic 	FTSENT *entry;
65040947Sbostic {
65142255Sbostic 	(void)printf("%s\n", entry->fts_path);
65242255Sbostic 	return(1);
65340947Sbostic }
65440947Sbostic 
65540947Sbostic PLAN *
65640947Sbostic c_print()
65740947Sbostic {
65845615Sbostic 	isoutput = 1;
65940947Sbostic 
66049868Sbostic 	return(palloc(N_PRINT, f_print));
66140947Sbostic }
66240947Sbostic 
66340947Sbostic /*
66440947Sbostic  * -prune functions --
66540947Sbostic  *
66640947Sbostic  *	Prune a portion of the hierarchy.
66740947Sbostic  */
66840947Sbostic /* ARGSUSED */
66940947Sbostic f_prune(plan, entry)
67040947Sbostic 	PLAN *plan;
67140947Sbostic 	FTSENT *entry;
67240947Sbostic {
67340947Sbostic 	extern FTS *tree;
67440947Sbostic 
67549868Sbostic 	if (fts_set(tree, entry, FTS_SKIP))
67649868Sbostic 		err("%s: %s", entry->fts_path, strerror(errno));
67742255Sbostic 	return(1);
67840947Sbostic }
67940947Sbostic 
68040947Sbostic PLAN *
68140947Sbostic c_prune()
68240947Sbostic {
68349868Sbostic 	return(palloc(N_PRUNE, f_prune));
68440947Sbostic }
68540947Sbostic 
68640947Sbostic /*
68740947Sbostic  * -size n[c] functions --
68840947Sbostic  *
68940947Sbostic  *	True if the file size in bytes, divided by an implementation defined
69040947Sbostic  *	value and rounded up to the next integer, is n.  If n is followed by
69140947Sbostic  *	a c, the size is in bytes.
69240947Sbostic  */
69340947Sbostic #define	FIND_SIZE	512
69440947Sbostic static int divsize = 1;
69540947Sbostic 
69640947Sbostic f_size(plan, entry)
69740947Sbostic 	PLAN *plan;
69840947Sbostic 	FTSENT *entry;
69940947Sbostic {
70040947Sbostic 	off_t size;
70140947Sbostic 
70242255Sbostic 	size = divsize ? (entry->fts_statb.st_size + FIND_SIZE - 1) /
70342255Sbostic 	    FIND_SIZE : entry->fts_statb.st_size;
70440947Sbostic 	COMPARE(size, plan->o_data);
70540947Sbostic }
70640947Sbostic 
70740947Sbostic PLAN *
70840947Sbostic c_size(arg)
70940947Sbostic 	char *arg;
71040947Sbostic {
71140947Sbostic 	PLAN *new;
71240947Sbostic 	char endch;
71340947Sbostic 
71440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
71540947Sbostic 
71649868Sbostic 	new = palloc(N_SIZE, f_size);
71740947Sbostic 	new->o_data = find_parsenum(new, "-size", arg, &endch);
71840947Sbostic 	if (endch == 'c')
71940947Sbostic 		divsize = 0;
72040947Sbostic 	return(new);
72140947Sbostic }
72240947Sbostic 
72340947Sbostic /*
72440947Sbostic  * -type c functions --
72540947Sbostic  *
72640947Sbostic  *	True if the type of the file is c, where c is b, c, d, p, or f for
72740947Sbostic  *	block special file, character special file, directory, FIFO, or
72840947Sbostic  *	regular file, respectively.
72940947Sbostic  */
73040947Sbostic f_type(plan, entry)
73140947Sbostic 	PLAN *plan;
73240947Sbostic 	FTSENT *entry;
73340947Sbostic {
73444764Strent 	return((entry->fts_statb.st_mode & S_IFMT) == plan->m_data);
73540947Sbostic }
73640947Sbostic 
73740947Sbostic PLAN *
73840947Sbostic c_type(typestring)
73940947Sbostic 	char *typestring;
74040947Sbostic {
74140947Sbostic 	PLAN *new;
74240947Sbostic 	mode_t  mask;
74340947Sbostic 
74440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
74540947Sbostic 
74640947Sbostic 	switch (typestring[0]) {
74740947Sbostic 	case 'b':
74840947Sbostic 		mask = S_IFBLK;
74940947Sbostic 		break;
75040947Sbostic 	case 'c':
75140947Sbostic 		mask = S_IFCHR;
75240947Sbostic 		break;
75340947Sbostic 	case 'd':
75440947Sbostic 		mask = S_IFDIR;
75540947Sbostic 		break;
75640947Sbostic 	case 'f':
75740947Sbostic 		mask = S_IFREG;
75840947Sbostic 		break;
75940947Sbostic 	case 'l':
76040947Sbostic 		mask = S_IFLNK;
76140947Sbostic 		break;
76240947Sbostic 	case 'p':
76340947Sbostic 		mask = S_IFIFO;
76440947Sbostic 		break;
76540947Sbostic 	case 's':
76640947Sbostic 		mask = S_IFSOCK;
76740947Sbostic 		break;
76840947Sbostic 	default:
76949868Sbostic 		err("%s: %s", "-type", "unknown type");
77040947Sbostic 	}
77140947Sbostic 
77249868Sbostic 	new = palloc(N_TYPE, f_type);
77340947Sbostic 	new->m_data = mask;
77440947Sbostic 	return(new);
77540947Sbostic }
77640947Sbostic 
77740947Sbostic /*
77840947Sbostic  * -user uname functions --
77940947Sbostic  *
78040947Sbostic  *	True if the file belongs to the user uname.  If uname is numeric and
78140947Sbostic  *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
78240947Sbostic  *	return a valid user name, uname is taken as a user ID.
78340947Sbostic  */
78440947Sbostic f_user(plan, entry)
78540947Sbostic 	PLAN *plan;
78640947Sbostic 	FTSENT *entry;
78740947Sbostic {
78842255Sbostic 	return(entry->fts_statb.st_uid == plan->u_data);
78940947Sbostic }
79040947Sbostic 
79140947Sbostic PLAN *
79240947Sbostic c_user(username)
79340947Sbostic 	char *username;
79440947Sbostic {
79540947Sbostic 	PLAN *new;
79640947Sbostic 	struct passwd *p;
79740947Sbostic 	uid_t uid;
79840947Sbostic 
79940947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
80040947Sbostic 
80140947Sbostic 	p = getpwnam(username);
80240947Sbostic 	if (p == NULL) {
80340947Sbostic 		uid = atoi(username);
80440947Sbostic 		if (uid == 0 && username[0] != '0')
80549868Sbostic 			err("%s: %s", "-user", "no such user");
80640947Sbostic 	} else
80740947Sbostic 		uid = p->pw_uid;
80840947Sbostic 
80949868Sbostic 	new = palloc(N_USER, f_user);
81040947Sbostic 	new->u_data = uid;
81140947Sbostic 	return(new);
81240947Sbostic }
81340947Sbostic 
81440947Sbostic /*
81540947Sbostic  * -xdev functions --
81640947Sbostic  *
81740947Sbostic  *	Always true, causes find not to decend past directories that have a
81840947Sbostic  *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
81940947Sbostic  */
82040947Sbostic PLAN *
82140947Sbostic c_xdev()
82240947Sbostic {
82342275Sbostic 	ftsoptions |= FTS_XDEV;
82440947Sbostic 
82549868Sbostic 	return(palloc(N_XDEV, f_always_true));
82640947Sbostic }
82740947Sbostic 
82840947Sbostic /*
82940947Sbostic  * ( expression ) functions --
83040947Sbostic  *
83140947Sbostic  *	True if expression is true.
83240947Sbostic  */
83340947Sbostic f_expr(plan, entry)
83440947Sbostic 	PLAN *plan;
83540947Sbostic 	FTSENT *entry;
83640947Sbostic {
83740947Sbostic 	register PLAN *p;
83840947Sbostic 	register int state;
83940947Sbostic 
84040947Sbostic 	for (p = plan->p_data[0];
84140947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
84240947Sbostic 	return(state);
84340947Sbostic }
84440947Sbostic 
84540947Sbostic /*
84649864Sbostic  * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
84740947Sbostic  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
84849864Sbostic  * to a N_EXPR node containing the expression and the ')' node is discarded.
84940947Sbostic  */
85040947Sbostic PLAN *
85140947Sbostic c_openparen()
85240947Sbostic {
85349868Sbostic 	return(palloc(N_OPENPAREN, (int (*)())-1));
85440947Sbostic }
85540947Sbostic 
85640947Sbostic PLAN *
85740947Sbostic c_closeparen()
85840947Sbostic {
85949868Sbostic 	return(palloc(N_CLOSEPAREN, (int (*)())-1));
86040947Sbostic }
86140947Sbostic 
86240947Sbostic /*
86340947Sbostic  * -mtime n functions --
86440947Sbostic  *
86540947Sbostic  *	True if the difference between the file modification time and the
86640947Sbostic  *	current time is n 24 hour periods.
86740947Sbostic  */
86840947Sbostic f_mtime(plan, entry)
86940947Sbostic 	PLAN *plan;
87040947Sbostic 	FTSENT *entry;
87140947Sbostic {
87240947Sbostic 	extern time_t now;
87340947Sbostic 
87442255Sbostic 	COMPARE((now - entry->fts_statb.st_mtime + SECSPERDAY - 1) /
87542255Sbostic 	    SECSPERDAY, plan->t_data);
87640947Sbostic }
87740947Sbostic 
87840947Sbostic PLAN *
87940947Sbostic c_mtime(arg)
88040947Sbostic 	char *arg;
88140947Sbostic {
88240947Sbostic 	PLAN *new;
88340947Sbostic 
88440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
88540947Sbostic 
88649868Sbostic 	new = palloc(N_MTIME, f_mtime);
88740947Sbostic 	new->t_data = find_parsenum(new, "-mtime", arg, (char *)NULL);
88840947Sbostic 	return(new);
88940947Sbostic }
89040947Sbostic 
89140947Sbostic /*
89240947Sbostic  * ! expression functions --
89340947Sbostic  *
89440947Sbostic  *	Negation of a primary; the unary NOT operator.
89540947Sbostic  */
89640947Sbostic f_not(plan, entry)
89740947Sbostic 	PLAN *plan;
89840947Sbostic 	FTSENT *entry;
89940947Sbostic {
90040947Sbostic 	register PLAN *p;
90140947Sbostic 	register int state;
90240947Sbostic 
90340947Sbostic 	for (p = plan->p_data[0];
90440947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
90540947Sbostic 	return(!state);
90640947Sbostic }
90740947Sbostic 
90840947Sbostic PLAN *
90940947Sbostic c_not()
91040947Sbostic {
91149868Sbostic 	return(palloc(N_NOT, f_not));
91240947Sbostic }
91340947Sbostic 
91440947Sbostic /*
91540947Sbostic  * expression -o expression functions --
91640947Sbostic  *
91740947Sbostic  *	Alternation of primaries; the OR operator.  The second expression is
91840947Sbostic  * not evaluated if the first expression is true.
91940947Sbostic  */
92040947Sbostic f_or(plan, entry)
92140947Sbostic 	PLAN *plan;
92240947Sbostic 	FTSENT *entry;
92340947Sbostic {
92440947Sbostic 	register PLAN *p;
92540947Sbostic 	register int state;
92640947Sbostic 
92740947Sbostic 	for (p = plan->p_data[0];
92840947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
92940947Sbostic 
93040947Sbostic 	if (state)
93142255Sbostic 		return(1);
93240947Sbostic 
93340947Sbostic 	for (p = plan->p_data[1];
93440947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
93540947Sbostic 	return(state);
93640947Sbostic }
93740947Sbostic 
93840947Sbostic PLAN *
93940947Sbostic c_or()
94040947Sbostic {
94149868Sbostic 	return(palloc(N_OR, f_or));
94249868Sbostic }
94349868Sbostic 
94449868Sbostic static PLAN *
94549868Sbostic palloc(t, f)
94649868Sbostic 	enum ntype t;
94749868Sbostic 	int (*f)();
94849868Sbostic {
94940947Sbostic 	PLAN *new;
95040947Sbostic 
95149868Sbostic 	if (new = malloc(sizeof(PLAN))) {
95249868Sbostic 		new->type = t;
95349868Sbostic 		new->eval = f;
95449868Sbostic 		new->flags = 0;
95549868Sbostic 		new->next = NULL;
95649868Sbostic 		return(new);
95749868Sbostic 	}
95849868Sbostic 	err("%s", strerror(errno));
95949868Sbostic 	/* NOTREACHED */
96040947Sbostic }
961