function.c revision 51037
1219820Sjeff/*-
2219820Sjeff * Copyright (c) 1990, 1993
3219820Sjeff *	The Regents of the University of California.  All rights reserved.
4219820Sjeff *
5219820Sjeff * This code is derived from software contributed to Berkeley by
6219820Sjeff * Cimarron D. Taylor of the University of California, Berkeley.
7219820Sjeff *
8219820Sjeff * Redistribution and use in source and binary forms, with or without
9219820Sjeff * modification, are permitted provided that the following conditions
10219820Sjeff * are met:
11219820Sjeff * 1. Redistributions of source code must retain the above copyright
12219820Sjeff *    notice, this list of conditions and the following disclaimer.
13219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
14219820Sjeff *    notice, this list of conditions and the following disclaimer in the
15219820Sjeff *    documentation and/or other materials provided with the distribution.
16219820Sjeff * 3. All advertising materials mentioning features or use of this software
17219820Sjeff *    must display the following acknowledgement:
18219820Sjeff *	This product includes software developed by the University of
19219820Sjeff *	California, Berkeley and its contributors.
20219820Sjeff * 4. Neither the name of the University nor the names of its contributors
21219820Sjeff *    may be used to endorse or promote products derived from this software
22219820Sjeff *    without specific prior written permission.
23219820Sjeff *
24219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27219820Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34219820Sjeff * SUCH DAMAGE.
35219820Sjeff */
36324685Shselasky
37324685Shselasky#ifndef lint
38324685Shselaskystatic char sccsid[] = "@(#)function.c	8.10 (Berkeley) 5/4/95";
39219820Sjeff#endif /* not lint */
40219820Sjeff
41219820Sjeff#include <sys/param.h>
42219820Sjeff#include <sys/ucred.h>
43219820Sjeff#include <sys/stat.h>
44219820Sjeff#include <sys/wait.h>
45219820Sjeff#include <sys/mount.h>
46219820Sjeff
47219820Sjeff#include <err.h>
48219820Sjeff#include <errno.h>
49219820Sjeff#include <fnmatch.h>
50219820Sjeff#include <fts.h>
51219820Sjeff#include <grp.h>
52219820Sjeff#include <pwd.h>
53219820Sjeff#include <stdio.h>
54219820Sjeff#include <stdlib.h>
55219820Sjeff#include <string.h>
56219820Sjeff#include <unistd.h>
57219820Sjeff
58219820Sjeff#include "find.h"
59219820Sjeff
60219820Sjeff#define	COMPARE(a, b) {							\
61219820Sjeff	switch (plan->flags) {						\
62219820Sjeff	case F_EQUAL:							\
63219820Sjeff		return (a == b);					\
64219820Sjeff	case F_LESSTHAN:						\
65219820Sjeff		return (a < b);						\
66219820Sjeff	case F_GREATER:							\
67219820Sjeff		return (a > b);						\
68219820Sjeff	default:							\
69219820Sjeff		abort();						\
70219820Sjeff	}								\
71219820Sjeff}
72219820Sjeff
73219820Sjeffstatic PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *))));
74219820Sjeff
75219820Sjeff/*
76219820Sjeff * find_parsenum --
77219820Sjeff *	Parse a string of the form [+-]# and return the value.
78219820Sjeff */
79219820Sjeffstatic long long
80219820Sjefffind_parsenum(plan, option, vp, endch)
81219820Sjeff	PLAN *plan;
82219820Sjeff	char *option, *vp, *endch;
83219820Sjeff{
84219820Sjeff	long long value;
85219820Sjeff	char *endchar, *str;	/* Pointer to character ending conversion. */
86219820Sjeff
87219820Sjeff	/* Determine comparison from leading + or -. */
88219820Sjeff	str = vp;
89219820Sjeff	switch (*str) {
90219820Sjeff	case '+':
91219820Sjeff		++str;
92219820Sjeff		plan->flags = F_GREATER;
93219820Sjeff		break;
94219820Sjeff	case '-':
95219820Sjeff		++str;
96219820Sjeff		plan->flags = F_LESSTHAN;
97219820Sjeff		break;
98219820Sjeff	default:
99219820Sjeff		plan->flags = F_EQUAL;
100219820Sjeff		break;
101219820Sjeff	}
102219820Sjeff
103219820Sjeff	/*
104219820Sjeff	 * Convert the string with strtoq().  Note, if strtoq() returns zero
105219820Sjeff	 * and endchar points to the beginning of the string we know we have
106219820Sjeff	 * a syntax error.
107219820Sjeff	 */
108219820Sjeff	value = strtoq(str, &endchar, 10);
109219820Sjeff	if (value == 0 && endchar == str)
110219820Sjeff		errx(1, "%s: %s: illegal numeric value", option, vp);
111219820Sjeff	if (endchar[0] && (endch == NULL || endchar[0] != *endch))
112219820Sjeff		errx(1, "%s: %s: illegal trailing character", option, vp);
113219820Sjeff	if (endch)
114219820Sjeff		*endch = endchar[0];
115219820Sjeff	return (value);
116219820Sjeff}
117219820Sjeff
118219820Sjeff/*
119219820Sjeff * The value of n for the inode times (atime, ctime, and mtime) is a range,
120219820Sjeff * i.e. n matches from (n - 1) to n 24 hour periods.  This interacts with
121219820Sjeff * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
122219820Sjeff * user wanted.  Correct so that -1 is "less than 1".
123219820Sjeff */
124219820Sjeff#define	TIME_CORRECT(p, ttype)						\
125219820Sjeff	if ((p)->type == ttype && (p)->flags == F_LESSTHAN)		\
126219820Sjeff		++((p)->t_data);
127219820Sjeff
128219820Sjeff/*
129219820Sjeff * -amin n functions --
130219820Sjeff *
131219820Sjeff *	True if the difference between the file access time and the
132219820Sjeff *	current time is n min periods.
133219820Sjeff */
134219820Sjeffint
135219820Sjefff_amin(plan, entry)
136219820Sjeff	PLAN *plan;
137219820Sjeff	FTSENT *entry;
138219820Sjeff{
139219820Sjeff	extern time_t now;
140219820Sjeff
141219820Sjeff	COMPARE((now - entry->fts_statp->st_atime +
142219820Sjeff	    60 - 1) / 60, plan->t_data);
143219820Sjeff}
144219820Sjeff
145219820SjeffPLAN *
146219820Sjeffc_amin(arg)
147219820Sjeff	char *arg;
148219820Sjeff{
149219820Sjeff	PLAN *new;
150219820Sjeff
151219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
152219820Sjeff
153219820Sjeff	new = palloc(N_AMIN, f_amin);
154219820Sjeff	new->t_data = find_parsenum(new, "-amin", arg, NULL);
155219820Sjeff	TIME_CORRECT(new, N_AMIN);
156219820Sjeff	return (new);
157219820Sjeff}
158219820Sjeff
159219820Sjeff
160219820Sjeff/*
161219820Sjeff * -atime n functions --
162219820Sjeff *
163219820Sjeff *	True if the difference between the file access time and the
164219820Sjeff *	current time is n 24 hour periods.
165219820Sjeff */
166219820Sjeffint
167219820Sjefff_atime(plan, entry)
168219820Sjeff	PLAN *plan;
169219820Sjeff	FTSENT *entry;
170219820Sjeff{
171219820Sjeff	extern time_t now;
172219820Sjeff
173219820Sjeff	COMPARE((now - entry->fts_statp->st_atime +
174219820Sjeff	    86400 - 1) / 86400, plan->t_data);
175219820Sjeff}
176219820Sjeff
177219820SjeffPLAN *
178219820Sjeffc_atime(arg)
179219820Sjeff	char *arg;
180219820Sjeff{
181219820Sjeff	PLAN *new;
182219820Sjeff
183219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
184219820Sjeff
185219820Sjeff	new = palloc(N_ATIME, f_atime);
186219820Sjeff	new->t_data = find_parsenum(new, "-atime", arg, NULL);
187219820Sjeff	TIME_CORRECT(new, N_ATIME);
188219820Sjeff	return (new);
189219820Sjeff}
190219820Sjeff
191219820Sjeff
192219820Sjeff/*
193219820Sjeff * -cmin n functions --
194219820Sjeff *
195219820Sjeff *	True if the difference between the last change of file
196219820Sjeff *	status information and the current time is n min periods.
197219820Sjeff */
198219820Sjeffint
199219820Sjefff_cmin(plan, entry)
200219820Sjeff	PLAN *plan;
201219820Sjeff	FTSENT *entry;
202219820Sjeff{
203219820Sjeff	extern time_t now;
204219820Sjeff
205219820Sjeff	COMPARE((now - entry->fts_statp->st_ctime +
206219820Sjeff	    60 - 1) / 60, plan->t_data);
207219820Sjeff}
208219820Sjeff
209219820SjeffPLAN *
210219820Sjeffc_cmin(arg)
211219820Sjeff	char *arg;
212219820Sjeff{
213219820Sjeff	PLAN *new;
214219820Sjeff
215219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
216219820Sjeff
217219820Sjeff	new = palloc(N_CMIN, f_cmin);
218219820Sjeff	new->t_data = find_parsenum(new, "-cmin", arg, NULL);
219219820Sjeff	TIME_CORRECT(new, N_CMIN);
220219820Sjeff	return (new);
221219820Sjeff}
222219820Sjeff
223219820Sjeff/*
224219820Sjeff * -ctime n functions --
225219820Sjeff *
226219820Sjeff *	True if the difference between the last change of file
227219820Sjeff *	status information and the current time is n 24 hour periods.
228219820Sjeff */
229219820Sjeffint
230219820Sjefff_ctime(plan, entry)
231219820Sjeff	PLAN *plan;
232219820Sjeff	FTSENT *entry;
233219820Sjeff{
234219820Sjeff	extern time_t now;
235219820Sjeff
236219820Sjeff	COMPARE((now - entry->fts_statp->st_ctime +
237219820Sjeff	    86400 - 1) / 86400, plan->t_data);
238219820Sjeff}
239219820Sjeff
240219820SjeffPLAN *
241219820Sjeffc_ctime(arg)
242219820Sjeff	char *arg;
243219820Sjeff{
244219820Sjeff	PLAN *new;
245219820Sjeff
246219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
247219820Sjeff
248219820Sjeff	new = palloc(N_CTIME, f_ctime);
249219820Sjeff	new->t_data = find_parsenum(new, "-ctime", arg, NULL);
250219820Sjeff	TIME_CORRECT(new, N_CTIME);
251219820Sjeff	return (new);
252219820Sjeff}
253219820Sjeff
254219820Sjeff
255219820Sjeff/*
256219820Sjeff * -depth functions --
257219820Sjeff *
258219820Sjeff *	Always true, causes descent of the directory hierarchy to be done
259219820Sjeff *	so that all entries in a directory are acted on before the directory
260219820Sjeff *	itself.
261219820Sjeff */
262219820Sjeffint
263219820Sjefff_always_true(plan, entry)
264219820Sjeff	PLAN *plan;
265219820Sjeff	FTSENT *entry;
266219820Sjeff{
267219820Sjeff	return (1);
268219820Sjeff}
269219820Sjeff
270219820SjeffPLAN *
271219820Sjeffc_depth()
272219820Sjeff{
273219820Sjeff	isdepth = 1;
274219820Sjeff
275219820Sjeff	return (palloc(N_DEPTH, f_always_true));
276219820Sjeff}
277219820Sjeff
278219820Sjeff/*
279219820Sjeff * [-exec | -ok] utility [arg ... ] ; functions --
280219820Sjeff *
281219820Sjeff *	True if the executed utility returns a zero value as exit status.
282219820Sjeff *	The end of the primary expression is delimited by a semicolon.  If
283219820Sjeff *	"{}" occurs anywhere, it gets replaced by the current pathname.
284219820Sjeff *	The current directory for the execution of utility is the same as
285219820Sjeff *	the current directory when the find utility was started.
286219820Sjeff *
287219820Sjeff *	The primary -ok is different in that it requests affirmation of the
288219820Sjeff *	user before executing the utility.
289219820Sjeff */
290219820Sjeffint
291219820Sjefff_exec(plan, entry)
292219820Sjeff	register PLAN *plan;
293219820Sjeff	FTSENT *entry;
294219820Sjeff{
295219820Sjeff	extern int dotfd;
296219820Sjeff	register int cnt;
297219820Sjeff	pid_t pid;
298219820Sjeff	int status;
299219820Sjeff
300219820Sjeff	for (cnt = 0; plan->e_argv[cnt]; ++cnt)
301219820Sjeff		if (plan->e_len[cnt])
302219820Sjeff			brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
303219820Sjeff			    entry->fts_path, plan->e_len[cnt]);
304219820Sjeff
305219820Sjeff	if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
306219820Sjeff		return (0);
307219820Sjeff
308219820Sjeff	/* make sure find output is interspersed correctly with subprocesses */
309219820Sjeff	fflush(stdout);
310219820Sjeff
311219820Sjeff	switch (pid = fork()) {
312219820Sjeff	case -1:
313219820Sjeff		err(1, "fork");
314219820Sjeff		/* NOTREACHED */
315219820Sjeff	case 0:
316219820Sjeff		if (fchdir(dotfd)) {
317219820Sjeff			warn("chdir");
318219820Sjeff			_exit(1);
319219820Sjeff		}
320219820Sjeff		execvp(plan->e_argv[0], plan->e_argv);
321219820Sjeff		warn("%s", plan->e_argv[0]);
322219820Sjeff		_exit(1);
323219820Sjeff	}
324219820Sjeff	pid = waitpid(pid, &status, 0);
325219820Sjeff	return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
326219820Sjeff}
327219820Sjeff
328219820Sjeff/*
329219820Sjeff * c_exec --
330219820Sjeff *	build three parallel arrays, one with pointers to the strings passed
331219820Sjeff *	on the command line, one with (possibly duplicated) pointers to the
332219820Sjeff *	argv array, and one with integer values that are lengths of the
333219820Sjeff *	strings, but also flags meaning that the string has to be massaged.
334219820Sjeff */
335219820SjeffPLAN *
336219820Sjeffc_exec(argvp, isok)
337219820Sjeff	char ***argvp;
338219820Sjeff	int isok;
339219820Sjeff{
340219820Sjeff	PLAN *new;			/* node returned */
341219820Sjeff	register int cnt;
342219820Sjeff	register char **argv, **ap, *p;
343219820Sjeff
344219820Sjeff	isoutput = 1;
345219820Sjeff
346219820Sjeff	new = palloc(N_EXEC, f_exec);
347219820Sjeff	if (isok)
348219820Sjeff		new->flags = F_NEEDOK;
349219820Sjeff
350219820Sjeff	for (ap = argv = *argvp;; ++ap) {
351219820Sjeff		if (!*ap)
352219820Sjeff			errx(1,
353219820Sjeff			    "%s: no terminating \";\"", isok ? "-ok" : "-exec");
354219820Sjeff		if (**ap == ';')
355219820Sjeff			break;
356219820Sjeff	}
357219820Sjeff
358219820Sjeff	cnt = ap - *argvp + 1;
359219820Sjeff	new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
360219820Sjeff	new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
361219820Sjeff	new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
362219820Sjeff
363219820Sjeff	for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
364219820Sjeff		new->e_orig[cnt] = *argv;
365219820Sjeff		for (p = *argv; *p; ++p)
366219820Sjeff			if (p[0] == '{' && p[1] == '}') {
367219820Sjeff				new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
368219820Sjeff				new->e_len[cnt] = MAXPATHLEN;
369219820Sjeff				break;
370219820Sjeff			}
371219820Sjeff		if (!*p) {
372219820Sjeff			new->e_argv[cnt] = *argv;
373219820Sjeff			new->e_len[cnt] = 0;
374219820Sjeff		}
375219820Sjeff	}
376219820Sjeff	new->e_argv[cnt] = new->e_orig[cnt] = NULL;
377219820Sjeff
378219820Sjeff	*argvp = argv + 1;
379219820Sjeff	return (new);
380219820Sjeff}
381219820Sjeff
382219820Sjeff/*
383219820Sjeff * -execdir utility [arg ... ] ; functions --
384219820Sjeff *
385219820Sjeff *	True if the executed utility returns a zero value as exit status.
386219820Sjeff *	The end of the primary expression is delimited by a semicolon.  If
387219820Sjeff *	"{}" occurs anywhere, it gets replaced by the unqualified pathname.
388219820Sjeff *	The current directory for the execution of utility is the same as
389219820Sjeff *	the directory where the file lives.
390219820Sjeff */
391219820Sjeffint
392219820Sjefff_execdir(plan, entry)
393219820Sjeff	register PLAN *plan;
394219820Sjeff	FTSENT *entry;
395219820Sjeff{
396219820Sjeff	register int cnt;
397219820Sjeff	pid_t pid;
398219820Sjeff	int status;
399219820Sjeff	char *file;
400219820Sjeff
401219820Sjeff	/* XXX - if file/dir ends in '/' this will not work -- can it? */
402219820Sjeff	if ((file = strrchr(entry->fts_path, '/')))
403219820Sjeff	    file++;
404219820Sjeff	else
405219820Sjeff	    file = entry->fts_path;
406219820Sjeff
407219820Sjeff	for (cnt = 0; plan->e_argv[cnt]; ++cnt)
408219820Sjeff		if (plan->e_len[cnt])
409219820Sjeff			brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
410219820Sjeff			    file, plan->e_len[cnt]);
411219820Sjeff
412219820Sjeff	/* don't mix output of command with find output */
413219820Sjeff	fflush(stdout);
414219820Sjeff	fflush(stderr);
415219820Sjeff
416219820Sjeff	switch (pid = fork()) {
417219820Sjeff	case -1:
418219820Sjeff		err(1, "fork");
419219820Sjeff		/* NOTREACHED */
420219820Sjeff	case 0:
421219820Sjeff		execvp(plan->e_argv[0], plan->e_argv);
422219820Sjeff		warn("%s", plan->e_argv[0]);
423219820Sjeff		_exit(1);
424219820Sjeff	}
425219820Sjeff	pid = waitpid(pid, &status, 0);
426219820Sjeff	return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
427219820Sjeff}
428219820Sjeff
429219820Sjeff/*
430219820Sjeff * c_execdir --
431219820Sjeff *	build three parallel arrays, one with pointers to the strings passed
432219820Sjeff *	on the command line, one with (possibly duplicated) pointers to the
433219820Sjeff *	argv array, and one with integer values that are lengths of the
434219820Sjeff *	strings, but also flags meaning that the string has to be massaged.
435219820Sjeff */
436219820SjeffPLAN *
437219820Sjeffc_execdir(argvp)
438219820Sjeff	char ***argvp;
439219820Sjeff{
440219820Sjeff	PLAN *new;			/* node returned */
441219820Sjeff	register int cnt;
442219820Sjeff	register char **argv, **ap, *p;
443219820Sjeff
444219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
445219820Sjeff	isoutput = 1;
446219820Sjeff
447219820Sjeff	new = palloc(N_EXECDIR, f_execdir);
448219820Sjeff
449219820Sjeff	for (ap = argv = *argvp;; ++ap) {
450219820Sjeff		if (!*ap)
451219820Sjeff			errx(1,
452219820Sjeff			    "-execdir: no terminating \";\"");
453219820Sjeff		if (**ap == ';')
454219820Sjeff			break;
455219820Sjeff	}
456219820Sjeff
457219820Sjeff	cnt = ap - *argvp + 1;
458219820Sjeff	new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
459219820Sjeff	new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
460219820Sjeff	new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
461219820Sjeff
462219820Sjeff	for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
463219820Sjeff		new->e_orig[cnt] = *argv;
464219820Sjeff		for (p = *argv; *p; ++p)
465219820Sjeff			if (p[0] == '{' && p[1] == '}') {
466219820Sjeff				new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
467219820Sjeff				new->e_len[cnt] = MAXPATHLEN;
468219820Sjeff				break;
469219820Sjeff			}
470219820Sjeff		if (!*p) {
471219820Sjeff			new->e_argv[cnt] = *argv;
472219820Sjeff			new->e_len[cnt] = 0;
473219820Sjeff		}
474219820Sjeff	}
475219820Sjeff	new->e_argv[cnt] = new->e_orig[cnt] = NULL;
476219820Sjeff
477219820Sjeff	*argvp = argv + 1;
478219820Sjeff	return (new);
479219820Sjeff}
480219820Sjeff
481219820Sjeff/*
482219820Sjeff * -follow functions --
483219820Sjeff *
484219820Sjeff *	Always true, causes symbolic links to be followed on a global
485219820Sjeff *	basis.
486219820Sjeff */
487219820SjeffPLAN *
488219820Sjeffc_follow()
489219820Sjeff{
490219820Sjeff	ftsoptions &= ~FTS_PHYSICAL;
491219820Sjeff	ftsoptions |= FTS_LOGICAL;
492219820Sjeff
493219820Sjeff	return (palloc(N_FOLLOW, f_always_true));
494219820Sjeff}
495219820Sjeff
496219820Sjeff/*
497219820Sjeff * -fstype functions --
498219820Sjeff *
499219820Sjeff *	True if the file is of a certain type.
500219820Sjeff */
501219820Sjeffint
502219820Sjefff_fstype(plan, entry)
503219820Sjeff	PLAN *plan;
504219820Sjeff	FTSENT *entry;
505219820Sjeff{
506219820Sjeff	static dev_t curdev;	/* need a guaranteed illegal dev value */
507219820Sjeff	static int first = 1;
508219820Sjeff	struct statfs sb;
509219820Sjeff	static int val_type, val_flags;
510219820Sjeff	char *p, save[2];
511219820Sjeff
512219820Sjeff	/* Only check when we cross mount point. */
513219820Sjeff	if (first || curdev != entry->fts_statp->st_dev) {
514219820Sjeff		curdev = entry->fts_statp->st_dev;
515219820Sjeff
516219820Sjeff		/*
517219820Sjeff		 * Statfs follows symlinks; find wants the link's file system,
518219820Sjeff		 * not where it points.
519219820Sjeff		 */
520219820Sjeff		if (entry->fts_info == FTS_SL ||
521219820Sjeff		    entry->fts_info == FTS_SLNONE) {
522219820Sjeff			if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
523219820Sjeff				++p;
524219820Sjeff			else
525219820Sjeff				p = entry->fts_accpath;
526219820Sjeff			save[0] = p[0];
527219820Sjeff			p[0] = '.';
528219820Sjeff			save[1] = p[1];
529219820Sjeff			p[1] = '\0';
530219820Sjeff
531219820Sjeff		} else
532219820Sjeff			p = NULL;
533219820Sjeff
534219820Sjeff		if (statfs(entry->fts_accpath, &sb))
535219820Sjeff			err(1, "%s", entry->fts_accpath);
536219820Sjeff
537219820Sjeff		if (p) {
538219820Sjeff			p[0] = save[0];
539219820Sjeff			p[1] = save[1];
540219820Sjeff		}
541219820Sjeff
542219820Sjeff		first = 0;
543219820Sjeff
544219820Sjeff		/*
545219820Sjeff		 * Further tests may need both of these values, so
546219820Sjeff		 * always copy both of them.
547219820Sjeff		 */
548219820Sjeff		val_flags = sb.f_flags;
549219820Sjeff		val_type = sb.f_type;
550219820Sjeff	}
551219820Sjeff	switch (plan->flags) {
552219820Sjeff	case F_MTFLAG:
553219820Sjeff		return (val_flags & plan->mt_data) != 0;
554219820Sjeff	case F_MTTYPE:
555219820Sjeff		return (val_type == plan->mt_data);
556219820Sjeff	default:
557219820Sjeff		abort();
558219820Sjeff	}
559219820Sjeff}
560219820Sjeff
561219820Sjeff#if !defined(__NetBSD__)
562219820SjeffPLAN *
563219820Sjeffc_fstype(arg)
564219820Sjeff	char *arg;
565219820Sjeff{
566219820Sjeff	register PLAN *new;
567219820Sjeff	struct vfsconf vfc;
568219820Sjeff
569219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
570219820Sjeff
571219820Sjeff	new = palloc(N_FSTYPE, f_fstype);
572219820Sjeff
573219820Sjeff	/*
574219820Sjeff	 * Check first for a filesystem name.
575219820Sjeff	 */
576219820Sjeff	if (getvfsbyname(arg, &vfc) == 0) {
577219820Sjeff		new->flags = F_MTTYPE;
578219820Sjeff		new->mt_data = vfc.vfc_typenum;
579219820Sjeff		return (new);
580219820Sjeff	}
581219820Sjeff
582219820Sjeff	switch (*arg) {
583219820Sjeff	case 'l':
584219820Sjeff		if (!strcmp(arg, "local")) {
585219820Sjeff			new->flags = F_MTFLAG;
586219820Sjeff			new->mt_data = MNT_LOCAL;
587219820Sjeff			return (new);
588219820Sjeff		}
589219820Sjeff		break;
590219820Sjeff	case 'r':
591219820Sjeff		if (!strcmp(arg, "rdonly")) {
592219820Sjeff			new->flags = F_MTFLAG;
593219820Sjeff			new->mt_data = MNT_RDONLY;
594219820Sjeff			return (new);
595219820Sjeff		}
596219820Sjeff		break;
597219820Sjeff	}
598219820Sjeff	errx(1, "%s: unknown file type", arg);
599219820Sjeff	/* NOTREACHED */
600219820Sjeff}
601219820Sjeff#endif
602219820Sjeff
603219820Sjeff/*
604219820Sjeff * -group gname functions --
605219820Sjeff *
606219820Sjeff *	True if the file belongs to the group gname.  If gname is numeric and
607219820Sjeff *	an equivalent of the getgrnam() function does not return a valid group
608219820Sjeff *	name, gname is taken as a group ID.
609219820Sjeff */
610219820Sjeffint
611219820Sjefff_group(plan, entry)
612219820Sjeff	PLAN *plan;
613219820Sjeff	FTSENT *entry;
614219820Sjeff{
615219820Sjeff	return (entry->fts_statp->st_gid == plan->g_data);
616219820Sjeff}
617219820Sjeff
618219820SjeffPLAN *
619219820Sjeffc_group(gname)
620219820Sjeff	char *gname;
621219820Sjeff{
622219820Sjeff	PLAN *new;
623219820Sjeff	struct group *g;
624219820Sjeff	gid_t gid;
625219820Sjeff
626219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
627219820Sjeff
628219820Sjeff	g = getgrnam(gname);
629219820Sjeff	if (g == NULL) {
630219820Sjeff		gid = atoi(gname);
631219820Sjeff		if (gid == 0 && gname[0] != '0')
632219820Sjeff			errx(1, "-group: %s: no such group", gname);
633219820Sjeff	} else
634219820Sjeff		gid = g->gr_gid;
635219820Sjeff
636219820Sjeff	new = palloc(N_GROUP, f_group);
637219820Sjeff	new->g_data = gid;
638219820Sjeff	return (new);
639219820Sjeff}
640219820Sjeff
641219820Sjeff/*
642219820Sjeff * -inum n functions --
643219820Sjeff *
644219820Sjeff *	True if the file has inode # n.
645219820Sjeff */
646219820Sjeffint
647219820Sjefff_inum(plan, entry)
648219820Sjeff	PLAN *plan;
649219820Sjeff	FTSENT *entry;
650219820Sjeff{
651219820Sjeff	COMPARE(entry->fts_statp->st_ino, plan->i_data);
652219820Sjeff}
653219820Sjeff
654219820SjeffPLAN *
655219820Sjeffc_inum(arg)
656219820Sjeff	char *arg;
657219820Sjeff{
658219820Sjeff	PLAN *new;
659219820Sjeff
660219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
661219820Sjeff
662219820Sjeff	new = palloc(N_INUM, f_inum);
663219820Sjeff	new->i_data = find_parsenum(new, "-inum", arg, NULL);
664219820Sjeff	return (new);
665219820Sjeff}
666219820Sjeff
667219820Sjeff/*
668219820Sjeff * -links n functions --
669219820Sjeff *
670219820Sjeff *	True if the file has n links.
671219820Sjeff */
672219820Sjeffint
673219820Sjefff_links(plan, entry)
674219820Sjeff	PLAN *plan;
675219820Sjeff	FTSENT *entry;
676219820Sjeff{
677219820Sjeff	COMPARE(entry->fts_statp->st_nlink, plan->l_data);
678219820Sjeff}
679219820Sjeff
680219820SjeffPLAN *
681219820Sjeffc_links(arg)
682219820Sjeff	char *arg;
683219820Sjeff{
684219820Sjeff	PLAN *new;
685219820Sjeff
686219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
687219820Sjeff
688219820Sjeff	new = palloc(N_LINKS, f_links);
689219820Sjeff	new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
690219820Sjeff	return (new);
691219820Sjeff}
692219820Sjeff
693219820Sjeff/*
694219820Sjeff * -ls functions --
695219820Sjeff *
696219820Sjeff *	Always true - prints the current entry to stdout in "ls" format.
697219820Sjeff */
698219820Sjeffint
699219820Sjefff_ls(plan, entry)
700219820Sjeff	PLAN *plan;
701219820Sjeff	FTSENT *entry;
702219820Sjeff{
703219820Sjeff	printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
704219820Sjeff	return (1);
705219820Sjeff}
706219820Sjeff
707219820SjeffPLAN *
708219820Sjeffc_ls()
709219820Sjeff{
710219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
711219820Sjeff	isoutput = 1;
712219820Sjeff
713219820Sjeff	return (palloc(N_LS, f_ls));
714219820Sjeff}
715219820Sjeff
716219820Sjeff/*
717219820Sjeff * -mtime n functions --
718219820Sjeff *
719219820Sjeff *	True if the difference between the file modification time and the
720219820Sjeff *	current time is n 24 hour periods.
721219820Sjeff */
722219820Sjeffint
723219820Sjefff_mtime(plan, entry)
724219820Sjeff	PLAN *plan;
725219820Sjeff	FTSENT *entry;
726219820Sjeff{
727219820Sjeff	extern time_t now;
728219820Sjeff
729219820Sjeff	COMPARE((now - entry->fts_statp->st_mtime + 86400 - 1) /
730219820Sjeff	    86400, plan->t_data);
731219820Sjeff}
732219820Sjeff
733219820SjeffPLAN *
734219820Sjeffc_mtime(arg)
735219820Sjeff	char *arg;
736219820Sjeff{
737219820Sjeff	PLAN *new;
738219820Sjeff
739219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
740219820Sjeff
741219820Sjeff	new = palloc(N_MTIME, f_mtime);
742219820Sjeff	new->t_data = find_parsenum(new, "-mtime", arg, NULL);
743219820Sjeff	TIME_CORRECT(new, N_MTIME);
744219820Sjeff	return (new);
745219820Sjeff}
746219820Sjeff
747219820Sjeff/*
748219820Sjeff * -mmin n functions --
749219820Sjeff *
750219820Sjeff *	True if the difference between the file modification time and the
751219820Sjeff *	current time is n min periods.
752219820Sjeff */
753219820Sjeffint
754219820Sjefff_mmin(plan, entry)
755219820Sjeff	PLAN *plan;
756219820Sjeff	FTSENT *entry;
757219820Sjeff{
758219820Sjeff	extern time_t now;
759219820Sjeff
760219820Sjeff	COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /
761219820Sjeff	    60, plan->t_data);
762219820Sjeff}
763219820Sjeff
764219820SjeffPLAN *
765219820Sjeffc_mmin(arg)
766219820Sjeff	char *arg;
767219820Sjeff{
768219820Sjeff	PLAN *new;
769219820Sjeff
770219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
771219820Sjeff
772219820Sjeff	new = palloc(N_MMIN, f_mmin);
773219820Sjeff	new->t_data = find_parsenum(new, "-mmin", arg, NULL);
774219820Sjeff	TIME_CORRECT(new, N_MMIN);
775219820Sjeff	return (new);
776219820Sjeff}
777219820Sjeff
778219820Sjeff
779219820Sjeff/*
780219820Sjeff * -name functions --
781219820Sjeff *
782219820Sjeff *	True if the basename of the filename being examined
783219820Sjeff *	matches pattern using Pattern Matching Notation S3.14
784219820Sjeff */
785219820Sjeffint
786219820Sjefff_name(plan, entry)
787219820Sjeff	PLAN *plan;
788219820Sjeff	FTSENT *entry;
789219820Sjeff{
790219820Sjeff	return (!fnmatch(plan->c_data, entry->fts_name, 0));
791219820Sjeff}
792219820Sjeff
793219820SjeffPLAN *
794219820Sjeffc_name(pattern)
795219820Sjeff	char *pattern;
796219820Sjeff{
797219820Sjeff	PLAN *new;
798219820Sjeff
799219820Sjeff	new = palloc(N_NAME, f_name);
800219820Sjeff	new->c_data = pattern;
801219820Sjeff	return (new);
802219820Sjeff}
803219820Sjeff
804219820Sjeff/*
805219820Sjeff * -newer file functions --
806219820Sjeff *
807219820Sjeff *	True if the current file has been modified more recently
808219820Sjeff *	then the modification time of the file named by the pathname
809219820Sjeff *	file.
810219820Sjeff */
811219820Sjeffint
812219820Sjefff_newer(plan, entry)
813219820Sjeff	PLAN *plan;
814219820Sjeff	FTSENT *entry;
815219820Sjeff{
816219820Sjeff	return (entry->fts_statp->st_mtime > plan->t_data);
817219820Sjeff}
818219820Sjeff
819219820SjeffPLAN *
820219820Sjeffc_newer(filename)
821219820Sjeff	char *filename;
822219820Sjeff{
823219820Sjeff	PLAN *new;
824219820Sjeff	struct stat sb;
825219820Sjeff
826219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
827219820Sjeff
828219820Sjeff	if (stat(filename, &sb))
829219820Sjeff		err(1, "%s", filename);
830219820Sjeff	new = palloc(N_NEWER, f_newer);
831219820Sjeff	new->t_data = sb.st_mtime;
832219820Sjeff	return (new);
833219820Sjeff}
834219820Sjeff
835219820Sjeff/*
836219820Sjeff * -nogroup functions --
837219820Sjeff *
838219820Sjeff *	True if file belongs to a user ID for which the equivalent
839219820Sjeff *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
840219820Sjeff */
841219820Sjeffint
842219820Sjefff_nogroup(plan, entry)
843219820Sjeff	PLAN *plan;
844219820Sjeff	FTSENT *entry;
845219820Sjeff{
846219820Sjeff	char *group_from_gid();
847219820Sjeff
848219820Sjeff	return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
849219820Sjeff}
850219820Sjeff
851219820SjeffPLAN *
852219820Sjeffc_nogroup()
853219820Sjeff{
854219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
855219820Sjeff
856219820Sjeff	return (palloc(N_NOGROUP, f_nogroup));
857219820Sjeff}
858219820Sjeff
859219820Sjeff/*
860219820Sjeff * -nouser functions --
861219820Sjeff *
862219820Sjeff *	True if file belongs to a user ID for which the equivalent
863219820Sjeff *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
864219820Sjeff */
865219820Sjeffint
866219820Sjefff_nouser(plan, entry)
867219820Sjeff	PLAN *plan;
868219820Sjeff	FTSENT *entry;
869219820Sjeff{
870219820Sjeff	char *user_from_uid();
871219820Sjeff
872219820Sjeff	return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
873219820Sjeff}
874219820Sjeff
875219820SjeffPLAN *
876219820Sjeffc_nouser()
877219820Sjeff{
878219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
879219820Sjeff
880219820Sjeff	return (palloc(N_NOUSER, f_nouser));
881219820Sjeff}
882219820Sjeff
883219820Sjeff/*
884219820Sjeff * -path functions --
885219820Sjeff *
886219820Sjeff *	True if the path of the filename being examined
887219820Sjeff *	matches pattern using Pattern Matching Notation S3.14
888219820Sjeff */
889219820Sjeffint
890219820Sjefff_path(plan, entry)
891219820Sjeff	PLAN *plan;
892219820Sjeff	FTSENT *entry;
893219820Sjeff{
894219820Sjeff	return (!fnmatch(plan->c_data, entry->fts_path, 0));
895219820Sjeff}
896219820Sjeff
897219820SjeffPLAN *
898219820Sjeffc_path(pattern)
899219820Sjeff	char *pattern;
900219820Sjeff{
901219820Sjeff	PLAN *new;
902219820Sjeff
903219820Sjeff	new = palloc(N_NAME, f_path);
904219820Sjeff	new->c_data = pattern;
905219820Sjeff	return (new);
906219820Sjeff}
907219820Sjeff
908219820Sjeff/*
909219820Sjeff * -perm functions --
910219820Sjeff *
911219820Sjeff *	The mode argument is used to represent file mode bits.  If it starts
912219820Sjeff *	with a leading digit, it's treated as an octal mode, otherwise as a
913219820Sjeff *	symbolic mode.
914219820Sjeff */
915219820Sjeffint
916219820Sjefff_perm(plan, entry)
917219820Sjeff	PLAN *plan;
918219820Sjeff	FTSENT *entry;
919219820Sjeff{
920219820Sjeff	mode_t mode;
921219820Sjeff
922219820Sjeff	mode = entry->fts_statp->st_mode &
923219820Sjeff	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
924219820Sjeff	if (plan->flags == F_ATLEAST)
925219820Sjeff		return ((plan->m_data | mode) == mode);
926219820Sjeff	else
927219820Sjeff		return (mode == plan->m_data);
928219820Sjeff	/* NOTREACHED */
929219820Sjeff}
930219820Sjeff
931219820SjeffPLAN *
932219820Sjeffc_perm(perm)
933219820Sjeff	char *perm;
934219820Sjeff{
935219820Sjeff	PLAN *new;
936219820Sjeff	mode_t *set;
937219820Sjeff
938219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
939219820Sjeff
940219820Sjeff	new = palloc(N_PERM, f_perm);
941219820Sjeff
942219820Sjeff	if (*perm == '-') {
943219820Sjeff		new->flags = F_ATLEAST;
944219820Sjeff		++perm;
945219820Sjeff	}
946219820Sjeff
947219820Sjeff	if ((set = setmode(perm)) == NULL)
948219820Sjeff		err(1, "-perm: %s: illegal mode string", perm);
949219820Sjeff
950219820Sjeff	new->m_data = getmode(set, 0);
951219820Sjeff	free(set);
952219820Sjeff	return (new);
953219820Sjeff}
954219820Sjeff
955219820Sjeff/*
956219820Sjeff * -print functions --
957219820Sjeff *
958219820Sjeff *	Always true, causes the current pathame to be written to
959219820Sjeff *	standard output.
960219820Sjeff */
961219820Sjeffint
962219820Sjefff_print(plan, entry)
963219820Sjeff	PLAN *plan;
964219820Sjeff	FTSENT *entry;
965219820Sjeff{
966219820Sjeff	(void)puts(entry->fts_path);
967219820Sjeff	return (1);
968219820Sjeff}
969219820Sjeff
970219820SjeffPLAN *
971219820Sjeffc_print()
972219820Sjeff{
973219820Sjeff	isoutput = 1;
974219820Sjeff
975219820Sjeff	return (palloc(N_PRINT, f_print));
976219820Sjeff}
977219820Sjeff
978219820Sjeff/*
979219820Sjeff * -print0 functions --
980219820Sjeff *
981219820Sjeff *	Always true, causes the current pathame to be written to
982219820Sjeff *	standard output followed by a NUL character
983219820Sjeff */
984219820Sjeffint
985219820Sjefff_print0(plan, entry)
986219820Sjeff	PLAN *plan;
987219820Sjeff	FTSENT *entry;
988219820Sjeff{
989219820Sjeff	fputs(entry->fts_path, stdout);
990219820Sjeff	fputc('\0', stdout);
991219820Sjeff	return (1);
992219820Sjeff}
993219820Sjeff
994219820SjeffPLAN *
995219820Sjeffc_print0()
996219820Sjeff{
997219820Sjeff	isoutput = 1;
998219820Sjeff
999219820Sjeff	return (palloc(N_PRINT0, f_print0));
1000219820Sjeff}
1001219820Sjeff
1002219820Sjeff/*
1003219820Sjeff * -prune functions --
1004219820Sjeff *
1005219820Sjeff *	Prune a portion of the hierarchy.
1006219820Sjeff */
1007219820Sjeffint
1008219820Sjefff_prune(plan, entry)
1009219820Sjeff	PLAN *plan;
1010219820Sjeff	FTSENT *entry;
1011219820Sjeff{
1012219820Sjeff	extern FTS *tree;
1013219820Sjeff
1014219820Sjeff	if (fts_set(tree, entry, FTS_SKIP))
1015219820Sjeff		err(1, "%s", entry->fts_path);
1016219820Sjeff	return (1);
1017219820Sjeff}
1018219820Sjeff
1019219820SjeffPLAN *
1020219820Sjeffc_prune()
1021219820Sjeff{
1022219820Sjeff	return (palloc(N_PRUNE, f_prune));
1023219820Sjeff}
1024219820Sjeff
1025219820Sjeff/*
1026219820Sjeff * -size n[c] functions --
1027219820Sjeff *
1028219820Sjeff *	True if the file size in bytes, divided by an implementation defined
1029219820Sjeff *	value and rounded up to the next integer, is n.  If n is followed by
1030219820Sjeff *	a c, the size is in bytes.
1031219820Sjeff */
1032219820Sjeff#define	FIND_SIZE	512
1033219820Sjeffstatic int divsize = 1;
1034219820Sjeff
1035219820Sjeffint
1036219820Sjefff_size(plan, entry)
1037219820Sjeff	PLAN *plan;
1038219820Sjeff	FTSENT *entry;
1039219820Sjeff{
1040219820Sjeff	off_t size;
1041219820Sjeff
1042219820Sjeff	size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1043219820Sjeff	    FIND_SIZE : entry->fts_statp->st_size;
1044219820Sjeff	COMPARE(size, plan->o_data);
1045219820Sjeff}
1046219820Sjeff
1047219820SjeffPLAN *
1048219820Sjeffc_size(arg)
1049219820Sjeff	char *arg;
1050219820Sjeff{
1051219820Sjeff	PLAN *new;
1052219820Sjeff	char endch;
1053219820Sjeff
1054219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
1055219820Sjeff
1056219820Sjeff	new = palloc(N_SIZE, f_size);
1057219820Sjeff	endch = 'c';
1058219820Sjeff	new->o_data = find_parsenum(new, "-size", arg, &endch);
1059219820Sjeff	if (endch == 'c')
1060219820Sjeff		divsize = 0;
1061219820Sjeff	return (new);
1062219820Sjeff}
1063219820Sjeff
1064219820Sjeff/*
1065219820Sjeff * -type c functions --
1066219820Sjeff *
1067219820Sjeff *	True if the type of the file is c, where c is b, c, d, p, f or w
1068219820Sjeff *	for block special file, character special file, directory, FIFO,
1069219820Sjeff *	regular file or whiteout respectively.
1070219820Sjeff */
1071219820Sjeffint
1072219820Sjefff_type(plan, entry)
1073219820Sjeff	PLAN *plan;
1074219820Sjeff	FTSENT *entry;
1075219820Sjeff{
1076219820Sjeff	return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1077219820Sjeff}
1078219820Sjeff
1079219820SjeffPLAN *
1080219820Sjeffc_type(typestring)
1081219820Sjeff	char *typestring;
1082219820Sjeff{
1083219820Sjeff	PLAN *new;
1084219820Sjeff	mode_t  mask;
1085219820Sjeff
1086219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
1087219820Sjeff
1088219820Sjeff	switch (typestring[0]) {
1089219820Sjeff	case 'b':
1090219820Sjeff		mask = S_IFBLK;
1091219820Sjeff		break;
1092219820Sjeff	case 'c':
1093219820Sjeff		mask = S_IFCHR;
1094219820Sjeff		break;
1095219820Sjeff	case 'd':
1096219820Sjeff		mask = S_IFDIR;
1097219820Sjeff		break;
1098219820Sjeff	case 'f':
1099219820Sjeff		mask = S_IFREG;
1100219820Sjeff		break;
1101219820Sjeff	case 'l':
1102219820Sjeff		mask = S_IFLNK;
1103219820Sjeff		break;
1104219820Sjeff	case 'p':
1105219820Sjeff		mask = S_IFIFO;
1106219820Sjeff		break;
1107219820Sjeff	case 's':
1108219820Sjeff		mask = S_IFSOCK;
1109219820Sjeff		break;
1110219820Sjeff#ifdef FTS_WHITEOUT
1111219820Sjeff	case 'w':
1112219820Sjeff		mask = S_IFWHT;
1113219820Sjeff		ftsoptions |= FTS_WHITEOUT;
1114219820Sjeff		break;
1115219820Sjeff#endif /* FTS_WHITEOUT */
1116219820Sjeff	default:
1117219820Sjeff		errx(1, "-type: %s: unknown type", typestring);
1118219820Sjeff	}
1119219820Sjeff
1120219820Sjeff	new = palloc(N_TYPE, f_type);
1121219820Sjeff	new->m_data = mask;
1122219820Sjeff	return (new);
1123219820Sjeff}
1124219820Sjeff
1125219820Sjeff/*
1126219820Sjeff * -delete functions --
1127219820Sjeff *
1128219820Sjeff *	True always.  Makes it's best shot and continues on regardless.
1129219820Sjeff */
1130219820Sjeffint
1131219820Sjefff_delete(plan, entry)
1132219820Sjeff	PLAN *plan;
1133219820Sjeff	FTSENT *entry;
1134219820Sjeff{
1135219820Sjeff	/* ignore these from fts */
1136219820Sjeff	if (strcmp(entry->fts_accpath, ".") == 0 ||
1137219820Sjeff	    strcmp(entry->fts_accpath, "..") == 0)
1138219820Sjeff		return (1);
1139219820Sjeff
1140219820Sjeff	/* sanity check */
1141219820Sjeff	if (isdepth == 0 ||			/* depth off */
1142219820Sjeff	    (ftsoptions & FTS_NOSTAT) ||	/* not stat()ing */
1143219820Sjeff	    !(ftsoptions & FTS_PHYSICAL) ||	/* physical off */
1144219820Sjeff	    (ftsoptions & FTS_LOGICAL))		/* or finally, logical on */
1145219820Sjeff		errx(1, "-delete: insecure options got turned on");
1146219820Sjeff
1147219820Sjeff	/* Potentially unsafe - do not accept relative paths whatsoever */
1148219820Sjeff	if (strchr(entry->fts_accpath, '/') != NULL)
1149219820Sjeff		errx(1, "-delete: %s: relative path potentially not safe",
1150219820Sjeff			entry->fts_accpath);
1151219820Sjeff
1152219820Sjeff	/* Turn off user immutable bits if running as root */
1153219820Sjeff	if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
1154219820Sjeff	    !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
1155219820Sjeff	    geteuid() == 0)
1156219820Sjeff		chflags(entry->fts_accpath,
1157219820Sjeff		       entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
1158219820Sjeff
1159219820Sjeff	/* rmdir directories, unlink everything else */
1160219820Sjeff	if (S_ISDIR(entry->fts_statp->st_mode)) {
1161219820Sjeff		if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
1162219820Sjeff			warn("-delete: rmdir(%s)", entry->fts_path);
1163219820Sjeff	} else {
1164219820Sjeff		if (unlink(entry->fts_accpath) < 0)
1165219820Sjeff			warn("-delete: unlink(%s)", entry->fts_path);
1166219820Sjeff	}
1167219820Sjeff
1168219820Sjeff	/* "succeed" */
1169219820Sjeff	return (1);
1170219820Sjeff}
1171219820Sjeff
1172219820SjeffPLAN *
1173219820Sjeffc_delete()
1174219820Sjeff{
1175219820Sjeff
1176219820Sjeff	ftsoptions &= ~FTS_NOSTAT;	/* no optimise */
1177219820Sjeff	ftsoptions |= FTS_PHYSICAL;	/* disable -follow */
1178219820Sjeff	ftsoptions &= ~FTS_LOGICAL;	/* disable -follow */
1179219820Sjeff	isoutput = 1;			/* possible output */
1180219820Sjeff	isdepth = 1;			/* -depth implied */
1181219820Sjeff
1182219820Sjeff	return (palloc(N_DELETE, f_delete));
1183219820Sjeff}
1184219820Sjeff
1185219820Sjeff/*
1186219820Sjeff * -user uname functions --
1187219820Sjeff *
1188219820Sjeff *	True if the file belongs to the user uname.  If uname is numeric and
1189219820Sjeff *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1190219820Sjeff *	return a valid user name, uname is taken as a user ID.
1191219820Sjeff */
1192219820Sjeffint
1193219820Sjefff_user(plan, entry)
1194219820Sjeff	PLAN *plan;
1195219820Sjeff	FTSENT *entry;
1196219820Sjeff{
1197219820Sjeff	return (entry->fts_statp->st_uid == plan->u_data);
1198219820Sjeff}
1199219820Sjeff
1200219820SjeffPLAN *
1201219820Sjeffc_user(username)
1202219820Sjeff	char *username;
1203219820Sjeff{
1204219820Sjeff	PLAN *new;
1205219820Sjeff	struct passwd *p;
1206219820Sjeff	uid_t uid;
1207219820Sjeff
1208219820Sjeff	ftsoptions &= ~FTS_NOSTAT;
1209219820Sjeff
1210219820Sjeff	p = getpwnam(username);
1211219820Sjeff	if (p == NULL) {
1212219820Sjeff		uid = atoi(username);
1213219820Sjeff		if (uid == 0 && username[0] != '0')
1214219820Sjeff			errx(1, "-user: %s: no such user", username);
1215219820Sjeff	} else
1216219820Sjeff		uid = p->pw_uid;
1217219820Sjeff
1218219820Sjeff	new = palloc(N_USER, f_user);
1219219820Sjeff	new->u_data = uid;
1220219820Sjeff	return (new);
1221219820Sjeff}
1222219820Sjeff
1223219820Sjeff/*
1224219820Sjeff * -xdev functions --
1225219820Sjeff *
1226219820Sjeff *	Always true, causes find not to decend past directories that have a
1227219820Sjeff *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1228219820Sjeff */
1229219820SjeffPLAN *
1230219820Sjeffc_xdev()
1231219820Sjeff{
1232219820Sjeff	ftsoptions |= FTS_XDEV;
1233219820Sjeff
1234219820Sjeff	return (palloc(N_XDEV, f_always_true));
1235219820Sjeff}
1236219820Sjeff
1237219820Sjeff/*
1238219820Sjeff * ( expression ) functions --
1239219820Sjeff *
1240219820Sjeff *	True if expression is true.
1241219820Sjeff */
1242219820Sjeffint
1243219820Sjefff_expr(plan, entry)
1244219820Sjeff	PLAN *plan;
1245219820Sjeff	FTSENT *entry;
1246219820Sjeff{
1247219820Sjeff	register PLAN *p;
1248219820Sjeff	register int state;
1249219820Sjeff
1250219820Sjeff	state = 0;
1251219820Sjeff	for (p = plan->p_data[0];
1252219820Sjeff	    p && (state = (p->eval)(p, entry)); p = p->next);
1253219820Sjeff	return (state);
1254219820Sjeff}
1255219820Sjeff
1256219820Sjeff/*
1257219820Sjeff * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
1258219820Sjeff * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1259219820Sjeff * to a N_EXPR node containing the expression and the ')' node is discarded.
1260219820Sjeff */
1261219820SjeffPLAN *
1262219820Sjeffc_openparen()
1263219820Sjeff{
1264219820Sjeff	return (palloc(N_OPENPAREN, (int (*)())-1));
1265219820Sjeff}
1266219820Sjeff
1267219820SjeffPLAN *
1268219820Sjeffc_closeparen()
1269219820Sjeff{
1270219820Sjeff	return (palloc(N_CLOSEPAREN, (int (*)())-1));
1271219820Sjeff}
1272219820Sjeff
1273219820Sjeff/*
1274219820Sjeff * ! expression functions --
1275219820Sjeff *
1276219820Sjeff *	Negation of a primary; the unary NOT operator.
1277219820Sjeff */
1278219820Sjeffint
1279219820Sjefff_not(plan, entry)
1280219820Sjeff	PLAN *plan;
1281219820Sjeff	FTSENT *entry;
1282219820Sjeff{
1283219820Sjeff	register PLAN *p;
1284219820Sjeff	register int state;
1285219820Sjeff
1286219820Sjeff	state = 0;
1287219820Sjeff	for (p = plan->p_data[0];
1288219820Sjeff	    p && (state = (p->eval)(p, entry)); p = p->next);
1289219820Sjeff	return (!state);
1290219820Sjeff}
1291219820Sjeff
1292219820SjeffPLAN *
1293219820Sjeffc_not()
1294219820Sjeff{
1295219820Sjeff	return (palloc(N_NOT, f_not));
1296219820Sjeff}
1297219820Sjeff
1298219820Sjeff/*
1299219820Sjeff * expression -o expression functions --
1300219820Sjeff *
1301219820Sjeff *	Alternation of primaries; the OR operator.  The second expression is
1302219820Sjeff * not evaluated if the first expression is true.
1303219820Sjeff */
1304219820Sjeffint
1305219820Sjefff_or(plan, entry)
1306219820Sjeff	PLAN *plan;
1307219820Sjeff	FTSENT *entry;
1308219820Sjeff{
1309219820Sjeff	register PLAN *p;
1310219820Sjeff	register int state;
1311219820Sjeff
1312219820Sjeff	state = 0;
1313219820Sjeff	for (p = plan->p_data[0];
1314219820Sjeff	    p && (state = (p->eval)(p, entry)); p = p->next);
1315219820Sjeff
1316219820Sjeff	if (state)
1317219820Sjeff		return (1);
1318219820Sjeff
1319219820Sjeff	for (p = plan->p_data[1];
1320219820Sjeff	    p && (state = (p->eval)(p, entry)); p = p->next);
1321219820Sjeff	return (state);
1322219820Sjeff}
1323219820Sjeff
1324219820SjeffPLAN *
1325219820Sjeffc_or()
1326219820Sjeff{
1327219820Sjeff	return (palloc(N_OR, f_or));
1328219820Sjeff}
1329219820Sjeff
1330219820Sjeffstatic PLAN *
1331219820Sjeffpalloc(t, f)
1332219820Sjeff	enum ntype t;
1333219820Sjeff	int (*f) __P((PLAN *, FTSENT *));
1334219820Sjeff{
1335219820Sjeff	PLAN *new;
1336219820Sjeff
1337219820Sjeff	if ((new = malloc(sizeof(PLAN))) == NULL)
1338219820Sjeff		err(1, NULL);
1339219820Sjeff	new->type = t;
1340219820Sjeff	new->eval = f;
1341219820Sjeff	new->flags = 0;
1342219820Sjeff	new->next = NULL;
1343219820Sjeff	return (new);
1344219820Sjeff}
1345219820Sjeff