xref: /csrg-svn/usr.bin/find/function.c (revision 42255)
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*42255Sbostic static char sccsid[] = "@(#)function.c	5.4 (Berkeley) 05/20/90";
1340947Sbostic #endif /* not lint */
1440947Sbostic 
1540947Sbostic #include <sys/types.h>
1640947Sbostic #include <sys/stat.h>
1740947Sbostic #include <sys/wait.h>
1840947Sbostic #include <sys/mount.h>
1940947Sbostic #include <grp.h>
2040947Sbostic #include <pwd.h>
2140947Sbostic #include <fts.h>
2240947Sbostic #include <unistd.h>
2340947Sbostic #include <tzfile.h>
2440947Sbostic #include <stdio.h>
2542047Sbostic #include <string.h>
2640947Sbostic #include "find.h"
2740947Sbostic 
2840947Sbostic #define	FIND_EQUAL	0
2940947Sbostic #define	FIND_LESSTHAN	1
3040947Sbostic #define	FIND_GREATER	2
3140947Sbostic 
3240947Sbostic #define	COMPARE(a, b) { \
3340947Sbostic 	switch(plan->flags) { \
3440947Sbostic 	case FIND_EQUAL: \
3540947Sbostic 		return(a == b); \
3640947Sbostic 	case FIND_LESSTHAN: \
3740947Sbostic 		return(a < b); \
3840947Sbostic 	case FIND_GREATER: \
3940947Sbostic 		return(a > b); \
4040947Sbostic 	} \
41*42255Sbostic 	return(0); \
4240947Sbostic }
4340947Sbostic 
4440947Sbostic #define NEW(t, f) { \
4540947Sbostic 	new = (PLAN *)emalloc(sizeof(PLAN)); \
4640947Sbostic 	new->type = t; \
4740947Sbostic 	new->eval = f; \
4840947Sbostic 	new->flags = 0; \
4940947Sbostic 	new->next = NULL; \
5040947Sbostic }
5140947Sbostic 
5240947Sbostic /*
5340947Sbostic  * find_parsenum --
5440947Sbostic  *	Parse a string of the form [+-]# and return the value.
5540947Sbostic  */
5640947Sbostic long
5740947Sbostic find_parsenum(plan, option, str, endch)
5840947Sbostic 	PLAN *plan;
5940947Sbostic 	char *option, *str, *endch;
6040947Sbostic {
6140947Sbostic 	long value;
6240947Sbostic 	char *endchar;		/* pointer to character ending conversion */
6340947Sbostic 
6440947Sbostic 	/* determine comparison from leading + or - */
6540947Sbostic 	switch(*str) {
6640947Sbostic 	case '+':
6740947Sbostic 		++str;
6840947Sbostic 		plan->flags = FIND_GREATER;
6940947Sbostic 		break;
7040947Sbostic 	case '-':
7140947Sbostic 		++str;
7240947Sbostic 		plan->flags = FIND_LESSTHAN;
7340947Sbostic 		break;
7440947Sbostic 	default:
7540947Sbostic 		plan->flags = FIND_EQUAL;
7640947Sbostic 		break;
7740947Sbostic 	}
7840947Sbostic 
7940947Sbostic 	/*
8040947Sbostic 	 * 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);
8540947Sbostic 	if (!value && endchar == str ||
8640947Sbostic 	    endchar[0] && (!endch || endchar[0] != *endch))
8740947Sbostic 		bad_arg(option, "illegal numeric value");
8840947Sbostic 	if (endch)
8940947Sbostic 		*endch = endchar[0];
9040947Sbostic 	return(value);
9140947Sbostic }
9240947Sbostic 
9340947Sbostic /*
9440947Sbostic  * -atime n functions --
9540947Sbostic  *
9640947Sbostic  *	True if the difference between the file access time and the
9740947Sbostic  *	current time is n 24 hour periods.
9840947Sbostic  *
9940947Sbostic  */
10040947Sbostic f_atime(plan, entry)
10140947Sbostic 	PLAN *plan;
10240947Sbostic 	FTSENT *entry;
10340947Sbostic {
10440947Sbostic 	extern time_t now;
10540947Sbostic 
106*42255Sbostic 	COMPARE((now - entry->fts_statb.st_atime +
107*42255Sbostic 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
10840947Sbostic }
10940947Sbostic 
11040947Sbostic PLAN *
11140947Sbostic c_atime(arg)
11240947Sbostic 	char *arg;
11340947Sbostic {
11440947Sbostic 	PLAN *new;
11540947Sbostic 
11640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
11740947Sbostic 
11840947Sbostic 	NEW(T_ATIME, f_atime);
11940947Sbostic 	new->t_data = find_parsenum(new, "-atime", arg, (char *)NULL);
12040947Sbostic 	return(new);
12140947Sbostic }
12240947Sbostic /*
12340947Sbostic  * -ctime n functions --
12440947Sbostic  *
12540947Sbostic  *	True if the difference between the last change of file
12640947Sbostic  *	status information and the current time is n 24 hour periods.
12740947Sbostic  */
12840947Sbostic f_ctime(plan, entry)
12940947Sbostic 	PLAN *plan;
13040947Sbostic 	FTSENT *entry;
13140947Sbostic {
13240947Sbostic 	extern time_t now;
13340947Sbostic 
134*42255Sbostic 	COMPARE((now - entry->fts_statb.st_ctime +
135*42255Sbostic 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
13640947Sbostic }
13740947Sbostic 
13840947Sbostic PLAN *
13940947Sbostic c_ctime(arg)
14040947Sbostic 	char *arg;
14140947Sbostic {
14240947Sbostic 	PLAN *new;
14340947Sbostic 
14440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
14540947Sbostic 
14640947Sbostic 	NEW(T_CTIME, f_ctime);
14740947Sbostic 	new->t_data = find_parsenum(new, "-ctime", arg, (char *)NULL);
14840947Sbostic 	return(new);
14940947Sbostic }
15040947Sbostic 
15140947Sbostic /*
15240947Sbostic  * -depth functions --
15340947Sbostic  *
15440947Sbostic  *	Always true, causes descent of the directory hierarchy to be done
15540947Sbostic  *	so that all entries in a directory are acted on before the directory
15640947Sbostic  *	itself.
15740947Sbostic  */
15840947Sbostic /* ARGSUSED */
15940947Sbostic f_always_true(plan, entry)
16040947Sbostic 	PLAN *plan;
16140947Sbostic 	FTSENT *entry;
16240947Sbostic {
163*42255Sbostic 	return(1);
16440947Sbostic }
16540947Sbostic 
16640947Sbostic PLAN *
16740947Sbostic c_depth()
16840947Sbostic {
16940947Sbostic 	extern int depth;
17040947Sbostic 	PLAN *new;
17140947Sbostic 
17240947Sbostic 	depth = 1;
17340947Sbostic 
17440947Sbostic 	NEW(T_DEPTH, f_always_true);
17540947Sbostic 	return(new);
17640947Sbostic }
17740947Sbostic 
17840947Sbostic /*
17940947Sbostic  * [-exec | -ok] utility [arg ... ] ; functions --
18040947Sbostic  *
18140947Sbostic  *	True if the executed utility returns a zero value as exit status.
18240947Sbostic  *	The end of the primary expression is delimited by a semicolon.  If
18340947Sbostic  *	"{}" occurs anywhere, it gets replaced by the current pathname.
18440947Sbostic  *	The current directory for the execution of utility is the same as
18540947Sbostic  *	the current directory when the find utility was started.
18640947Sbostic  *
18740947Sbostic  *	The primary -ok is different in that it requests affirmation of the
18840947Sbostic  *	user before executing the utility.
18940947Sbostic  */
19040947Sbostic f_exec(plan, entry)
19140947Sbostic 	register PLAN *plan;
19240947Sbostic 	FTSENT *entry;
19340947Sbostic {
19440947Sbostic 	register int cnt;
19540947Sbostic 	char *find_subst();
19640947Sbostic 	union wait pstat;
19740947Sbostic 	pid_t pid, waitpid();
19840947Sbostic 
19940947Sbostic 	for (cnt = 0; plan->e_argv[cnt]; ++cnt)
20040947Sbostic 		if (plan->e_len[cnt])
20140947Sbostic 			find_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
202*42255Sbostic 			    entry->fts_path, plan->e_len[cnt]);
20340947Sbostic 
20440947Sbostic 	if (plan->flags && !find_queryuser(plan->e_argv))
205*42255Sbostic 		return(0);
20640947Sbostic 
20740947Sbostic 	switch(pid = vfork()) {
20840947Sbostic 	case -1:
20940947Sbostic 		(void)fprintf(stderr, "find: fork: %s.\n", strerror(errno));
21040947Sbostic 		exit(1);
21140947Sbostic 		/* NOTREACHED */
21240947Sbostic 	case 0:
21340947Sbostic 		execvp(plan->e_argv[0], plan->e_argv);
21440947Sbostic 		(void)fprintf(stderr,
21540947Sbostic 		    "find: %s: %s.\n", plan->e_argv[0], strerror(errno));
21640947Sbostic 		exit(1);
21740947Sbostic 		/* NOTREACHED */
21840947Sbostic 	}
21940947Sbostic 	pid = waitpid(pid, &pstat, 0);
220*42255Sbostic 	return(pid != -1 && !pstat.w_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 
23940947Sbostic 	ftsoptions |= FTS_NOCHDIR;
24040947Sbostic 	output_specified = 1;
24140947Sbostic 
24240947Sbostic 	NEW(T_EXEC, f_exec);
24340947Sbostic 	new->flags = isok;
24440947Sbostic 
24540947Sbostic 	for (ap = argv = *argvp;; ++ap) {
24640947Sbostic 		if (!*ap)
24740947Sbostic 			bad_arg(isok ? "-ok" : "-exec", "no terminating \";\"");
24840947Sbostic 		if (**ap == ';')
24940947Sbostic 			break;
25040947Sbostic 	}
25140947Sbostic 
25240947Sbostic 	cnt = ap - *argvp + 1;
25340947Sbostic 	new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
25440947Sbostic 	new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
25540947Sbostic 	new->e_len = (int *)emalloc((u_int)cnt * sizeof(u_char));
25640947Sbostic 
25740947Sbostic 	for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
25840947Sbostic 		new->e_orig[cnt] = *argv;
25940947Sbostic 		for (p = *argv; *p; ++p)
26040947Sbostic 			if (p[0] == '{' && p[1] == '}') {
26140947Sbostic 				new->e_argv[cnt] = emalloc((u_int)1024);
26240947Sbostic 				new->e_len[cnt] = 1024;
26340947Sbostic 				break;
26440947Sbostic 			}
26540947Sbostic 		if (!*p) {
26640947Sbostic 			new->e_argv[cnt] = *argv;
26740947Sbostic 			new->e_len[cnt] = 0;
26840947Sbostic 		}
26940947Sbostic 	}
27040947Sbostic 	new->e_argv[cnt] = new->e_orig[cnt] = NULL;
27140947Sbostic 
27240947Sbostic 	*argvp = argv + 1;
27340947Sbostic 	return(new);
27440947Sbostic }
27540947Sbostic 
27640947Sbostic /*
27740947Sbostic  * -follow functions --
27840947Sbostic  *
27940947Sbostic  *	Always true, causes symbolic links to be followed on a global
28040947Sbostic  *	basis.
28140947Sbostic  */
28240947Sbostic PLAN *
28340947Sbostic c_follow()
28440947Sbostic {
28540947Sbostic 	PLAN *new;
28640947Sbostic 
28740947Sbostic 	ftsoptions &= ~FTS_PHYSICAL;
28840947Sbostic 	ftsoptions |= FTS_LOGICAL;
28940947Sbostic 
29040947Sbostic 	NEW(T_FOLLOW, f_always_true);
29140947Sbostic 	return(new);
29240947Sbostic }
29340947Sbostic 
29440947Sbostic /*
29540947Sbostic  * -fstype functions --
29640947Sbostic  *
29740947Sbostic  *	True if the file is of a certain type.
29840947Sbostic  */
29940947Sbostic f_fstype(plan, entry)
30040947Sbostic 	PLAN *plan;
30140947Sbostic 	FTSENT *entry;
30240947Sbostic {
30340947Sbostic 	extern dev_t curdev;
30440947Sbostic 	struct statfs sb;
305*42255Sbostic 	static short val;
30640947Sbostic 
30740947Sbostic 	/* only check when we cross mount point */
308*42255Sbostic 	if (curdev != entry->fts_statb.st_dev) {
309*42255Sbostic 		if (statfs(entry->fts_accpath, &sb)) {
31040947Sbostic 			(void)fprintf(stderr, "find: %s: %s.\n",
311*42255Sbostic 			    entry->fts_accpath, strerror(errno));
31240947Sbostic 			exit(1);
31340947Sbostic 		}
314*42255Sbostic 		val = plan->flags == MOUNT_NONE ? sb.f_flags : sb.f_type;
31540947Sbostic 	}
316*42255Sbostic 	return(plan->flags == MOUNT_NONE ?
317*42255Sbostic 	    val & MNT_LOCAL : val == plan->flags);
31840947Sbostic }
31940947Sbostic 
32040947Sbostic PLAN *
32140947Sbostic c_fstype(arg)
32240947Sbostic 	char *arg;
32340947Sbostic {
32440947Sbostic 	register PLAN *new;
32540947Sbostic 
32640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
32740947Sbostic 
32840947Sbostic 	NEW(T_FSTYPE, f_fstype);
32940947Sbostic 	switch(*arg) {
330*42255Sbostic 	case 'l':
331*42255Sbostic 		if (!strcmp(arg, "local")) {
332*42255Sbostic 			new->flags = MOUNT_NONE;
333*42255Sbostic 			return(new);
334*42255Sbostic 		}
335*42255Sbostic 		break;
33640947Sbostic 	case 'm':
33740947Sbostic 		if (!strcmp(arg, "mfs")) {
33840947Sbostic 			new->flags = MOUNT_MFS;
33940947Sbostic 			return(new);
34040947Sbostic 		}
34140947Sbostic 		break;
34240947Sbostic 	case 'n':
34340947Sbostic 		if (!strcmp(arg, "nfs")) {
34440947Sbostic 			new->flags = MOUNT_NFS;
34540947Sbostic 			return(new);
34640947Sbostic 		}
34740947Sbostic 		break;
34840947Sbostic 	case 'p':
34940947Sbostic 		if (!strcmp(arg, "pc")) {
35040947Sbostic 			new->flags = MOUNT_PC;
35140947Sbostic 			return(new);
35240947Sbostic 		}
35340947Sbostic 		break;
35440947Sbostic 	case 'u':
35540947Sbostic 		if (!strcmp(arg, "ufs")) {
35640947Sbostic 			new->flags = MOUNT_UFS;
35740947Sbostic 			return(new);
35840947Sbostic 		}
35940947Sbostic 		break;
36040947Sbostic 	}
36140947Sbostic 	(void)fprintf(stderr, "find: unknown file type %s.\n", arg);
36240947Sbostic 	exit(1);
36340947Sbostic 	/* NOTREACHED */
36440947Sbostic }
36540947Sbostic 
36640947Sbostic /*
36740947Sbostic  * -group gname functions --
36840947Sbostic  *
36940947Sbostic  *	True if the file belongs to the group gname.  If gname is numeric and
37040947Sbostic  *	an equivalent of the getgrnam() function does not return a valid group
37140947Sbostic  *	name, gname is taken as a group ID.
37240947Sbostic  */
37340947Sbostic f_group(plan, entry)
37440947Sbostic 	PLAN *plan;
37540947Sbostic 	FTSENT *entry;
37640947Sbostic {
377*42255Sbostic 	return(entry->fts_statb.st_gid == plan->g_data);
37840947Sbostic }
37940947Sbostic 
38040947Sbostic PLAN *
38140947Sbostic c_group(gname)
38240947Sbostic 	char *gname;
38340947Sbostic {
38440947Sbostic 	PLAN *new;
38540947Sbostic 	struct group *g;
38640947Sbostic 	gid_t gid;
38740947Sbostic 
38840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
38940947Sbostic 
39040947Sbostic 	g = getgrnam(gname);
39140947Sbostic 	if (g == NULL) {
39240947Sbostic 		gid = atoi(gname);
39340947Sbostic 		if (gid == 0 && gname[0] != '0')
39440947Sbostic 			bad_arg("-group", "no such group");
39540947Sbostic 	} else
39640947Sbostic 		gid = g->gr_gid;
39740947Sbostic 
39840947Sbostic 	NEW(T_GROUP, f_group);
39940947Sbostic 	new->g_data = gid;
40040947Sbostic 	return(new);
40140947Sbostic }
40240947Sbostic 
40340947Sbostic /*
40440947Sbostic  * -inum n functions --
40540947Sbostic  *
40640947Sbostic  *	True if the file has inode # n.
40740947Sbostic  */
40840947Sbostic f_inum(plan, entry)
40940947Sbostic 	PLAN *plan;
41040947Sbostic 	FTSENT *entry;
41140947Sbostic {
412*42255Sbostic 	COMPARE(entry->fts_statb.st_ino, plan->i_data);
41340947Sbostic }
41440947Sbostic 
41540947Sbostic PLAN *
41640947Sbostic c_inum(arg)
41740947Sbostic 	char *arg;
41840947Sbostic {
41940947Sbostic 	PLAN *new;
42040947Sbostic 
42140947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
42240947Sbostic 
42340947Sbostic 	NEW(T_INUM, f_inum);
42440947Sbostic 	new->i_data = find_parsenum(new, "-inum", arg, (char *)NULL);
42540947Sbostic 	return(new);
42640947Sbostic }
42740947Sbostic 
42840947Sbostic /*
42940947Sbostic  * -links n functions --
43040947Sbostic  *
43140947Sbostic  *	True if the file has n links.
43240947Sbostic  */
43340947Sbostic f_links(plan, entry)
43440947Sbostic 	PLAN *plan;
43540947Sbostic 	FTSENT *entry;
43640947Sbostic {
437*42255Sbostic 	COMPARE(entry->fts_statb.st_nlink, plan->l_data);
43840947Sbostic }
43940947Sbostic 
44040947Sbostic PLAN *
44140947Sbostic c_links(arg)
44240947Sbostic 	char *arg;
44340947Sbostic {
44440947Sbostic 	PLAN *new;
44540947Sbostic 
44640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
44740947Sbostic 
44840947Sbostic 	NEW(T_LINKS, f_links);
44940947Sbostic 	new->l_data = find_parsenum(new, "-links", arg, (char *)NULL);
45040947Sbostic 	return(new);
45140947Sbostic }
45240947Sbostic 
45340947Sbostic /*
45440947Sbostic  * -ls functions --
45540947Sbostic  *
45640947Sbostic  *	Always true - prints the current entry to stdout in "ls" format.
45740947Sbostic  */
45840947Sbostic /* ARGSUSED */
45940947Sbostic f_ls(plan, entry)
46040947Sbostic 	PLAN *plan;
46140947Sbostic 	FTSENT *entry;
46240947Sbostic {
463*42255Sbostic 	printlong(entry->fts_path, entry->fts_accpath, &entry->fts_statb);
464*42255Sbostic 	return(1);
46540947Sbostic }
46640947Sbostic 
46740947Sbostic PLAN *
46840947Sbostic c_ls()
46940947Sbostic {
47040947Sbostic 	PLAN *new;
47140947Sbostic 
47240947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
47340947Sbostic 	output_specified = 1;
47440947Sbostic 
47540947Sbostic 	NEW(T_LS, f_ls);
47640947Sbostic 	return(new);
47740947Sbostic }
47840947Sbostic 
47940947Sbostic /*
48040947Sbostic  * -name functions --
48140947Sbostic  *
48240947Sbostic  *	True if the basename of the filename being examined
48340947Sbostic  *	matches pattern using Pattern Matching Notation S3.14
48440947Sbostic  */
48540947Sbostic f_name(plan, entry)
48640947Sbostic 	PLAN *plan;
48740947Sbostic 	FTSENT *entry;
48840947Sbostic {
489*42255Sbostic 	return(fnmatch(plan->c_data, entry->fts_name, FNM_QUOTE));
49040947Sbostic }
49140947Sbostic 
49240947Sbostic PLAN *
49340947Sbostic c_name(pattern)
49440947Sbostic 	char *pattern;
49540947Sbostic {
49640947Sbostic 	PLAN *new;
49740947Sbostic 
49840947Sbostic 	NEW(T_NAME, f_name);
49940947Sbostic 	new->c_data = pattern;
50040947Sbostic 	return(new);
50140947Sbostic }
50240947Sbostic 
50340947Sbostic /*
50440947Sbostic  * -newer file functions --
50540947Sbostic  *
50640947Sbostic  *	True if the current file has been modified more recently
50740947Sbostic  *	then the modification time of the file named by the pathname
50840947Sbostic  *	file.
50940947Sbostic  */
51040947Sbostic f_newer(plan, entry)
51140947Sbostic 	PLAN *plan;
51240947Sbostic 	FTSENT *entry;
51340947Sbostic {
514*42255Sbostic 	return(entry->fts_statb.st_mtime > plan->t_data);
51540947Sbostic }
51640947Sbostic 
51740947Sbostic PLAN *
51840947Sbostic c_newer(filename)
51940947Sbostic 	char *filename;
52040947Sbostic {
52140947Sbostic 	PLAN *new;
52240947Sbostic 	struct stat sb;
52340947Sbostic 
52440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
52540947Sbostic 
52640947Sbostic 	if (stat(filename, &sb)) {
52740947Sbostic 		(void)fprintf(stderr, "find: %s: %s.\n",
52840947Sbostic 		    filename, strerror(errno));
52940947Sbostic 		exit(1);
53040947Sbostic 	}
53140947Sbostic 	NEW(T_NEWER, f_newer);
53240947Sbostic 	new->t_data = sb.st_mtime;
53340947Sbostic 	return(new);
53440947Sbostic }
53540947Sbostic 
53640947Sbostic /*
53740947Sbostic  * -nogroup functions --
53840947Sbostic  *
53940947Sbostic  *	True if file belongs to a user ID for which the equivalent
54040947Sbostic  *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
54140947Sbostic  */
54240947Sbostic /* ARGSUSED */
54340947Sbostic f_nogroup(plan, entry)
54440947Sbostic 	PLAN *plan;
54540947Sbostic 	FTSENT *entry;
54640947Sbostic {
547*42255Sbostic 	return(group_from_gid(entry->fts_statb.st_gid, 1));
54840947Sbostic }
54940947Sbostic 
55040947Sbostic PLAN *
55140947Sbostic c_nogroup()
55240947Sbostic {
55340947Sbostic 	PLAN *new;
55440947Sbostic 
55540947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
55640947Sbostic 
55740947Sbostic 	NEW(T_NOGROUP, f_nogroup);
55840947Sbostic 	return(new);
55940947Sbostic }
56040947Sbostic 
56140947Sbostic /*
56240947Sbostic  * -nouser functions --
56340947Sbostic  *
56440947Sbostic  *	True if file belongs to a user ID for which the equivalent
56540947Sbostic  *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
56640947Sbostic  */
56740947Sbostic /* ARGSUSED */
56840947Sbostic f_nouser(plan, entry)
56940947Sbostic 	PLAN *plan;
57040947Sbostic 	FTSENT *entry;
57140947Sbostic {
572*42255Sbostic 	return(user_from_uid(entry->fts_statb.st_uid, 1));
57340947Sbostic }
57440947Sbostic 
57540947Sbostic PLAN *
57640947Sbostic c_nouser()
57740947Sbostic {
57840947Sbostic 	PLAN *new;
57940947Sbostic 
58040947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
58140947Sbostic 
58240947Sbostic 	NEW(T_NOUSER, f_nouser);
58340947Sbostic 	return(new);
58440947Sbostic }
58540947Sbostic 
58640947Sbostic /*
58740947Sbostic  * -perm functions --
58840947Sbostic  *
58940947Sbostic  *	The mode argument is used to represent file mode bits.  If it starts
59040947Sbostic  *	with a leading digit, it's treated as an octal mode, otherwise as a
59140947Sbostic  *	symbolic mode.
59240947Sbostic  */
59340947Sbostic f_perm(plan, entry)
59440947Sbostic 	PLAN *plan;
59540947Sbostic 	FTSENT *entry;
59640947Sbostic {
59740947Sbostic 	mode_t mode;
59840947Sbostic 
599*42255Sbostic 	mode = entry->fts_statb.st_mode &
60040947Sbostic 	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
60140947Sbostic 	if (plan->flags)
60240947Sbostic 		return((plan->m_data | mode) == mode);
60340947Sbostic 	else
60440947Sbostic 		return(mode == plan->m_data);
60540947Sbostic 	/* NOTREACHED */
60640947Sbostic }
60740947Sbostic 
60840947Sbostic PLAN *
60940947Sbostic c_perm(perm)
61040947Sbostic 	char *perm;
61140947Sbostic {
61240947Sbostic 	PLAN *new;
61340947Sbostic 
61440947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
61540947Sbostic 
61640947Sbostic 	NEW(T_PERM, f_perm);
61740947Sbostic 
61840947Sbostic 	if (*perm == '-') {
61940947Sbostic 		new->flags = 1;
62040947Sbostic 		++perm;
62140947Sbostic 	}
62240947Sbostic 
62340947Sbostic 	if (setmode(perm))
62440947Sbostic 		bad_arg("-perm", "illegal mode string");
62540947Sbostic 
62640947Sbostic 	new->m_data = getmode(0);
62740947Sbostic 	return(new);
62840947Sbostic }
62940947Sbostic 
63040947Sbostic /*
63140947Sbostic  * -print functions --
63240947Sbostic  *
63340947Sbostic  *	Always true, causes the current pathame to be written to
63440947Sbostic  *	standard output.
63540947Sbostic  */
63640947Sbostic /* ARGSUSED */
63740947Sbostic f_print(plan, entry)
63840947Sbostic 	PLAN *plan;
63940947Sbostic 	FTSENT *entry;
64040947Sbostic {
641*42255Sbostic 	(void)printf("%s\n", entry->fts_path);
642*42255Sbostic 	return(1);
64340947Sbostic }
64440947Sbostic 
64540947Sbostic PLAN *
64640947Sbostic c_print()
64740947Sbostic {
64840947Sbostic 	PLAN *new;
64940947Sbostic 
65040947Sbostic 	output_specified = 1;
65140947Sbostic 
65240947Sbostic 	NEW(T_PRINT, f_print);
65340947Sbostic 	return(new);
65440947Sbostic }
65540947Sbostic 
65640947Sbostic /*
65740947Sbostic  * -prune functions --
65840947Sbostic  *
65940947Sbostic  *	Prune a portion of the hierarchy.
66040947Sbostic  */
66140947Sbostic /* ARGSUSED */
66240947Sbostic f_prune(plan, entry)
66340947Sbostic 	PLAN *plan;
66440947Sbostic 	FTSENT *entry;
66540947Sbostic {
66640947Sbostic 	extern FTS *tree;
66740947Sbostic 
66840947Sbostic 	if (ftsset(tree, entry, FTS_SKIP)) {
66940947Sbostic 		(void)fprintf(stderr,
670*42255Sbostic 		    "find: %s: %s.\n", entry->fts_path, strerror(errno));
67140947Sbostic 		exit(1);
67240947Sbostic 	}
673*42255Sbostic 	return(1);
67440947Sbostic }
67540947Sbostic 
67640947Sbostic PLAN *
67740947Sbostic c_prune()
67840947Sbostic {
67940947Sbostic 	PLAN *new;
68040947Sbostic 
68140947Sbostic 	NEW(T_PRUNE, f_prune);
68240947Sbostic 	return(new);
68340947Sbostic }
68440947Sbostic 
68540947Sbostic /*
68640947Sbostic  * -size n[c] functions --
68740947Sbostic  *
68840947Sbostic  *	True if the file size in bytes, divided by an implementation defined
68940947Sbostic  *	value and rounded up to the next integer, is n.  If n is followed by
69040947Sbostic  *	a c, the size is in bytes.
69140947Sbostic  */
69240947Sbostic #define	FIND_SIZE	512
69340947Sbostic static int divsize = 1;
69440947Sbostic 
69540947Sbostic f_size(plan, entry)
69640947Sbostic 	PLAN *plan;
69740947Sbostic 	FTSENT *entry;
69840947Sbostic {
69940947Sbostic 	off_t size;
70040947Sbostic 
701*42255Sbostic 	size = divsize ? (entry->fts_statb.st_size + FIND_SIZE - 1) /
702*42255Sbostic 	    FIND_SIZE : entry->fts_statb.st_size;
70340947Sbostic 	COMPARE(size, plan->o_data);
70440947Sbostic }
70540947Sbostic 
70640947Sbostic PLAN *
70740947Sbostic c_size(arg)
70840947Sbostic 	char *arg;
70940947Sbostic {
71040947Sbostic 	PLAN *new;
71140947Sbostic 	char endch;
71240947Sbostic 
71340947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
71440947Sbostic 
71540947Sbostic 	NEW(T_SIZE, f_size);
71640947Sbostic 	new->o_data = find_parsenum(new, "-size", arg, &endch);
71740947Sbostic 	if (endch == 'c')
71840947Sbostic 		divsize = 0;
71940947Sbostic 	return(new);
72040947Sbostic }
72140947Sbostic 
72240947Sbostic /*
72340947Sbostic  * -type c functions --
72440947Sbostic  *
72540947Sbostic  *	True if the type of the file is c, where c is b, c, d, p, or f for
72640947Sbostic  *	block special file, character special file, directory, FIFO, or
72740947Sbostic  *	regular file, respectively.
72840947Sbostic  */
72940947Sbostic f_type(plan, entry)
73040947Sbostic 	PLAN *plan;
73140947Sbostic 	FTSENT *entry;
73240947Sbostic {
733*42255Sbostic 	return(entry->fts_statb.st_mode & plan->m_data);
73440947Sbostic }
73540947Sbostic 
73640947Sbostic PLAN *
73740947Sbostic c_type(typestring)
73840947Sbostic 	char *typestring;
73940947Sbostic {
74040947Sbostic 	PLAN *new;
74140947Sbostic 	mode_t  mask;
74240947Sbostic 
74340947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
74440947Sbostic 
74540947Sbostic 	switch (typestring[0]) {
74640947Sbostic 	case 'b':
74740947Sbostic 		mask = S_IFBLK;
74840947Sbostic 		break;
74940947Sbostic 	case 'c':
75040947Sbostic 		mask = S_IFCHR;
75140947Sbostic 		break;
75240947Sbostic 	case 'd':
75340947Sbostic 		mask = S_IFDIR;
75440947Sbostic 		break;
75540947Sbostic 	case 'f':
75640947Sbostic 		mask = S_IFREG;
75740947Sbostic 		break;
75840947Sbostic 	case 'l':
75940947Sbostic 		mask = S_IFLNK;
76040947Sbostic 		break;
76140947Sbostic 	case 'p':
76240947Sbostic 		mask = S_IFIFO;
76340947Sbostic 		break;
76440947Sbostic 	case 's':
76540947Sbostic 		mask = S_IFSOCK;
76640947Sbostic 		break;
76740947Sbostic 	default:
76840947Sbostic 		bad_arg("-type", "unknown type");
76940947Sbostic 	}
77040947Sbostic 
77140947Sbostic 	NEW(T_TYPE, f_type);
77240947Sbostic 	new->m_data = mask;
77340947Sbostic 	return(new);
77440947Sbostic }
77540947Sbostic 
77640947Sbostic /*
77740947Sbostic  * -user uname functions --
77840947Sbostic  *
77940947Sbostic  *	True if the file belongs to the user uname.  If uname is numeric and
78040947Sbostic  *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
78140947Sbostic  *	return a valid user name, uname is taken as a user ID.
78240947Sbostic  */
78340947Sbostic f_user(plan, entry)
78440947Sbostic 	PLAN *plan;
78540947Sbostic 	FTSENT *entry;
78640947Sbostic {
787*42255Sbostic 	return(entry->fts_statb.st_uid == plan->u_data);
78840947Sbostic }
78940947Sbostic 
79040947Sbostic PLAN *
79140947Sbostic c_user(username)
79240947Sbostic 	char *username;
79340947Sbostic {
79440947Sbostic 	PLAN *new;
79540947Sbostic 	struct passwd *p;
79640947Sbostic 	uid_t uid;
79740947Sbostic 
79840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
79940947Sbostic 
80040947Sbostic 	p = getpwnam(username);
80140947Sbostic 	if (p == NULL) {
80240947Sbostic 		uid = atoi(username);
80340947Sbostic 		if (uid == 0 && username[0] != '0')
80440947Sbostic 			bad_arg("-user", "no such user");
80540947Sbostic 	} else
80640947Sbostic 		uid = p->pw_uid;
80740947Sbostic 
80840947Sbostic 	NEW(T_USER, f_user);
80940947Sbostic 	new->u_data = uid;
81040947Sbostic 	return(new);
81140947Sbostic }
81240947Sbostic 
81340947Sbostic /*
81440947Sbostic  * -xdev functions --
81540947Sbostic  *
81640947Sbostic  *	Always true, causes find not to decend past directories that have a
81740947Sbostic  *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
81840947Sbostic  *
81940947Sbostic  *	Note: this checking is done in find_execute().
82040947Sbostic  */
82140947Sbostic PLAN *
82240947Sbostic c_xdev()
82340947Sbostic {
82440947Sbostic 	extern int xdev;
82540947Sbostic 	PLAN *new;
82640947Sbostic 
82740947Sbostic 	xdev = 1;
82840947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
82940947Sbostic 
83040947Sbostic 	NEW(T_XDEV, f_always_true);
83140947Sbostic 	return(new);
83240947Sbostic }
83340947Sbostic 
83440947Sbostic /*
83540947Sbostic  * ( expression ) functions --
83640947Sbostic  *
83740947Sbostic  *	True if expression is true.
83840947Sbostic  */
83940947Sbostic f_expr(plan, entry)
84040947Sbostic 	PLAN *plan;
84140947Sbostic 	FTSENT *entry;
84240947Sbostic {
84340947Sbostic 	register PLAN *p;
84440947Sbostic 	register int state;
84540947Sbostic 
84640947Sbostic 	for (p = plan->p_data[0];
84740947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
84840947Sbostic 	return(state);
84940947Sbostic }
85040947Sbostic 
85140947Sbostic /*
85240947Sbostic  * T_OPENPAREN and T_CLOSEPAREN nodes are temporary place markers.  They are
85340947Sbostic  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
85440947Sbostic  * to a T_EXPR node containing the expression and the ')' node is discarded.
85540947Sbostic  */
85640947Sbostic PLAN *
85740947Sbostic c_openparen()
85840947Sbostic {
85940947Sbostic 	PLAN *new;
86040947Sbostic 
86140947Sbostic 	NEW(T_OPENPAREN, (int (*)())-1);
86240947Sbostic 	return(new);
86340947Sbostic }
86440947Sbostic 
86540947Sbostic PLAN *
86640947Sbostic c_closeparen()
86740947Sbostic {
86840947Sbostic 	PLAN *new;
86940947Sbostic 
87040947Sbostic 	NEW(T_CLOSEPAREN, (int (*)())-1);
87140947Sbostic 	return(new);
87240947Sbostic }
87340947Sbostic 
87440947Sbostic /*
87540947Sbostic  * -mtime n functions --
87640947Sbostic  *
87740947Sbostic  *	True if the difference between the file modification time and the
87840947Sbostic  *	current time is n 24 hour periods.
87940947Sbostic  */
88040947Sbostic f_mtime(plan, entry)
88140947Sbostic 	PLAN *plan;
88240947Sbostic 	FTSENT *entry;
88340947Sbostic {
88440947Sbostic 	extern time_t now;
88540947Sbostic 
886*42255Sbostic 	COMPARE((now - entry->fts_statb.st_mtime + SECSPERDAY - 1) /
887*42255Sbostic 	    SECSPERDAY, plan->t_data);
88840947Sbostic }
88940947Sbostic 
89040947Sbostic PLAN *
89140947Sbostic c_mtime(arg)
89240947Sbostic 	char *arg;
89340947Sbostic {
89440947Sbostic 	PLAN *new;
89540947Sbostic 
89640947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
89740947Sbostic 
89840947Sbostic 	NEW(T_MTIME, f_mtime);
89940947Sbostic 	new->t_data = find_parsenum(new, "-mtime", arg, (char *)NULL);
90040947Sbostic 	return(new);
90140947Sbostic }
90240947Sbostic 
90340947Sbostic /*
90440947Sbostic  * ! expression functions --
90540947Sbostic  *
90640947Sbostic  *	Negation of a primary; the unary NOT operator.
90740947Sbostic  */
90840947Sbostic f_not(plan, entry)
90940947Sbostic 	PLAN *plan;
91040947Sbostic 	FTSENT *entry;
91140947Sbostic {
91240947Sbostic 	register PLAN *p;
91340947Sbostic 	register int state;
91440947Sbostic 
91540947Sbostic 	for (p = plan->p_data[0];
91640947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
91740947Sbostic 	return(!state);
91840947Sbostic }
91940947Sbostic 
92040947Sbostic PLAN *
92140947Sbostic c_not()
92240947Sbostic {
92340947Sbostic 	PLAN *new;
92440947Sbostic 
92540947Sbostic 	NEW(T_NOT, f_not);
92640947Sbostic 	return(new);
92740947Sbostic }
92840947Sbostic 
92940947Sbostic /*
93040947Sbostic  * expression -o expression functions --
93140947Sbostic  *
93240947Sbostic  *	Alternation of primaries; the OR operator.  The second expression is
93340947Sbostic  * not evaluated if the first expression is true.
93440947Sbostic  */
93540947Sbostic f_or(plan, entry)
93640947Sbostic 	PLAN *plan;
93740947Sbostic 	FTSENT *entry;
93840947Sbostic {
93940947Sbostic 	register PLAN *p;
94040947Sbostic 	register int state;
94140947Sbostic 
94240947Sbostic 	for (p = plan->p_data[0];
94340947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
94440947Sbostic 
94540947Sbostic 	if (state)
946*42255Sbostic 		return(1);
94740947Sbostic 
94840947Sbostic 	for (p = plan->p_data[1];
94940947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
95040947Sbostic 	return(state);
95140947Sbostic }
95240947Sbostic 
95340947Sbostic PLAN *
95440947Sbostic c_or()
95540947Sbostic {
95640947Sbostic 	PLAN *new;
95740947Sbostic 
95840947Sbostic 	NEW(T_OR, f_or);
95940947Sbostic 	return(new);
96040947Sbostic }
961