xref: /csrg-svn/usr.bin/find/function.c (revision 40947)
1*40947Sbostic /*-
2*40947Sbostic  * Copyright (c) 1990 The Regents of the University of California.
3*40947Sbostic  * All rights reserved.
4*40947Sbostic  *
5*40947Sbostic  * This code is derived from software contributed to Berkeley by
6*40947Sbostic  * Cimarron D. Taylor of the University of California, Berkeley.
7*40947Sbostic  *
8*40947Sbostic  * %sccs.include.redist.c%
9*40947Sbostic  */
10*40947Sbostic 
11*40947Sbostic #ifndef lint
12*40947Sbostic static char sccsid[] = "@(#)function.c	5.1 (Berkeley) 04/16/90";
13*40947Sbostic #endif /* not lint */
14*40947Sbostic 
15*40947Sbostic #include <sys/types.h>
16*40947Sbostic #include <sys/stat.h>
17*40947Sbostic #include <sys/wait.h>
18*40947Sbostic #include <sys/mount.h>
19*40947Sbostic #include <grp.h>
20*40947Sbostic #include <pwd.h>
21*40947Sbostic #include <fts.h>
22*40947Sbostic #include <unistd.h>
23*40947Sbostic #include <tzfile.h>
24*40947Sbostic #include <stdio.h>
25*40947Sbostic #include "find.h"
26*40947Sbostic 
27*40947Sbostic #define	FIND_EQUAL	0
28*40947Sbostic #define	FIND_LESSTHAN	1
29*40947Sbostic #define	FIND_GREATER	2
30*40947Sbostic 
31*40947Sbostic #define	FIND_FALSE	0
32*40947Sbostic #define	FIND_TRUE	1
33*40947Sbostic 
34*40947Sbostic #define	COMPARE(a, b) { \
35*40947Sbostic 	switch(plan->flags) { \
36*40947Sbostic 	case FIND_EQUAL: \
37*40947Sbostic 		return(a == b); \
38*40947Sbostic 	case FIND_LESSTHAN: \
39*40947Sbostic 		return(a < b); \
40*40947Sbostic 	case FIND_GREATER: \
41*40947Sbostic 		return(a > b); \
42*40947Sbostic 	} \
43*40947Sbostic 	return(FIND_FALSE); \
44*40947Sbostic }
45*40947Sbostic 
46*40947Sbostic #define NEW(t, f) { \
47*40947Sbostic 	new = (PLAN *)emalloc(sizeof(PLAN)); \
48*40947Sbostic 	new->type = t; \
49*40947Sbostic 	new->eval = f; \
50*40947Sbostic 	new->flags = 0; \
51*40947Sbostic 	new->next = NULL; \
52*40947Sbostic }
53*40947Sbostic 
54*40947Sbostic /*
55*40947Sbostic  * find_parsenum --
56*40947Sbostic  *	Parse a string of the form [+-]# and return the value.
57*40947Sbostic  */
58*40947Sbostic long
59*40947Sbostic find_parsenum(plan, option, str, endch)
60*40947Sbostic 	PLAN *plan;
61*40947Sbostic 	char *option, *str, *endch;
62*40947Sbostic {
63*40947Sbostic 	long value;
64*40947Sbostic 	char *endchar;		/* pointer to character ending conversion */
65*40947Sbostic 
66*40947Sbostic 	/* determine comparison from leading + or - */
67*40947Sbostic 	switch(*str) {
68*40947Sbostic 	case '+':
69*40947Sbostic 		++str;
70*40947Sbostic 		plan->flags = FIND_GREATER;
71*40947Sbostic 		break;
72*40947Sbostic 	case '-':
73*40947Sbostic 		++str;
74*40947Sbostic 		plan->flags = FIND_LESSTHAN;
75*40947Sbostic 		break;
76*40947Sbostic 	default:
77*40947Sbostic 		plan->flags = FIND_EQUAL;
78*40947Sbostic 		break;
79*40947Sbostic 	}
80*40947Sbostic 
81*40947Sbostic 	/*
82*40947Sbostic 	 * convert the string with strtol().  Note, if strtol() returns zero
83*40947Sbostic 	 * and endchar points to the beginning of the string we know we have
84*40947Sbostic 	 * a syntax error.
85*40947Sbostic 	 */
86*40947Sbostic 	value = strtol(str, &endchar, 10);
87*40947Sbostic 	if (!value && endchar == str ||
88*40947Sbostic 	    endchar[0] && (!endch || endchar[0] != *endch))
89*40947Sbostic 		bad_arg(option, "illegal numeric value");
90*40947Sbostic 	if (endch)
91*40947Sbostic 		*endch = endchar[0];
92*40947Sbostic 	return(value);
93*40947Sbostic }
94*40947Sbostic 
95*40947Sbostic /*
96*40947Sbostic  * -atime n functions --
97*40947Sbostic  *
98*40947Sbostic  *	True if the difference between the file access time and the
99*40947Sbostic  *	current time is n 24 hour periods.
100*40947Sbostic  *
101*40947Sbostic  */
102*40947Sbostic f_atime(plan, entry)
103*40947Sbostic 	PLAN *plan;
104*40947Sbostic 	FTSENT *entry;
105*40947Sbostic {
106*40947Sbostic 	extern time_t now;
107*40947Sbostic 
108*40947Sbostic 	COMPARE((now - entry->statb.st_atime + SECSPERDAY - 1) / SECSPERDAY,
109*40947Sbostic 	    plan->t_data);
110*40947Sbostic }
111*40947Sbostic 
112*40947Sbostic PLAN *
113*40947Sbostic c_atime(arg)
114*40947Sbostic 	char *arg;
115*40947Sbostic {
116*40947Sbostic 	PLAN *new;
117*40947Sbostic 
118*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
119*40947Sbostic 
120*40947Sbostic 	NEW(T_ATIME, f_atime);
121*40947Sbostic 	new->t_data = find_parsenum(new, "-atime", arg, (char *)NULL);
122*40947Sbostic 	return(new);
123*40947Sbostic }
124*40947Sbostic /*
125*40947Sbostic  * -ctime n functions --
126*40947Sbostic  *
127*40947Sbostic  *	True if the difference between the last change of file
128*40947Sbostic  *	status information and the current time is n 24 hour periods.
129*40947Sbostic  */
130*40947Sbostic f_ctime(plan, entry)
131*40947Sbostic 	PLAN *plan;
132*40947Sbostic 	FTSENT *entry;
133*40947Sbostic {
134*40947Sbostic 	extern time_t now;
135*40947Sbostic 
136*40947Sbostic 	COMPARE((now - entry->statb.st_ctime + SECSPERDAY - 1) / SECSPERDAY,
137*40947Sbostic 	    plan->t_data);
138*40947Sbostic }
139*40947Sbostic 
140*40947Sbostic PLAN *
141*40947Sbostic c_ctime(arg)
142*40947Sbostic 	char *arg;
143*40947Sbostic {
144*40947Sbostic 	PLAN *new;
145*40947Sbostic 
146*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
147*40947Sbostic 
148*40947Sbostic 	NEW(T_CTIME, f_ctime);
149*40947Sbostic 	new->t_data = find_parsenum(new, "-ctime", arg, (char *)NULL);
150*40947Sbostic 	return(new);
151*40947Sbostic }
152*40947Sbostic 
153*40947Sbostic /*
154*40947Sbostic  * -depth functions --
155*40947Sbostic  *
156*40947Sbostic  *	Always true, causes descent of the directory hierarchy to be done
157*40947Sbostic  *	so that all entries in a directory are acted on before the directory
158*40947Sbostic  *	itself.
159*40947Sbostic  */
160*40947Sbostic /* ARGSUSED */
161*40947Sbostic f_always_true(plan, entry)
162*40947Sbostic 	PLAN *plan;
163*40947Sbostic 	FTSENT *entry;
164*40947Sbostic {
165*40947Sbostic 	return(FIND_TRUE);
166*40947Sbostic }
167*40947Sbostic 
168*40947Sbostic PLAN *
169*40947Sbostic c_depth()
170*40947Sbostic {
171*40947Sbostic 	extern int depth;
172*40947Sbostic 	PLAN *new;
173*40947Sbostic 
174*40947Sbostic 	depth = 1;
175*40947Sbostic 
176*40947Sbostic 	NEW(T_DEPTH, f_always_true);
177*40947Sbostic 	return(new);
178*40947Sbostic }
179*40947Sbostic 
180*40947Sbostic /*
181*40947Sbostic  * [-exec | -ok] utility [arg ... ] ; functions --
182*40947Sbostic  *
183*40947Sbostic  *	True if the executed utility returns a zero value as exit status.
184*40947Sbostic  *	The end of the primary expression is delimited by a semicolon.  If
185*40947Sbostic  *	"{}" occurs anywhere, it gets replaced by the current pathname.
186*40947Sbostic  *	The current directory for the execution of utility is the same as
187*40947Sbostic  *	the current directory when the find utility was started.
188*40947Sbostic  *
189*40947Sbostic  *	The primary -ok is different in that it requests affirmation of the
190*40947Sbostic  *	user before executing the utility.
191*40947Sbostic  */
192*40947Sbostic f_exec(plan, entry)
193*40947Sbostic 	register PLAN *plan;
194*40947Sbostic 	FTSENT *entry;
195*40947Sbostic {
196*40947Sbostic 	register int cnt;
197*40947Sbostic 	char *find_subst();
198*40947Sbostic 	union wait pstat;
199*40947Sbostic 	pid_t pid, waitpid();
200*40947Sbostic 
201*40947Sbostic 	for (cnt = 0; plan->e_argv[cnt]; ++cnt)
202*40947Sbostic 		if (plan->e_len[cnt])
203*40947Sbostic 			find_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
204*40947Sbostic 			    entry->path, plan->e_len[cnt]);
205*40947Sbostic 
206*40947Sbostic 	if (plan->flags && !find_queryuser(plan->e_argv))
207*40947Sbostic 		return(FIND_FALSE);
208*40947Sbostic 
209*40947Sbostic 	switch(pid = vfork()) {
210*40947Sbostic 	case -1:
211*40947Sbostic 		(void)fprintf(stderr, "find: fork: %s.\n", strerror(errno));
212*40947Sbostic 		exit(1);
213*40947Sbostic 		/* NOTREACHED */
214*40947Sbostic 	case 0:
215*40947Sbostic 		execvp(plan->e_argv[0], plan->e_argv);
216*40947Sbostic 		(void)fprintf(stderr,
217*40947Sbostic 		    "find: %s: %s.\n", plan->e_argv[0], strerror(errno));
218*40947Sbostic 		exit(1);
219*40947Sbostic 		/* NOTREACHED */
220*40947Sbostic 	}
221*40947Sbostic 	pid = waitpid(pid, &pstat, 0);
222*40947Sbostic 	return(pid == -1 || pstat.w_status ? FIND_FALSE : FIND_TRUE);
223*40947Sbostic }
224*40947Sbostic 
225*40947Sbostic /*
226*40947Sbostic  * c_exec --
227*40947Sbostic  *	build three parallel arrays, one with pointers to the strings passed
228*40947Sbostic  *	on the command line, one with (possibly duplicated) pointers to the
229*40947Sbostic  *	argv array, and one with integer values that are lengths of the
230*40947Sbostic  *	strings, but also flags meaning that the string has to be massaged.
231*40947Sbostic  */
232*40947Sbostic PLAN *
233*40947Sbostic c_exec(argvp, isok)
234*40947Sbostic 	char ***argvp;
235*40947Sbostic 	int isok;
236*40947Sbostic {
237*40947Sbostic 	PLAN *new;			/* node returned */
238*40947Sbostic 	register int cnt;
239*40947Sbostic 	register char **argv, **ap, *p;
240*40947Sbostic 
241*40947Sbostic 	ftsoptions |= FTS_NOCHDIR;
242*40947Sbostic 	output_specified = 1;
243*40947Sbostic 
244*40947Sbostic 	NEW(T_EXEC, f_exec);
245*40947Sbostic 	new->flags = isok;
246*40947Sbostic 
247*40947Sbostic 	for (ap = argv = *argvp;; ++ap) {
248*40947Sbostic 		if (!*ap)
249*40947Sbostic 			bad_arg(isok ? "-ok" : "-exec", "no terminating \";\"");
250*40947Sbostic 		if (**ap == ';')
251*40947Sbostic 			break;
252*40947Sbostic 	}
253*40947Sbostic 
254*40947Sbostic 	cnt = ap - *argvp + 1;
255*40947Sbostic 	new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
256*40947Sbostic 	new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
257*40947Sbostic 	new->e_len = (int *)emalloc((u_int)cnt * sizeof(u_char));
258*40947Sbostic 
259*40947Sbostic 	for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
260*40947Sbostic 		new->e_orig[cnt] = *argv;
261*40947Sbostic 		for (p = *argv; *p; ++p)
262*40947Sbostic 			if (p[0] == '{' && p[1] == '}') {
263*40947Sbostic 				new->e_argv[cnt] = emalloc((u_int)1024);
264*40947Sbostic 				new->e_len[cnt] = 1024;
265*40947Sbostic 				break;
266*40947Sbostic 			}
267*40947Sbostic 		if (!*p) {
268*40947Sbostic 			new->e_argv[cnt] = *argv;
269*40947Sbostic 			new->e_len[cnt] = 0;
270*40947Sbostic 		}
271*40947Sbostic 	}
272*40947Sbostic 	new->e_argv[cnt] = new->e_orig[cnt] = NULL;
273*40947Sbostic 
274*40947Sbostic 	*argvp = argv + 1;
275*40947Sbostic 	return(new);
276*40947Sbostic }
277*40947Sbostic 
278*40947Sbostic /*
279*40947Sbostic  * -follow functions --
280*40947Sbostic  *
281*40947Sbostic  *	Always true, causes symbolic links to be followed on a global
282*40947Sbostic  *	basis.
283*40947Sbostic  */
284*40947Sbostic PLAN *
285*40947Sbostic c_follow()
286*40947Sbostic {
287*40947Sbostic 	PLAN *new;
288*40947Sbostic 
289*40947Sbostic 	ftsoptions &= ~FTS_PHYSICAL;
290*40947Sbostic 	ftsoptions |= FTS_LOGICAL;
291*40947Sbostic 
292*40947Sbostic 	NEW(T_FOLLOW, f_always_true);
293*40947Sbostic 	return(new);
294*40947Sbostic }
295*40947Sbostic 
296*40947Sbostic /*
297*40947Sbostic  * -fstype functions --
298*40947Sbostic  *
299*40947Sbostic  *	True if the file is of a certain type.
300*40947Sbostic  */
301*40947Sbostic f_fstype(plan, entry)
302*40947Sbostic 	PLAN *plan;
303*40947Sbostic 	FTSENT *entry;
304*40947Sbostic {
305*40947Sbostic 	extern dev_t curdev;
306*40947Sbostic 	struct statfs sb;
307*40947Sbostic 	static short curtype;
308*40947Sbostic 
309*40947Sbostic 	/* only check when we cross mount point */
310*40947Sbostic 	if (curdev != entry->statb.st_dev) {
311*40947Sbostic 		if (statfs(entry->name, &sb)) {
312*40947Sbostic 			(void)fprintf(stderr, "find: %s: %s.\n",
313*40947Sbostic 			    entry->name, strerror(errno));
314*40947Sbostic 			exit(1);
315*40947Sbostic 		}
316*40947Sbostic 		curtype = sb.f_type;
317*40947Sbostic 	}
318*40947Sbostic 	return(plan->flags == curtype);
319*40947Sbostic }
320*40947Sbostic 
321*40947Sbostic PLAN *
322*40947Sbostic c_fstype(arg)
323*40947Sbostic 	char *arg;
324*40947Sbostic {
325*40947Sbostic 	register PLAN *new;
326*40947Sbostic 
327*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
328*40947Sbostic 
329*40947Sbostic 	NEW(T_FSTYPE, f_fstype);
330*40947Sbostic 	switch(*arg) {
331*40947Sbostic 	case 'm':
332*40947Sbostic 		if (!strcmp(arg, "mfs")) {
333*40947Sbostic 			new->flags = MOUNT_MFS;
334*40947Sbostic 			return(new);
335*40947Sbostic 		}
336*40947Sbostic 		break;
337*40947Sbostic 	case 'n':
338*40947Sbostic 		if (!strcmp(arg, "nfs")) {
339*40947Sbostic 			new->flags = MOUNT_NFS;
340*40947Sbostic 			return(new);
341*40947Sbostic 		}
342*40947Sbostic 		break;
343*40947Sbostic 	case 'p':
344*40947Sbostic 		if (!strcmp(arg, "pc")) {
345*40947Sbostic 			new->flags = MOUNT_PC;
346*40947Sbostic 			return(new);
347*40947Sbostic 		}
348*40947Sbostic 		break;
349*40947Sbostic 	case 'u':
350*40947Sbostic 		if (!strcmp(arg, "ufs")) {
351*40947Sbostic 			new->flags = MOUNT_UFS;
352*40947Sbostic 			return(new);
353*40947Sbostic 		}
354*40947Sbostic 		break;
355*40947Sbostic 	}
356*40947Sbostic 	(void)fprintf(stderr, "find: unknown file type %s.\n", arg);
357*40947Sbostic 	exit(1);
358*40947Sbostic 	/* NOTREACHED */
359*40947Sbostic }
360*40947Sbostic 
361*40947Sbostic /*
362*40947Sbostic  * -group gname functions --
363*40947Sbostic  *
364*40947Sbostic  *	True if the file belongs to the group gname.  If gname is numeric and
365*40947Sbostic  *	an equivalent of the getgrnam() function does not return a valid group
366*40947Sbostic  *	name, gname is taken as a group ID.
367*40947Sbostic  */
368*40947Sbostic f_group(plan, entry)
369*40947Sbostic 	PLAN *plan;
370*40947Sbostic 	FTSENT *entry;
371*40947Sbostic {
372*40947Sbostic 	return(entry->statb.st_gid == plan->g_data ? FIND_TRUE : FIND_FALSE);
373*40947Sbostic }
374*40947Sbostic 
375*40947Sbostic PLAN *
376*40947Sbostic c_group(gname)
377*40947Sbostic 	char *gname;
378*40947Sbostic {
379*40947Sbostic 	PLAN *new;
380*40947Sbostic 	struct group *g;
381*40947Sbostic 	gid_t gid;
382*40947Sbostic 
383*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
384*40947Sbostic 
385*40947Sbostic 	g = getgrnam(gname);
386*40947Sbostic 	if (g == NULL) {
387*40947Sbostic 		gid = atoi(gname);
388*40947Sbostic 		if (gid == 0 && gname[0] != '0')
389*40947Sbostic 			bad_arg("-group", "no such group");
390*40947Sbostic 	} else
391*40947Sbostic 		gid = g->gr_gid;
392*40947Sbostic 
393*40947Sbostic 	NEW(T_GROUP, f_group);
394*40947Sbostic 	new->g_data = gid;
395*40947Sbostic 	return(new);
396*40947Sbostic }
397*40947Sbostic 
398*40947Sbostic /*
399*40947Sbostic  * -inum n functions --
400*40947Sbostic  *
401*40947Sbostic  *	True if the file has inode # n.
402*40947Sbostic  */
403*40947Sbostic f_inum(plan, entry)
404*40947Sbostic 	PLAN *plan;
405*40947Sbostic 	FTSENT *entry;
406*40947Sbostic {
407*40947Sbostic 	COMPARE(entry->statb.st_ino, plan->i_data);
408*40947Sbostic }
409*40947Sbostic 
410*40947Sbostic PLAN *
411*40947Sbostic c_inum(arg)
412*40947Sbostic 	char *arg;
413*40947Sbostic {
414*40947Sbostic 	PLAN *new;
415*40947Sbostic 
416*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
417*40947Sbostic 
418*40947Sbostic 	NEW(T_INUM, f_inum);
419*40947Sbostic 	new->i_data = find_parsenum(new, "-inum", arg, (char *)NULL);
420*40947Sbostic 	return(new);
421*40947Sbostic }
422*40947Sbostic 
423*40947Sbostic /*
424*40947Sbostic  * -links n functions --
425*40947Sbostic  *
426*40947Sbostic  *	True if the file has n links.
427*40947Sbostic  */
428*40947Sbostic f_links(plan, entry)
429*40947Sbostic 	PLAN *plan;
430*40947Sbostic 	FTSENT *entry;
431*40947Sbostic {
432*40947Sbostic 	COMPARE(entry->statb.st_nlink, plan->l_data);
433*40947Sbostic }
434*40947Sbostic 
435*40947Sbostic PLAN *
436*40947Sbostic c_links(arg)
437*40947Sbostic 	char *arg;
438*40947Sbostic {
439*40947Sbostic 	PLAN *new;
440*40947Sbostic 
441*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
442*40947Sbostic 
443*40947Sbostic 	NEW(T_LINKS, f_links);
444*40947Sbostic 	new->l_data = find_parsenum(new, "-links", arg, (char *)NULL);
445*40947Sbostic 	return(new);
446*40947Sbostic }
447*40947Sbostic 
448*40947Sbostic /*
449*40947Sbostic  * -ls functions --
450*40947Sbostic  *
451*40947Sbostic  *	Always true - prints the current entry to stdout in "ls" format.
452*40947Sbostic  */
453*40947Sbostic /* ARGSUSED */
454*40947Sbostic f_ls(plan, entry)
455*40947Sbostic 	PLAN *plan;
456*40947Sbostic 	FTSENT *entry;
457*40947Sbostic {
458*40947Sbostic 	printlong(entry->path, entry->accpath, &entry->statb);
459*40947Sbostic 	return(FIND_TRUE);
460*40947Sbostic }
461*40947Sbostic 
462*40947Sbostic PLAN *
463*40947Sbostic c_ls()
464*40947Sbostic {
465*40947Sbostic 	PLAN *new;
466*40947Sbostic 
467*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
468*40947Sbostic 	output_specified = 1;
469*40947Sbostic 
470*40947Sbostic 	NEW(T_LS, f_ls);
471*40947Sbostic 	return(new);
472*40947Sbostic }
473*40947Sbostic 
474*40947Sbostic /*
475*40947Sbostic  * -name functions --
476*40947Sbostic  *
477*40947Sbostic  *	True if the basename of the filename being examined
478*40947Sbostic  *	matches pattern using Pattern Matching Notation S3.14
479*40947Sbostic  */
480*40947Sbostic f_name(plan, entry)
481*40947Sbostic 	PLAN *plan;
482*40947Sbostic 	FTSENT *entry;
483*40947Sbostic {
484*40947Sbostic 	return(fnmatch(plan->c_data, entry->name, FNM_QUOTE) ?
485*40947Sbostic 	    FIND_TRUE : FIND_FALSE);
486*40947Sbostic }
487*40947Sbostic 
488*40947Sbostic PLAN *
489*40947Sbostic c_name(pattern)
490*40947Sbostic 	char *pattern;
491*40947Sbostic {
492*40947Sbostic 	PLAN *new;
493*40947Sbostic 
494*40947Sbostic 	NEW(T_NAME, f_name);
495*40947Sbostic 	new->c_data = pattern;
496*40947Sbostic 	return(new);
497*40947Sbostic }
498*40947Sbostic 
499*40947Sbostic /*
500*40947Sbostic  * -newer file functions --
501*40947Sbostic  *
502*40947Sbostic  *	True if the current file has been modified more recently
503*40947Sbostic  *	then the modification time of the file named by the pathname
504*40947Sbostic  *	file.
505*40947Sbostic  */
506*40947Sbostic f_newer(plan, entry)
507*40947Sbostic 	PLAN *plan;
508*40947Sbostic 	FTSENT *entry;
509*40947Sbostic {
510*40947Sbostic 	return(entry->statb.st_mtime > plan->t_data ? FIND_TRUE : FIND_FALSE);
511*40947Sbostic }
512*40947Sbostic 
513*40947Sbostic PLAN *
514*40947Sbostic c_newer(filename)
515*40947Sbostic 	char *filename;
516*40947Sbostic {
517*40947Sbostic 	PLAN *new;
518*40947Sbostic 	struct stat sb;
519*40947Sbostic 
520*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
521*40947Sbostic 
522*40947Sbostic 	if (stat(filename, &sb)) {
523*40947Sbostic 		(void)fprintf(stderr, "find: %s: %s.\n",
524*40947Sbostic 		    filename, strerror(errno));
525*40947Sbostic 		exit(1);
526*40947Sbostic 	}
527*40947Sbostic 	NEW(T_NEWER, f_newer);
528*40947Sbostic 	new->t_data = sb.st_mtime;
529*40947Sbostic 	return(new);
530*40947Sbostic }
531*40947Sbostic 
532*40947Sbostic /*
533*40947Sbostic  * -nogroup functions --
534*40947Sbostic  *
535*40947Sbostic  *	True if file belongs to a user ID for which the equivalent
536*40947Sbostic  *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
537*40947Sbostic  */
538*40947Sbostic /* ARGSUSED */
539*40947Sbostic f_nogroup(plan, entry)
540*40947Sbostic 	PLAN *plan;
541*40947Sbostic 	FTSENT *entry;
542*40947Sbostic {
543*40947Sbostic 	return(group_from_gid(entry->statb.st_gid, 1) ? FIND_FALSE : FIND_TRUE);
544*40947Sbostic }
545*40947Sbostic 
546*40947Sbostic PLAN *
547*40947Sbostic c_nogroup()
548*40947Sbostic {
549*40947Sbostic 	PLAN *new;
550*40947Sbostic 
551*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
552*40947Sbostic 
553*40947Sbostic 	NEW(T_NOGROUP, f_nogroup);
554*40947Sbostic 	return(new);
555*40947Sbostic }
556*40947Sbostic 
557*40947Sbostic /*
558*40947Sbostic  * -nouser functions --
559*40947Sbostic  *
560*40947Sbostic  *	True if file belongs to a user ID for which the equivalent
561*40947Sbostic  *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
562*40947Sbostic  */
563*40947Sbostic /* ARGSUSED */
564*40947Sbostic f_nouser(plan, entry)
565*40947Sbostic 	PLAN *plan;
566*40947Sbostic 	FTSENT *entry;
567*40947Sbostic {
568*40947Sbostic 	return(user_from_uid(entry->statb.st_uid, 1) ? FIND_FALSE : FIND_TRUE);
569*40947Sbostic }
570*40947Sbostic 
571*40947Sbostic PLAN *
572*40947Sbostic c_nouser()
573*40947Sbostic {
574*40947Sbostic 	PLAN *new;
575*40947Sbostic 
576*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
577*40947Sbostic 
578*40947Sbostic 	NEW(T_NOUSER, f_nouser);
579*40947Sbostic 	return(new);
580*40947Sbostic }
581*40947Sbostic 
582*40947Sbostic /*
583*40947Sbostic  * -perm functions --
584*40947Sbostic  *
585*40947Sbostic  *	The mode argument is used to represent file mode bits.  If it starts
586*40947Sbostic  *	with a leading digit, it's treated as an octal mode, otherwise as a
587*40947Sbostic  *	symbolic mode.
588*40947Sbostic  */
589*40947Sbostic f_perm(plan, entry)
590*40947Sbostic 	PLAN *plan;
591*40947Sbostic 	FTSENT *entry;
592*40947Sbostic {
593*40947Sbostic 	mode_t mode;
594*40947Sbostic 
595*40947Sbostic 	mode = entry->statb.st_mode &
596*40947Sbostic 	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
597*40947Sbostic 	if (plan->flags)
598*40947Sbostic 		return((plan->m_data | mode) == mode);
599*40947Sbostic 	else
600*40947Sbostic 		return(mode == plan->m_data);
601*40947Sbostic 	/* NOTREACHED */
602*40947Sbostic }
603*40947Sbostic 
604*40947Sbostic PLAN *
605*40947Sbostic c_perm(perm)
606*40947Sbostic 	char *perm;
607*40947Sbostic {
608*40947Sbostic 	PLAN *new;
609*40947Sbostic 
610*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
611*40947Sbostic 
612*40947Sbostic 	NEW(T_PERM, f_perm);
613*40947Sbostic 
614*40947Sbostic 	if (*perm == '-') {
615*40947Sbostic 		new->flags = 1;
616*40947Sbostic 		++perm;
617*40947Sbostic 	}
618*40947Sbostic 
619*40947Sbostic 	if (setmode(perm))
620*40947Sbostic 		bad_arg("-perm", "illegal mode string");
621*40947Sbostic 
622*40947Sbostic 	new->m_data = getmode(0);
623*40947Sbostic 	return(new);
624*40947Sbostic }
625*40947Sbostic 
626*40947Sbostic /*
627*40947Sbostic  * -print functions --
628*40947Sbostic  *
629*40947Sbostic  *	Always true, causes the current pathame to be written to
630*40947Sbostic  *	standard output.
631*40947Sbostic  */
632*40947Sbostic /* ARGSUSED */
633*40947Sbostic f_print(plan, entry)
634*40947Sbostic 	PLAN *plan;
635*40947Sbostic 	FTSENT *entry;
636*40947Sbostic {
637*40947Sbostic 	(void)printf("%s\n", entry->path);
638*40947Sbostic 	return(FIND_TRUE);
639*40947Sbostic }
640*40947Sbostic 
641*40947Sbostic PLAN *
642*40947Sbostic c_print()
643*40947Sbostic {
644*40947Sbostic 	PLAN *new;
645*40947Sbostic 
646*40947Sbostic 	output_specified = 1;
647*40947Sbostic 
648*40947Sbostic 	NEW(T_PRINT, f_print);
649*40947Sbostic 	return(new);
650*40947Sbostic }
651*40947Sbostic 
652*40947Sbostic /*
653*40947Sbostic  * -prune functions --
654*40947Sbostic  *
655*40947Sbostic  *	Prune a portion of the hierarchy.
656*40947Sbostic  */
657*40947Sbostic /* ARGSUSED */
658*40947Sbostic f_prune(plan, entry)
659*40947Sbostic 	PLAN *plan;
660*40947Sbostic 	FTSENT *entry;
661*40947Sbostic {
662*40947Sbostic 	extern FTS *tree;
663*40947Sbostic 
664*40947Sbostic 	if (ftsset(tree, entry, FTS_SKIP)) {
665*40947Sbostic 		(void)fprintf(stderr,
666*40947Sbostic 		    "find: %s: %s.\n", entry->path, strerror(errno));
667*40947Sbostic 		exit(1);
668*40947Sbostic 	}
669*40947Sbostic 	return(FIND_TRUE);
670*40947Sbostic }
671*40947Sbostic 
672*40947Sbostic PLAN *
673*40947Sbostic c_prune()
674*40947Sbostic {
675*40947Sbostic 	PLAN *new;
676*40947Sbostic 
677*40947Sbostic 	NEW(T_PRUNE, f_prune);
678*40947Sbostic 	return(new);
679*40947Sbostic }
680*40947Sbostic 
681*40947Sbostic /*
682*40947Sbostic  * -size n[c] functions --
683*40947Sbostic  *
684*40947Sbostic  *	True if the file size in bytes, divided by an implementation defined
685*40947Sbostic  *	value and rounded up to the next integer, is n.  If n is followed by
686*40947Sbostic  *	a c, the size is in bytes.
687*40947Sbostic  */
688*40947Sbostic #define	FIND_SIZE	512
689*40947Sbostic static int divsize = 1;
690*40947Sbostic 
691*40947Sbostic f_size(plan, entry)
692*40947Sbostic 	PLAN *plan;
693*40947Sbostic 	FTSENT *entry;
694*40947Sbostic {
695*40947Sbostic 	off_t size;
696*40947Sbostic 
697*40947Sbostic 	size = divsize ?
698*40947Sbostic 	    (entry->statb.st_size + FIND_SIZE - 1) / FIND_SIZE :
699*40947Sbostic 	    entry->statb.st_size;
700*40947Sbostic 	COMPARE(size, plan->o_data);
701*40947Sbostic }
702*40947Sbostic 
703*40947Sbostic PLAN *
704*40947Sbostic c_size(arg)
705*40947Sbostic 	char *arg;
706*40947Sbostic {
707*40947Sbostic 	PLAN *new;
708*40947Sbostic 	char endch;
709*40947Sbostic 
710*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
711*40947Sbostic 
712*40947Sbostic 	NEW(T_SIZE, f_size);
713*40947Sbostic 	new->o_data = find_parsenum(new, "-size", arg, &endch);
714*40947Sbostic 	if (endch == 'c')
715*40947Sbostic 		divsize = 0;
716*40947Sbostic 	return(new);
717*40947Sbostic }
718*40947Sbostic 
719*40947Sbostic /*
720*40947Sbostic  * -type c functions --
721*40947Sbostic  *
722*40947Sbostic  *	True if the type of the file is c, where c is b, c, d, p, or f for
723*40947Sbostic  *	block special file, character special file, directory, FIFO, or
724*40947Sbostic  *	regular file, respectively.
725*40947Sbostic  */
726*40947Sbostic f_type(plan, entry)
727*40947Sbostic 	PLAN *plan;
728*40947Sbostic 	FTSENT *entry;
729*40947Sbostic {
730*40947Sbostic 	return(entry->statb.st_mode & plan->m_data ? FIND_TRUE : FIND_FALSE);
731*40947Sbostic }
732*40947Sbostic 
733*40947Sbostic PLAN *
734*40947Sbostic c_type(typestring)
735*40947Sbostic 	char *typestring;
736*40947Sbostic {
737*40947Sbostic 	PLAN *new;
738*40947Sbostic 	mode_t  mask;
739*40947Sbostic 
740*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
741*40947Sbostic 
742*40947Sbostic 	switch (typestring[0]) {
743*40947Sbostic 	case 'b':
744*40947Sbostic 		mask = S_IFBLK;
745*40947Sbostic 		break;
746*40947Sbostic 	case 'c':
747*40947Sbostic 		mask = S_IFCHR;
748*40947Sbostic 		break;
749*40947Sbostic 	case 'd':
750*40947Sbostic 		mask = S_IFDIR;
751*40947Sbostic 		break;
752*40947Sbostic 	case 'f':
753*40947Sbostic 		mask = S_IFREG;
754*40947Sbostic 		break;
755*40947Sbostic 	case 'l':
756*40947Sbostic 		mask = S_IFLNK;
757*40947Sbostic 		break;
758*40947Sbostic 	case 'p':
759*40947Sbostic 		mask = S_IFIFO;
760*40947Sbostic 		break;
761*40947Sbostic 	case 's':
762*40947Sbostic 		mask = S_IFSOCK;
763*40947Sbostic 		break;
764*40947Sbostic 	default:
765*40947Sbostic 		bad_arg("-type", "unknown type");
766*40947Sbostic 	}
767*40947Sbostic 
768*40947Sbostic 	NEW(T_TYPE, f_type);
769*40947Sbostic 	new->m_data = mask;
770*40947Sbostic 	return(new);
771*40947Sbostic }
772*40947Sbostic 
773*40947Sbostic /*
774*40947Sbostic  * -user uname functions --
775*40947Sbostic  *
776*40947Sbostic  *	True if the file belongs to the user uname.  If uname is numeric and
777*40947Sbostic  *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
778*40947Sbostic  *	return a valid user name, uname is taken as a user ID.
779*40947Sbostic  */
780*40947Sbostic f_user(plan, entry)
781*40947Sbostic 	PLAN *plan;
782*40947Sbostic 	FTSENT *entry;
783*40947Sbostic {
784*40947Sbostic 	return(entry->statb.st_uid == plan->u_data ? FIND_TRUE : FIND_FALSE);
785*40947Sbostic }
786*40947Sbostic 
787*40947Sbostic PLAN *
788*40947Sbostic c_user(username)
789*40947Sbostic 	char *username;
790*40947Sbostic {
791*40947Sbostic 	PLAN *new;
792*40947Sbostic 	struct passwd *p;
793*40947Sbostic 	uid_t uid;
794*40947Sbostic 
795*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
796*40947Sbostic 
797*40947Sbostic 	p = getpwnam(username);
798*40947Sbostic 	if (p == NULL) {
799*40947Sbostic 		uid = atoi(username);
800*40947Sbostic 		if (uid == 0 && username[0] != '0')
801*40947Sbostic 			bad_arg("-user", "no such user");
802*40947Sbostic 	} else
803*40947Sbostic 		uid = p->pw_uid;
804*40947Sbostic 
805*40947Sbostic 	NEW(T_USER, f_user);
806*40947Sbostic 	new->u_data = uid;
807*40947Sbostic 	return(new);
808*40947Sbostic }
809*40947Sbostic 
810*40947Sbostic /*
811*40947Sbostic  * -xdev functions --
812*40947Sbostic  *
813*40947Sbostic  *	Always true, causes find not to decend past directories that have a
814*40947Sbostic  *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
815*40947Sbostic  *
816*40947Sbostic  *	Note: this checking is done in find_execute().
817*40947Sbostic  */
818*40947Sbostic PLAN *
819*40947Sbostic c_xdev()
820*40947Sbostic {
821*40947Sbostic 	extern int xdev;
822*40947Sbostic 	PLAN *new;
823*40947Sbostic 
824*40947Sbostic 	xdev = 1;
825*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
826*40947Sbostic 
827*40947Sbostic 	NEW(T_XDEV, f_always_true);
828*40947Sbostic 	return(new);
829*40947Sbostic }
830*40947Sbostic 
831*40947Sbostic /*
832*40947Sbostic  * ( expression ) functions --
833*40947Sbostic  *
834*40947Sbostic  *	True if expression is true.
835*40947Sbostic  */
836*40947Sbostic f_expr(plan, entry)
837*40947Sbostic 	PLAN *plan;
838*40947Sbostic 	FTSENT *entry;
839*40947Sbostic {
840*40947Sbostic 	register PLAN *p;
841*40947Sbostic 	register int state;
842*40947Sbostic 
843*40947Sbostic 	for (p = plan->p_data[0];
844*40947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
845*40947Sbostic 	return(state);
846*40947Sbostic }
847*40947Sbostic 
848*40947Sbostic /*
849*40947Sbostic  * T_OPENPAREN and T_CLOSEPAREN nodes are temporary place markers.  They are
850*40947Sbostic  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
851*40947Sbostic  * to a T_EXPR node containing the expression and the ')' node is discarded.
852*40947Sbostic  */
853*40947Sbostic PLAN *
854*40947Sbostic c_openparen()
855*40947Sbostic {
856*40947Sbostic 	PLAN *new;
857*40947Sbostic 
858*40947Sbostic 	NEW(T_OPENPAREN, (int (*)())-1);
859*40947Sbostic 	return(new);
860*40947Sbostic }
861*40947Sbostic 
862*40947Sbostic PLAN *
863*40947Sbostic c_closeparen()
864*40947Sbostic {
865*40947Sbostic 	PLAN *new;
866*40947Sbostic 
867*40947Sbostic 	NEW(T_CLOSEPAREN, (int (*)())-1);
868*40947Sbostic 	return(new);
869*40947Sbostic }
870*40947Sbostic 
871*40947Sbostic /*
872*40947Sbostic  * -mtime n functions --
873*40947Sbostic  *
874*40947Sbostic  *	True if the difference between the file modification time and the
875*40947Sbostic  *	current time is n 24 hour periods.
876*40947Sbostic  */
877*40947Sbostic f_mtime(plan, entry)
878*40947Sbostic 	PLAN *plan;
879*40947Sbostic 	FTSENT *entry;
880*40947Sbostic {
881*40947Sbostic 	extern time_t now;
882*40947Sbostic 
883*40947Sbostic 	COMPARE((now - entry->statb.st_mtime + SECSPERDAY - 1) / SECSPERDAY,
884*40947Sbostic 	    plan->t_data);
885*40947Sbostic }
886*40947Sbostic 
887*40947Sbostic PLAN *
888*40947Sbostic c_mtime(arg)
889*40947Sbostic 	char *arg;
890*40947Sbostic {
891*40947Sbostic 	PLAN *new;
892*40947Sbostic 
893*40947Sbostic 	ftsoptions &= ~FTS_NOSTAT;
894*40947Sbostic 
895*40947Sbostic 	NEW(T_MTIME, f_mtime);
896*40947Sbostic 	new->t_data = find_parsenum(new, "-mtime", arg, (char *)NULL);
897*40947Sbostic 	return(new);
898*40947Sbostic }
899*40947Sbostic 
900*40947Sbostic /*
901*40947Sbostic  * ! expression functions --
902*40947Sbostic  *
903*40947Sbostic  *	Negation of a primary; the unary NOT operator.
904*40947Sbostic  */
905*40947Sbostic f_not(plan, entry)
906*40947Sbostic 	PLAN *plan;
907*40947Sbostic 	FTSENT *entry;
908*40947Sbostic {
909*40947Sbostic 	register PLAN *p;
910*40947Sbostic 	register int state;
911*40947Sbostic 
912*40947Sbostic 	for (p = plan->p_data[0];
913*40947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
914*40947Sbostic 	return(!state);
915*40947Sbostic }
916*40947Sbostic 
917*40947Sbostic PLAN *
918*40947Sbostic c_not()
919*40947Sbostic {
920*40947Sbostic 	PLAN *new;
921*40947Sbostic 
922*40947Sbostic 	NEW(T_NOT, f_not);
923*40947Sbostic 	return(new);
924*40947Sbostic }
925*40947Sbostic 
926*40947Sbostic /*
927*40947Sbostic  * expression -o expression functions --
928*40947Sbostic  *
929*40947Sbostic  *	Alternation of primaries; the OR operator.  The second expression is
930*40947Sbostic  * not evaluated if the first expression is true.
931*40947Sbostic  */
932*40947Sbostic f_or(plan, entry)
933*40947Sbostic 	PLAN *plan;
934*40947Sbostic 	FTSENT *entry;
935*40947Sbostic {
936*40947Sbostic 	register PLAN *p;
937*40947Sbostic 	register int state;
938*40947Sbostic 
939*40947Sbostic 	for (p = plan->p_data[0];
940*40947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
941*40947Sbostic 
942*40947Sbostic 	if (state)
943*40947Sbostic 		return(FIND_TRUE);
944*40947Sbostic 
945*40947Sbostic 	for (p = plan->p_data[1];
946*40947Sbostic 	    p && (state = (p->eval)(p, entry)); p = p->next);
947*40947Sbostic 	return(state);
948*40947Sbostic }
949*40947Sbostic 
950*40947Sbostic PLAN *
951*40947Sbostic c_or()
952*40947Sbostic {
953*40947Sbostic 	PLAN *new;
954*40947Sbostic 
955*40947Sbostic 	NEW(T_OR, f_or);
956*40947Sbostic 	return(new);
957*40947Sbostic }
958