function.c revision 207677
174462Salfred/*-
274462Salfred * Copyright (c) 1990, 1993
374462Salfred *	The Regents of the University of California.  All rights reserved.
487096Salfred *
574462Salfred * This code is derived from software contributed to Berkeley by
674462Salfred * Cimarron D. Taylor of the University of California, Berkeley.
774462Salfred *
874462Salfred * Redistribution and use in source and binary forms, with or without
974462Salfred * modification, are permitted provided that the following conditions
1074462Salfred * are met:
1174462Salfred * 1. Redistributions of source code must retain the above copyright
1274462Salfred *    notice, this list of conditions and the following disclaimer.
1374462Salfred * 2. Redistributions in binary form must reproduce the above copyright
1474462Salfred *    notice, this list of conditions and the following disclaimer in the
1574462Salfred *    documentation and/or other materials provided with the distribution.
1674462Salfred * 3. All advertising materials mentioning features or use of this software
1774462Salfred *    must display the following acknowledgement:
1874462Salfred *	This product includes software developed by the University of
1974462Salfred *	California, Berkeley and its contributors.
2074462Salfred * 4. Neither the name of the University nor the names of its contributors
2174462Salfred *    may be used to endorse or promote products derived from this software
2274462Salfred *    without specific prior written permission.
2374462Salfred *
2474462Salfred * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2574462Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2674462Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2774462Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2874462Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2974462Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3074462Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3174462Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3274462Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3374462Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3474462Salfred * SUCH DAMAGE.
3574462Salfred *
3674462Salfred *	@(#)function.c	8.10 (Berkeley) 5/4/95
37146445Scharnier */
38146445Scharnier
39146445Scharnier#include <sys/cdefs.h>
4087096Salfred__FBSDID("$FreeBSD: head/usr.bin/find/function.c 207677 2010-05-05 21:24:18Z delphij $");
4187096Salfred
4274462Salfred#include <sys/param.h>
4387096Salfred#include <sys/ucred.h>
4487096Salfred#include <sys/stat.h>
4587096Salfred#include <sys/types.h>
4674462Salfred#include <sys/acl.h>
4774462Salfred#include <sys/wait.h>
4874462Salfred#include <sys/mount.h>
4974462Salfred
5074462Salfred#include <dirent.h>
5174462Salfred#include <err.h>
5274462Salfred#include <errno.h>
5374462Salfred#include <fnmatch.h>
5474462Salfred#include <fts.h>
5574462Salfred#include <grp.h>
5674462Salfred#include <limits.h>
5774462Salfred#include <pwd.h>
5874462Salfred#include <regex.h>
5974462Salfred#include <stdio.h>
6074462Salfred#include <stdlib.h>
6174462Salfred#include <string.h>
6274462Salfred#include <unistd.h>
6374462Salfred#include <ctype.h>
6474462Salfred
6587096Salfred#include "find.h"
6687096Salfred
6787096Salfredstatic PLAN *palloc(OPTION *);
6884923Salfredstatic long long find_parsenum(PLAN *, const char *, char *, char *);
6984923Salfredstatic long long find_parsetime(PLAN *, const char *, char *);
7084923Salfredstatic char *nextarg(OPTION *, char ***);
7184923Salfred
72228990Suqsextern char **environ;
7384923Salfred
7474462Salfredstatic PLAN *lastexecplus = NULL;
7574462Salfred
7674462Salfred#define	COMPARE(a, b) do {						\
7787096Salfred	switch (plan->flags & F_ELG_MASK) {				\
7874462Salfred	case F_EQUAL:							\
7974462Salfred		return (a == b);					\
8074462Salfred	case F_LESSTHAN:						\
81165776Smjacob		return (a < b);						\
8274462Salfred	case F_GREATER:							\
8374462Salfred		return (a > b);						\
8474462Salfred	default:							\
8574462Salfred		abort();						\
8687096Salfred	}								\
87132254Smr} while(0)
88132254Smr
8974462Salfredstatic PLAN *
9074462Salfredpalloc(OPTION *option)
9187096Salfred{
9287096Salfred	PLAN *new;
9387096Salfred
9487096Salfred	if ((new = malloc(sizeof(PLAN))) == NULL)
9587096Salfred		err(1, NULL);
9687096Salfred	new->execute = option->execute;
9774462Salfred	new->flags = option->flags;
9874462Salfred	new->next = NULL;
9984923Salfred	return new;
10074462Salfred}
101228990Suqs
10274462Salfred/*
10374462Salfred * find_parsenum --
10487096Salfred *	Parse a string of the form [+-]# and return the value.
10574462Salfred */
10674462Salfredstatic long long
10774462Salfredfind_parsenum(PLAN *plan, const char *option, char *vp, char *endch)
108132254Smr{
109132254Smr	long long value;
11074462Salfred	char *endchar, *str;	/* Pointer to character ending conversion. */
11187096Salfred
11287096Salfred	/* Determine comparison from leading + or -. */
11387096Salfred	str = vp;
11474462Salfred	switch (*str) {
11587096Salfred	case '+':
11687096Salfred		++str;
11787096Salfred		plan->flags |= F_GREATER;
11887096Salfred		break;
11987096Salfred	case '-':
12087096Salfred		++str;
12174462Salfred		plan->flags |= F_LESSTHAN;
12287096Salfred		break;
12387096Salfred	default:
12487096Salfred		plan->flags |= F_EQUAL;
12587096Salfred		break;
12687096Salfred	}
12787096Salfred
12887096Salfred	/*
12987096Salfred	 * Convert the string with strtoq().  Note, if strtoq() returns zero
13087096Salfred	 * and endchar points to the beginning of the string we know we have
13187096Salfred	 * a syntax error.
13287096Salfred	 */
13387096Salfred	value = strtoq(str, &endchar, 10);
13487096Salfred	if (value == 0 && endchar == str)
13587096Salfred		errx(1, "%s: %s: illegal numeric value", option, vp);
13687096Salfred	if (endchar[0] && endch == NULL)
13787096Salfred		errx(1, "%s: %s: illegal trailing character", option, vp);
13887096Salfred	if (endch)
13987096Salfred		*endch = endchar[0];
14087096Salfred	return value;
14187096Salfred}
142165776Smjacob
14387096Salfred/*
14487096Salfred * find_parsetime --
14587096Salfred *	Parse a string of the form [+-]([0-9]+[smhdw]?)+ and return the value.
146165776Smjacob */
14787096Salfredstatic long long
14887096Salfredfind_parsetime(PLAN *plan, const char *option, char *vp)
14987096Salfred{
15087096Salfred	long long secs, value;
15187096Salfred	char *str, *unit;	/* Pointer to character ending conversion. */
15287096Salfred
15387096Salfred	/* Determine comparison from leading + or -. */
15487096Salfred	str = vp;
15587096Salfred	switch (*str) {
15687096Salfred	case '+':
15787096Salfred		++str;
15887096Salfred		plan->flags |= F_GREATER;
15987096Salfred		break;
160121558Speter	case '-':
16187096Salfred		++str;
16287096Salfred		plan->flags |= F_LESSTHAN;
16387096Salfred		break;
16487199Salfred	default:
165132254Smr		plan->flags |= F_EQUAL;
166132254Smr		break;
167132254Smr	}
16887096Salfred
16987199Salfred	value = strtoq(str, &unit, 10);
170132254Smr	if (value == 0 && unit == str) {
171132254Smr		errx(1, "%s: %s: illegal time value", option, vp);
17287199Salfred		/* NOTREACHED */
17387096Salfred	}
174129302Sstefanf	if (*unit == '\0')
17587096Salfred		return value;
17687096Salfred
17787096Salfred	/* Units syntax. */
17887096Salfred	secs = 0;
17987096Salfred	for (;;) {
18087096Salfred		switch(*unit) {
18187096Salfred		case 's':	/* seconds */
18287096Salfred			secs += value;
18387096Salfred			break;
18487096Salfred		case 'm':	/* minutes */
18587096Salfred			secs += value * 60;
18687096Salfred			break;
18787096Salfred		case 'h':	/* hours */
18887096Salfred			secs += value * 3600;
18987096Salfred			break;
19087096Salfred		case 'd':	/* days */
19187096Salfred			secs += value * 86400;
19287096Salfred			break;
19387096Salfred		case 'w':	/* weeks */
19487096Salfred			secs += value * 604800;
19587096Salfred			break;
19687096Salfred		default:
19787096Salfred			errx(1, "%s: %s: bad unit '%c'", option, vp, *unit);
19887096Salfred			/* NOTREACHED */
19987096Salfred		}
200216603Suqs		str = unit + 1;
20187096Salfred		if (*str == '\0')	/* EOS */
20287096Salfred			break;
20387096Salfred		value = strtoq(str, &unit, 10);
204165776Smjacob		if (value == 0 && unit == str) {
20587096Salfred			errx(1, "%s: %s: illegal time value", option, vp);
20687096Salfred			/* NOTREACHED */
20787096Salfred		}
20887096Salfred		if (*unit == '\0') {
20987096Salfred			errx(1, "%s: %s: missing trailing unit", option, vp);
21087096Salfred			/* NOTREACHED */
21187096Salfred		}
21287096Salfred	}
21387096Salfred	plan->flags |= F_EXACTTIME;
21487096Salfred	return secs;
21587096Salfred}
21687096Salfred
21787096Salfred/*
218146445Scharnier * nextarg --
21987096Salfred *	Check that another argument still exists, return a pointer to it,
22087096Salfred *	and increment the argument vector pointer.
22187096Salfred */
22287096Salfredstatic char *
22387096Salfrednextarg(OPTION *option, char ***argvp)
22487096Salfred{
22587096Salfred	char *arg;
22687096Salfred
22787096Salfred	if ((arg = **argvp) == 0)
22887096Salfred		errx(1, "%s: requires additional arguments", option->name);
22987096Salfred	(*argvp)++;
23087096Salfred	return arg;
23187096Salfred} /* nextarg() */
23287096Salfred
23387096Salfred/*
23487096Salfred * The value of n for the inode times (atime, birthtime, ctime, mtime) is a
23587096Salfred * range, i.e. n matches from (n - 1) to n 24 hour periods.  This interacts
23687096Salfred * with -n, such that "-mtime -1" would be less than 0 days, which isn't what
23787096Salfred * the user wanted.  Correct so that -1 is "less than 1".
23887096Salfred */
23987096Salfred#define	TIME_CORRECT(p) \
24087096Salfred	if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \
24187096Salfred		++((p)->t_data);
24287096Salfred
24387096Salfred/*
244165776Smjacob * -[acm]min n functions --
24587096Salfred *
24687096Salfred *    True if the difference between the
24787096Salfred *		file access time (-amin)
24887096Salfred *		file birth time (-Bmin)
24987096Salfred *		last change of file status information (-cmin)
25087096Salfred *		file modification time (-mmin)
25187096Salfred *    and the current time is n min periods.
25287096Salfred */
25387096Salfredint
25487096Salfredf_Xmin(PLAN *plan, FTSENT *entry)
25587096Salfred{
25687096Salfred	if (plan->flags & F_TIME_C) {
25787096Salfred		COMPARE((now - entry->fts_statp->st_ctime +
25887096Salfred		    60 - 1) / 60, plan->t_data);
25987096Salfred	} else if (plan->flags & F_TIME_A) {
26087096Salfred		COMPARE((now - entry->fts_statp->st_atime +
261165776Smjacob		    60 - 1) / 60, plan->t_data);
26287096Salfred	} else if (plan->flags & F_TIME_B) {
26387096Salfred		COMPARE((now - entry->fts_statp->st_birthtime +
26487096Salfred		    60 - 1) / 60, plan->t_data);
26587096Salfred	} else {
26687096Salfred		COMPARE((now - entry->fts_statp->st_mtime +
26787096Salfred		    60 - 1) / 60, plan->t_data);
26887096Salfred	}
26987096Salfred}
27087096Salfred
27187096SalfredPLAN *
27287096Salfredc_Xmin(OPTION *option, char ***argvp)
273165776Smjacob{
27487096Salfred	char *nmins;
27587096Salfred	PLAN *new;
27687096Salfred
27787096Salfred	nmins = nextarg(option, argvp);
27887096Salfred	ftsoptions &= ~FTS_NOSTAT;
279165776Smjacob
28087096Salfred	new = palloc(option);
28187096Salfred	new->t_data = find_parsenum(new, option->name, nmins, NULL);
28287096Salfred	TIME_CORRECT(new);
28387096Salfred	return new;
28487096Salfred}
28587096Salfred
28687096Salfred/*
28787096Salfred * -[acm]time n functions --
28887096Salfred *
28987096Salfred *	True if the difference between the
29087096Salfred *		file access time (-atime)
29187096Salfred *		file birth time (-Btime)
29287096Salfred *		last change of file status information (-ctime)
29387096Salfred *		file modification time (-mtime)
29487096Salfred *	and the current time is n 24 hour periods.
29587096Salfred */
29687096Salfred
29787096Salfredint
29887096Salfredf_Xtime(PLAN *plan, FTSENT *entry)
29987096Salfred{
30087096Salfred	time_t xtime;
30187096Salfred
30287096Salfred	if (plan->flags & F_TIME_A)
30387096Salfred		xtime = entry->fts_statp->st_atime;
30487096Salfred	else if (plan->flags & F_TIME_B)
30587096Salfred		xtime = entry->fts_statp->st_birthtime;
30687096Salfred	else if (plan->flags & F_TIME_C)
30787096Salfred		xtime = entry->fts_statp->st_ctime;
30887096Salfred	else
30987096Salfred		xtime = entry->fts_statp->st_mtime;
31087096Salfred
31187096Salfred	if (plan->flags & F_EXACTTIME)
31287096Salfred		COMPARE(now - xtime, plan->t_data);
31387096Salfred	else
31487096Salfred		COMPARE((now - xtime + 86400 - 1) / 86400, plan->t_data);
31592911Salfred}
31687096Salfred
31787096SalfredPLAN *
31887096Salfredc_Xtime(OPTION *option, char ***argvp)
31992911Salfred{
32087096Salfred	char *value;
32187096Salfred	PLAN *new;
32292911Salfred
32387096Salfred	value = nextarg(option, argvp);
32487096Salfred	ftsoptions &= ~FTS_NOSTAT;
32587096Salfred
32687096Salfred	new = palloc(option);
32787096Salfred	new->t_data = find_parsetime(new, option->name, value);
32887096Salfred	if (!(new->flags & F_EXACTTIME))
32987096Salfred		TIME_CORRECT(new);
33087096Salfred	return new;
33192911Salfred}
33287096Salfred
33387096Salfred/*
33487096Salfred * -maxdepth/-mindepth n functions --
33587096Salfred *
33692911Salfred *        Does the same as -prune if the level of the current file is
337165776Smjacob *        greater/less than the specified maximum/minimum depth.
33887096Salfred *
33987096Salfred *        Note that -maxdepth and -mindepth are handled specially in
34087096Salfred *        find_execute() so their f_* functions are set to f_always_true().
34187096Salfred */
34287096SalfredPLAN *
34392911Salfredc_mXXdepth(OPTION *option, char ***argvp)
34487096Salfred{
34587096Salfred	char *dstr;
346165776Smjacob	PLAN *new;
34787096Salfred
34887096Salfred	dstr = nextarg(option, argvp);
349165776Smjacob	if (dstr[0] == '-')
350168640Skuriyama		/* all other errors handled by find_parsenum() */
351168640Skuriyama		errx(1, "%s: %s: value must be positive", option->name, dstr);
352168640Skuriyama
35392911Salfred	new = palloc(option);
35487096Salfred	if (option->flags & F_MAXDEPTH)
35587096Salfred		maxdepth = find_parsenum(new, option->name, dstr, NULL);
35687096Salfred	else
35787096Salfred		mindepth = find_parsenum(new, option->name, dstr, NULL);
35887096Salfred	return new;
35987096Salfred}
36087096Salfred
36187096Salfred/*
36287096Salfred * -acl function --
36387096Salfred *
36487096Salfred *	Show files with EXTENDED ACL attributes.
36587096Salfred */
36687096Salfredint
36787096Salfredf_acl(PLAN *plan __unused, FTSENT *entry)
36887096Salfred{
36987096Salfred	acl_t facl;
37087096Salfred	acl_type_t acl_type;
37187096Salfred	int acl_supported = 0, ret, trivial;
37287096Salfred
37387096Salfred	if (S_ISLNK(entry->fts_statp->st_mode))
37487096Salfred		return 0;
375132254Smr	ret = pathconf(entry->fts_accpath, _PC_ACL_NFS4);
376132254Smr	if (ret > 0) {
377132254Smr		acl_supported = 1;
378132254Smr		acl_type = ACL_TYPE_NFS4;
379132254Smr	} else if (ret < 0 && errno != EINVAL) {
380132254Smr		warn("%s", entry->fts_accpath);
381132254Smr		return (0);
382132254Smr	}
383132254Smr	if (acl_supported == 0) {
384132254Smr		ret = pathconf(entry->fts_accpath, _PC_ACL_EXTENDED);
38574462Salfred		if (ret > 0) {
38687096Salfred			acl_supported = 1;
38784923Salfred			acl_type = ACL_TYPE_ACCESS;
38887096Salfred		} else if (ret < 0 && errno != EINVAL) {
38987096Salfred			warn("%s", entry->fts_accpath);
390132254Smr			return (0);
391132254Smr		}
39287096Salfred	}
39387096Salfred	if (acl_supported == 0)
394132254Smr		return (0);
39587096Salfred
396132254Smr	facl = acl_get_file(entry->fts_accpath, acl_type);
397132254Smr	if (facl == NULL) {
398132254Smr		warn("%s", entry->fts_accpath);
399132254Smr		return (0);
400132254Smr	}
401132254Smr	ret = acl_is_trivial_np(facl, &trivial);
402132254Smr	acl_free(facl);
40387096Salfred	if (ret) {
40487096Salfred		warn("%s", entry->fts_accpath);
40587096Salfred		acl_free(facl);
406132254Smr		return (0);
407132254Smr	}
408132254Smr	if (trivial)
40987096Salfred		return (0);
41087096Salfred	return (1);
41187096Salfred}
41287096Salfred
41387096SalfredPLAN *
41487096Salfredc_acl(OPTION *option, char ***argvp __unused)
41587096Salfred{
41687096Salfred	ftsoptions &= ~FTS_NOSTAT;
41787096Salfred	return (palloc(option));
41887096Salfred}
41987096Salfred
42087096Salfred/*
42187096Salfred * -delete functions --
42287096Salfred *
42387096Salfred *	True always.  Makes its best shot and continues on regardless.
42487096Salfred */
42587096Salfredint
42687096Salfredf_delete(PLAN *plan __unused, FTSENT *entry)
427132254Smr{
428132254Smr	/* ignore these from fts */
429132254Smr	if (strcmp(entry->fts_accpath, ".") == 0 ||
430132254Smr	    strcmp(entry->fts_accpath, "..") == 0)
431132254Smr		return 1;
432132254Smr
433132254Smr	/* sanity check */
434132254Smr	if (isdepth == 0 ||			/* depth off */
435132254Smr	    (ftsoptions & FTS_NOSTAT))		/* not stat()ing */
43687096Salfred		errx(1, "-delete: insecure options got turned on");
43787096Salfred
43887096Salfred	if (!(ftsoptions & FTS_PHYSICAL) ||	/* physical off */
43987096Salfred	    (ftsoptions & FTS_LOGICAL))		/* or finally, logical on */
44087096Salfred		errx(1, "-delete: forbidden when symlinks are followed");
44187096Salfred
44287096Salfred	/* Potentially unsafe - do not accept relative paths whatsoever */
44387199Salfred	if (strchr(entry->fts_accpath, '/') != NULL)
444132254Smr		errx(1, "-delete: %s: relative path potentially not safe",
445132254Smr			entry->fts_accpath);
44687199Salfred
44787096Salfred	/* Turn off user immutable bits if running as root */
44887096Salfred	if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
44987096Salfred	    !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
45087096Salfred	    geteuid() == 0)
45187096Salfred		lchflags(entry->fts_accpath,
45287096Salfred		       entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
45387096Salfred
45487096Salfred	/* rmdir directories, unlink everything else */
45587096Salfred	if (S_ISDIR(entry->fts_statp->st_mode)) {
45687096Salfred		if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
45787096Salfred			warn("-delete: rmdir(%s)", entry->fts_path);
45887096Salfred	} else {
45987096Salfred		if (unlink(entry->fts_accpath) < 0)
46087096Salfred			warn("-delete: unlink(%s)", entry->fts_path);
46187096Salfred	}
46287096Salfred
46387096Salfred	/* "succeed" */
46487096Salfred	return 1;
46587096Salfred}
46687096Salfred
467132254SmrPLAN *
46887096Salfredc_delete(OPTION *option, char ***argvp __unused)
46987096Salfred{
47087096Salfred
47187096Salfred	ftsoptions &= ~FTS_NOSTAT;	/* no optimise */
47287096Salfred	isoutput = 1;			/* possible output */
47387096Salfred	isdepth = 1;			/* -depth implied */
47487096Salfred
47587096Salfred	return palloc(option);
47687096Salfred}
47784923Salfred
47884923Salfred
47987096Salfred/*
48084923Salfred * always_true --
48187096Salfred *
48287096Salfred *	Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true
48384923Salfred */
48487096Salfredint
48587096Salfredf_always_true(PLAN *plan __unused, FTSENT *entry __unused)
48687096Salfred{
48787096Salfred	return 1;
48887096Salfred}
48987096Salfred
49087096Salfred/*
49187096Salfred * -depth functions --
49287096Salfred *
49387096Salfred *	With argument: True if the file is at level n.
49484923Salfred *	Without argument: Always true, causes descent of the directory hierarchy
49587096Salfred *	to be done so that all entries in a directory are acted on before the
49684923Salfred *	directory itself.
49787096Salfred */
49884923Salfredint
49984923Salfredf_depth(PLAN *plan, FTSENT *entry)
50087096Salfred{
50184923Salfred	if (plan->flags & F_DEPTH)
50287096Salfred		COMPARE(entry->fts_level, plan->d_data);
50387096Salfred	else
50487096Salfred		return 1;
505165776Smjacob}
50687096Salfred
50774462SalfredPLAN *
50887096Salfredc_depth(OPTION *option, char ***argvp)
50987096Salfred{
51087096Salfred	PLAN *new;
51187096Salfred	char *str;
51287096Salfred
51387096Salfred	new = palloc(option);
51487096Salfred
51587096Salfred	str = **argvp;
51687096Salfred	if (str && !(new->flags & F_DEPTH)) {
51787096Salfred		/* skip leading + or - */
51887096Salfred		if (*str == '+' || *str == '-')
51987096Salfred			str++;
52087096Salfred		/* skip sign */
52174462Salfred		if (*str == '+' || *str == '-')
52287096Salfred			str++;
52387096Salfred		if (isdigit(*str))
52487096Salfred			new->flags |= F_DEPTH;
52587096Salfred	}
526146445Scharnier
52787096Salfred	if (new->flags & F_DEPTH) {	/* -depth n */
52887096Salfred		char *ndepth;
52987096Salfred
53087096Salfred		ndepth = nextarg(option, argvp);
53187096Salfred		new->d_data = find_parsenum(new, option->name, ndepth, NULL);
53287096Salfred	} else {			/* -d */
53387096Salfred		isdepth = 1;
53487096Salfred	}
53587096Salfred
53687096Salfred	return new;
53787096Salfred}
53887096Salfred
53987096Salfred/*
54087096Salfred * -empty functions --
54187096Salfred *
54287096Salfred *	True if the file or directory is empty
54387096Salfred */
54487096Salfredint
54587096Salfredf_empty(PLAN *plan __unused, FTSENT *entry)
54687096Salfred{
54787096Salfred	if (S_ISREG(entry->fts_statp->st_mode) &&
548146445Scharnier	    entry->fts_statp->st_size == 0)
54987096Salfred		return 1;
55087096Salfred	if (S_ISDIR(entry->fts_statp->st_mode)) {
55187096Salfred		struct dirent *dp;
55287096Salfred		int empty;
55387096Salfred		DIR *dir;
55487096Salfred
55587096Salfred		empty = 1;
55687096Salfred		dir = opendir(entry->fts_accpath);
55787096Salfred		if (dir == NULL)
55887096Salfred			err(1, "%s", entry->fts_accpath);
55987096Salfred		for (dp = readdir(dir); dp; dp = readdir(dir))
56087096Salfred			if (dp->d_name[0] != '.' ||
56187096Salfred			    (dp->d_name[1] != '\0' &&
56287096Salfred			     (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
56387096Salfred				empty = 0;
56487096Salfred				break;
56587096Salfred			}
56687096Salfred		closedir(dir);
56787096Salfred		return empty;
56887096Salfred	}
56987096Salfred	return 0;
57087096Salfred}
57187096Salfred
57287096SalfredPLAN *
57387096Salfredc_empty(OPTION *option, char ***argvp __unused)
57487096Salfred{
57587096Salfred	ftsoptions &= ~FTS_NOSTAT;
57687096Salfred
57787096Salfred	return palloc(option);
57887096Salfred}
57987096Salfred
58087096Salfred/*
58187096Salfred * [-exec | -execdir | -ok] utility [arg ... ] ; functions --
58287096Salfred *
58387096Salfred *	True if the executed utility returns a zero value as exit status.
584146445Scharnier *	The end of the primary expression is delimited by a semicolon.  If
58587096Salfred *	"{}" occurs anywhere, it gets replaced by the current pathname,
58687096Salfred *	or, in the case of -execdir, the current basename (filename
58787096Salfred *	without leading directory prefix). For -exec and -ok,
58887096Salfred *	the current directory for the execution of utility is the same as
58987096Salfred *	the current directory when the find utility was started, whereas
59087096Salfred *	for -execdir, it is the directory the file resides in.
59187096Salfred *
59287096Salfred *	The primary -ok differs from -exec in that it requests affirmation
59387096Salfred *	of the user before executing the utility.
59487096Salfred */
59587096Salfredint
59687096Salfredf_exec(PLAN *plan, FTSENT *entry)
59787096Salfred{
59887096Salfred	int cnt;
59987096Salfred	pid_t pid;
60087096Salfred	int status;
60187096Salfred	char *file;
60287096Salfred
60387096Salfred	if (entry == NULL && plan->flags & F_EXECPLUS) {
60487096Salfred		if (plan->e_ppos == plan->e_pbnum)
60587096Salfred			return (1);
60687096Salfred		plan->e_argv[plan->e_ppos] = NULL;
60787096Salfred		goto doexec;
60887096Salfred	}
60987096Salfred
61087096Salfred	/* XXX - if file/dir ends in '/' this will not work -- can it? */
61187096Salfred	if ((plan->flags & F_EXECDIR) && \
61287096Salfred	    (file = strrchr(entry->fts_path, '/')))
61387096Salfred		file++;
614146445Scharnier	else
61587096Salfred		file = entry->fts_path;
61687096Salfred
61787096Salfred	if (plan->flags & F_EXECPLUS) {
61887096Salfred		if ((plan->e_argv[plan->e_ppos] = strdup(file)) == NULL)
61987096Salfred			err(1, NULL);
62087096Salfred		plan->e_len[plan->e_ppos] = strlen(file);
62187096Salfred		plan->e_psize += plan->e_len[plan->e_ppos];
62287096Salfred		if (++plan->e_ppos < plan->e_pnummax &&
62387096Salfred		    plan->e_psize < plan->e_psizemax)
62487096Salfred			return (1);
62587096Salfred		plan->e_argv[plan->e_ppos] = NULL;
62687096Salfred	} else {
62787096Salfred		for (cnt = 0; plan->e_argv[cnt]; ++cnt)
62887096Salfred			if (plan->e_len[cnt])
62987096Salfred				brace_subst(plan->e_orig[cnt],
63087096Salfred				    &plan->e_argv[cnt], file,
63187096Salfred				    plan->e_len[cnt]);
63287096Salfred	}
63387096Salfred
63487096Salfreddoexec:	if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv))
63587096Salfred		return 0;
63687096Salfred
63787096Salfred	/* make sure find output is interspersed correctly with subprocesses */
63887096Salfred	fflush(stdout);
63987096Salfred	fflush(stderr);
64087096Salfred
64187096Salfred	switch (pid = fork()) {
64287096Salfred	case -1:
64387096Salfred		err(1, "fork");
64487096Salfred		/* NOTREACHED */
64587096Salfred	case 0:
64687096Salfred		/* change dir back from where we started */
64787096Salfred		if (!(plan->flags & F_EXECDIR) && fchdir(dotfd)) {
64887096Salfred			warn("chdir");
64987096Salfred			_exit(1);
65087096Salfred		}
65187096Salfred		execvp(plan->e_argv[0], plan->e_argv);
65287096Salfred		warn("%s", plan->e_argv[0]);
65387096Salfred		_exit(1);
65487096Salfred	}
65587096Salfred	if (plan->flags & F_EXECPLUS) {
65687096Salfred		while (--plan->e_ppos >= plan->e_pbnum)
65787096Salfred			free(plan->e_argv[plan->e_ppos]);
65887096Salfred		plan->e_ppos = plan->e_pbnum;
65987096Salfred		plan->e_psize = plan->e_pbsize;
66087096Salfred	}
66187096Salfred	pid = waitpid(pid, &status, 0);
66287096Salfred	return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
66387096Salfred}
66487096Salfred
66587096Salfred/*
66687096Salfred * c_exec, c_execdir, c_ok --
66787096Salfred *	build three parallel arrays, one with pointers to the strings passed
66887096Salfred *	on the command line, one with (possibly duplicated) pointers to the
66987096Salfred *	argv array, and one with integer values that are lengths of the
67087096Salfred *	strings, but also flags meaning that the string has to be massaged.
67187096Salfred */
67287096SalfredPLAN *
67387096Salfredc_exec(OPTION *option, char ***argvp)
67487096Salfred{
67574462Salfred	PLAN *new;			/* node returned */
67687096Salfred	long argmax;
67774462Salfred	int cnt, i;
67887096Salfred	char **argv, **ap, **ep, *p;
67974462Salfred
68087096Salfred	/* XXX - was in c_execdir, but seems unnecessary!?
68187096Salfred	ftsoptions &= ~FTS_NOSTAT;
68287096Salfred	*/
68387096Salfred	isoutput = 1;
68487096Salfred
68587096Salfred	/* XXX - this is a change from the previous coding */
68687096Salfred	new = palloc(option);
687165776Smjacob
68887096Salfred	for (ap = argv = *argvp;; ++ap) {
68987096Salfred		if (!*ap)
69087096Salfred			errx(1,
69187096Salfred			    "%s: no terminating \";\" or \"+\"", option->name);
69287096Salfred		if (**ap == ';')
69387096Salfred			break;
69487096Salfred		if (**ap == '+' && ap != argv && strcmp(*(ap - 1), "{}") == 0) {
69587096Salfred			new->flags |= F_EXECPLUS;
69687096Salfred			break;
69787096Salfred		}
69887096Salfred	}
69987096Salfred
70087096Salfred	if (ap == argv)
70187096Salfred		errx(1, "%s: no command specified", option->name);
70287096Salfred
70387096Salfred	cnt = ap - *argvp + 1;
70487096Salfred	if (new->flags & F_EXECPLUS) {
70587096Salfred		new->e_ppos = new->e_pbnum = cnt - 2;
70687096Salfred		if ((argmax = sysconf(_SC_ARG_MAX)) == -1) {
70787096Salfred			warn("sysconf(_SC_ARG_MAX)");
70887096Salfred			argmax = _POSIX_ARG_MAX;
70987096Salfred		}
71087096Salfred		argmax -= 1024;
71187096Salfred		for (ep = environ; *ep != NULL; ep++)
71287096Salfred			argmax -= strlen(*ep) + 1 + sizeof(*ep);
71387096Salfred		argmax -= 1 + sizeof(*ep);
71487096Salfred		new->e_pnummax = argmax / 16;
71587096Salfred		argmax -= sizeof(char *) * new->e_pnummax;
71687096Salfred		if (argmax <= 0)
71787096Salfred			errx(1, "no space for arguments");
71887096Salfred		new->e_psizemax = argmax;
71987096Salfred		new->e_pbsize = 0;
72087096Salfred		cnt += new->e_pnummax + 1;
72187096Salfred		new->e_next = lastexecplus;
72287096Salfred		lastexecplus = new;
72387096Salfred	}
72487096Salfred	if ((new->e_argv = malloc(cnt * sizeof(char *))) == NULL)
72587096Salfred		err(1, NULL);
72687096Salfred	if ((new->e_orig = malloc(cnt * sizeof(char *))) == NULL)
72787096Salfred		err(1, NULL);
72887096Salfred	if ((new->e_len = malloc(cnt * sizeof(int))) == NULL)
72987096Salfred		err(1, NULL);
730168640Skuriyama
73187096Salfred	for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
73287096Salfred		new->e_orig[cnt] = *argv;
73387096Salfred		if (new->flags & F_EXECPLUS)
73487096Salfred			new->e_pbsize += strlen(*argv) + 1;
73587096Salfred		for (p = *argv; *p; ++p)
73687096Salfred			if (!(new->flags & F_EXECPLUS) && p[0] == '{' &&
73787096Salfred			    p[1] == '}') {
73887096Salfred				if ((new->e_argv[cnt] =
73987096Salfred				    malloc(MAXPATHLEN)) == NULL)
74087096Salfred					err(1, NULL);
74187096Salfred				new->e_len[cnt] = MAXPATHLEN;
74287096Salfred				break;
74387096Salfred			}
74487096Salfred		if (!*p) {
74587096Salfred			new->e_argv[cnt] = *argv;
74687096Salfred			new->e_len[cnt] = 0;
74787096Salfred		}
74874462Salfred	}
74987096Salfred	if (new->flags & F_EXECPLUS) {
750168640Skuriyama		new->e_psize = new->e_pbsize;
75187096Salfred		cnt--;
75287096Salfred		for (i = 0; i < new->e_pnummax; i++) {
75387096Salfred			new->e_argv[cnt] = NULL;
75487096Salfred			new->e_len[cnt] = 0;
75587096Salfred			cnt++;
75687096Salfred		}
757165776Smjacob		argv = ap;
758168640Skuriyama		goto done;
75987096Salfred	}
76087096Salfred	new->e_argv[cnt] = new->e_orig[cnt] = NULL;
76187096Salfred
76287096Salfreddone:	*argvp = argv + 1;
76387096Salfred	return new;
76487096Salfred}
76587096Salfred
76687096Salfred/* Finish any pending -exec ... {} + functions. */
767168640Skuriyamavoid
76887096Salfredfinish_execplus(void)
76987096Salfred{
77087096Salfred	PLAN *p;
771168640Skuriyama
77287096Salfred	p = lastexecplus;
77387096Salfred	while (p != NULL) {
77487096Salfred		(p->execute)(p, NULL);
77587096Salfred		p = p->e_next;
77687096Salfred	}
77787096Salfred}
77887096Salfred
77987096Salfredint
78087096Salfredf_flags(PLAN *plan, FTSENT *entry)
78187096Salfred{
78287096Salfred	u_long flags;
78387096Salfred
78487096Salfred	flags = entry->fts_statp->st_flags;
78587096Salfred	if (plan->flags & F_ATLEAST)
78687096Salfred		return (flags | plan->fl_flags) == flags &&
78787096Salfred		    !(flags & plan->fl_notflags);
78887096Salfred	else if (plan->flags & F_ANY)
78987096Salfred		return (flags & plan->fl_flags) ||
79087096Salfred		    (flags | plan->fl_notflags) != flags;
79187096Salfred	else
79287096Salfred		return flags == plan->fl_flags &&
79387096Salfred		    !(plan->fl_flags & plan->fl_notflags);
79487096Salfred}
79587096Salfred
79687096SalfredPLAN *
79787096Salfredc_flags(OPTION *option, char ***argvp)
79887096Salfred{
79987096Salfred	char *flags_str;
80087096Salfred	PLAN *new;
80187096Salfred	u_long flags, notflags;
80287096Salfred
80387096Salfred	flags_str = nextarg(option, argvp);
80487096Salfred	ftsoptions &= ~FTS_NOSTAT;
80587096Salfred
80687096Salfred	new = palloc(option);
80787096Salfred
80887096Salfred	if (*flags_str == '-') {
80987096Salfred		new->flags |= F_ATLEAST;
81087096Salfred		flags_str++;
81187096Salfred	} else if (*flags_str == '+') {
81287096Salfred		new->flags |= F_ANY;
81387096Salfred		flags_str++;
814165776Smjacob	}
81587096Salfred	if (strtofflags(&flags_str, &flags, &notflags) == 1)
81687096Salfred		errx(1, "%s: %s: illegal flags string", option->name, flags_str);
81787096Salfred
81887096Salfred	new->fl_flags = flags;
81987096Salfred	new->fl_notflags = notflags;
82087096Salfred	return new;
82184923Salfred}
82287096Salfred
82387096Salfred/*
82487096Salfred * -follow functions --
82587096Salfred *
82684923Salfred *	Always true, causes symbolic links to be followed on a global
82787096Salfred *	basis.
82874462Salfred */
82987096SalfredPLAN *
83087096Salfredc_follow(OPTION *option, char ***argvp __unused)
83187096Salfred{
83287096Salfred	ftsoptions &= ~FTS_PHYSICAL;
83387096Salfred	ftsoptions |= FTS_LOGICAL;
83487096Salfred
83587096Salfred	return palloc(option);
83687096Salfred}
83787096Salfred
83887096Salfred/*
83987096Salfred * -fstype functions --
84087096Salfred *
84187096Salfred *	True if the file is of a certain type.
84287096Salfred */
84387096Salfredint
84487096Salfredf_fstype(PLAN *plan, FTSENT *entry)
845165776Smjacob{
84687096Salfred	static dev_t curdev;	/* need a guaranteed illegal dev value */
84787096Salfred	static int first = 1;
84887096Salfred	struct statfs sb;
84987096Salfred	static int val_type, val_flags;
85087096Salfred	char *p, save[2] = {0,0};
85187096Salfred
85287096Salfred	if ((plan->flags & F_MTMASK) == F_MTUNKNOWN)
85387096Salfred		return 0;
85487096Salfred
85587096Salfred	/* Only check when we cross mount point. */
85687096Salfred	if (first || curdev != entry->fts_statp->st_dev) {
85787096Salfred		curdev = entry->fts_statp->st_dev;
85887096Salfred
85987096Salfred		/*
86087096Salfred		 * Statfs follows symlinks; find wants the link's filesystem,
86187096Salfred		 * not where it points.
86287096Salfred		 */
86384923Salfred		if (entry->fts_info == FTS_SL ||
86484923Salfred		    entry->fts_info == FTS_SLNONE) {
865165776Smjacob			if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
86687096Salfred				++p;
86787096Salfred			else
868165776Smjacob				p = entry->fts_accpath;
86987096Salfred			save[0] = p[0];
87087096Salfred			p[0] = '.';
87187096Salfred			save[1] = p[1];
87287096Salfred			p[1] = '\0';
87387096Salfred		} else
87487096Salfred			p = NULL;
87587096Salfred
87687096Salfred		if (statfs(entry->fts_accpath, &sb))
877165776Smjacob			err(1, "%s", entry->fts_accpath);
87887096Salfred
87987096Salfred		if (p) {
88087096Salfred			p[0] = save[0];
88187096Salfred			p[1] = save[1];
88287096Salfred		}
88387096Salfred
88487096Salfred		first = 0;
88587096Salfred
88687096Salfred		/*
88787096Salfred		 * Further tests may need both of these values, so
88887096Salfred		 * always copy both of them.
88987096Salfred		 */
89087096Salfred		val_flags = sb.f_flags;
89187096Salfred		val_type = sb.f_type;
89287096Salfred	}
89387096Salfred	switch (plan->flags & F_MTMASK) {
89487096Salfred	case F_MTFLAG:
89587096Salfred		return val_flags & plan->mt_data;
89687096Salfred	case F_MTTYPE:
89787096Salfred		return val_type == plan->mt_data;
89887096Salfred	default:
89987096Salfred		abort();
90087096Salfred	}
90187096Salfred}
90287096Salfred
90387096SalfredPLAN *
90487096Salfredc_fstype(OPTION *option, char ***argvp)
90587096Salfred{
90674462Salfred	char *fsname;
90787096Salfred	PLAN *new;
90887096Salfred	struct xvfsconf vfc;
90987096Salfred
91087096Salfred	fsname = nextarg(option, argvp);
91174462Salfred	ftsoptions &= ~FTS_NOSTAT;
91274462Salfred
91374462Salfred	new = palloc(option);
91487096Salfred
91587096Salfred	/*
91687096Salfred	 * Check first for a filesystem name.
91787096Salfred	 */
91887096Salfred	if (getvfsbyname(fsname, &vfc) == 0) {
91987096Salfred		new->flags |= F_MTTYPE;
92087096Salfred		new->mt_data = vfc.vfc_typenum;
92187096Salfred		return new;
92274462Salfred	}
92387096Salfred
92487096Salfred	switch (*fsname) {
92587096Salfred	case 'l':
92674462Salfred		if (!strcmp(fsname, "local")) {
92774462Salfred			new->flags |= F_MTFLAG;
92887096Salfred			new->mt_data = MNT_LOCAL;
92987096Salfred			return new;
93087096Salfred		}
93187096Salfred		break;
93287096Salfred	case 'r':
93387096Salfred		if (!strcmp(fsname, "rdonly")) {
93487096Salfred			new->flags |= F_MTFLAG;
93587096Salfred			new->mt_data = MNT_RDONLY;
93687096Salfred			return new;
93787096Salfred		}
93887096Salfred		break;
93987096Salfred	}
94087096Salfred
94187096Salfred	/*
94287096Salfred	 * We need to make filesystem checks for filesystems
94387096Salfred	 * that exists but aren't in the kernel work.
94487096Salfred	 */
94587096Salfred	fprintf(stderr, "Warning: Unknown filesystem type %s\n", fsname);
946132254Smr	new->flags |= F_MTUNKNOWN;
94787096Salfred	return new;
94887096Salfred}
94987096Salfred
95087096Salfred/*
95187096Salfred * -group gname functions --
95287199Salfred *
95387096Salfred *	True if the file belongs to the group gname.  If gname is numeric and
95487096Salfred *	an equivalent of the getgrnam() function does not return a valid group
955132254Smr *	name, gname is taken as a group ID.
95687096Salfred */
95774462Salfredint
95887096Salfredf_group(PLAN *plan, FTSENT *entry)
95987096Salfred{
960132254Smr	COMPARE(entry->fts_statp->st_gid, plan->g_data);
96187096Salfred}
96287096Salfred
96387096SalfredPLAN *
96487096Salfredc_group(OPTION *option, char ***argvp)
96587096Salfred{
96687096Salfred	char *gname;
96787096Salfred	PLAN *new;
96887096Salfred	struct group *g;
96987199Salfred	gid_t gid;
97087096Salfred
97187096Salfred	gname = nextarg(option, argvp);
972132254Smr	ftsoptions &= ~FTS_NOSTAT;
97387096Salfred
97474462Salfred	new = palloc(option);
97587096Salfred	g = getgrnam(gname);
97687096Salfred	if (g == NULL) {
97787096Salfred		char* cp = gname;
97887096Salfred		if (gname[0] == '-' || gname[0] == '+')
97987096Salfred			gname++;
98087096Salfred		gid = atoi(gname);
98187096Salfred		if (gid == 0 && gname[0] != '0')
98287096Salfred			errx(1, "%s: %s: no such group", option->name, gname);
98387096Salfred		gid = find_parsenum(new, option->name, cp, NULL);
98487096Salfred	} else
98587096Salfred		gid = g->gr_gid;
98687096Salfred
98787096Salfred	new->g_data = gid;
98887096Salfred	return new;
98987096Salfred}
99087096Salfred
99187096Salfred/*
99287096Salfred * -inum n functions --
99387096Salfred *
99487096Salfred *	True if the file has inode # n.
99587096Salfred */
99687096Salfredint
99787096Salfredf_inum(PLAN *plan, FTSENT *entry)
998132254Smr{
99987096Salfred	COMPARE(entry->fts_statp->st_ino, plan->i_data);
100087096Salfred}
100187096Salfred
100287096SalfredPLAN *
100387096Salfredc_inum(OPTION *option, char ***argvp)
100487096Salfred{
100587096Salfred	char *inum_str;
100687096Salfred	PLAN *new;
100787096Salfred
100887096Salfred	inum_str = nextarg(option, argvp);
100987096Salfred	ftsoptions &= ~FTS_NOSTAT;
101087096Salfred
101187096Salfred	new = palloc(option);
101287096Salfred	new->i_data = find_parsenum(new, option->name, inum_str, NULL);
101387096Salfred	return new;
101487096Salfred}
101587096Salfred
101687096Salfred/*
101787096Salfred * -samefile FN
101874462Salfred *
101987096Salfred *	True if the file has the same inode (eg hard link) FN
102087096Salfred */
102187096Salfred
102287096Salfred/* f_samefile is just f_inum */
102374462SalfredPLAN *
102487096Salfredc_samefile(OPTION *option, char ***argvp)
102587096Salfred{
102674462Salfred	char *fn;
102787096Salfred	PLAN *new;
102887096Salfred	struct stat sb;
102987096Salfred
103087096Salfred	fn = nextarg(option, argvp);
103187096Salfred	ftsoptions &= ~FTS_NOSTAT;
103287096Salfred
103387096Salfred	new = palloc(option);
103487096Salfred	if (stat(fn, &sb))
103587096Salfred		err(1, "%s", fn);
103687096Salfred	new->i_data = sb.st_ino;
103787096Salfred	return new;
103887096Salfred}
103987096Salfred
104087096Salfred/*
104187096Salfred * -links n functions --
104287096Salfred *
104374462Salfred *	True if the file has n links.
104487096Salfred */
104587096Salfredint
104687096Salfredf_links(PLAN *plan, FTSENT *entry)
104774462Salfred{
104874462Salfred	COMPARE(entry->fts_statp->st_nlink, plan->l_data);
104974462Salfred}
105087096Salfred
105187096SalfredPLAN *
105287096Salfredc_links(OPTION *option, char ***argvp)
105387096Salfred{
105487096Salfred	char *nlinks;
105587096Salfred	PLAN *new;
105674462Salfred
105787096Salfred	nlinks = nextarg(option, argvp);
105887096Salfred	ftsoptions &= ~FTS_NOSTAT;
105974462Salfred
106087096Salfred	new = palloc(option);
106187096Salfred	new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL);
106287096Salfred	return new;
106387096Salfred}
106487096Salfred
106574462Salfred/*
106687096Salfred * -ls functions --
106787096Salfred *
106887096Salfred *	Always true - prints the current entry to stdout in "ls" format.
106987096Salfred */
107087096Salfredint
107187096Salfredf_ls(PLAN *plan __unused, FTSENT *entry)
107287096Salfred{
107387096Salfred	printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
107487096Salfred	return 1;
107587096Salfred}
107687096Salfred
107787096SalfredPLAN *
107887096Salfredc_ls(OPTION *option, char ***argvp __unused)
107987096Salfred{
108087096Salfred	ftsoptions &= ~FTS_NOSTAT;
108187096Salfred	isoutput = 1;
108287096Salfred
108387096Salfred	return palloc(option);
108487096Salfred}
108587096Salfred
108687096Salfred/*
108787096Salfred * -name functions --
108887096Salfred *
108987096Salfred *	True if the basename of the filename being examined
109087096Salfred *	matches pattern using Pattern Matching Notation S3.14
109187096Salfred */
109287096Salfredint
109387096Salfredf_name(PLAN *plan, FTSENT *entry)
109487096Salfred{
109587096Salfred	char fn[PATH_MAX];
109687096Salfred	const char *name;
109787096Salfred
109887096Salfred	if (plan->flags & F_LINK) {
109987096Salfred		name = fn;
110087096Salfred		if (readlink(entry->fts_path, fn, sizeof(fn)) == -1)
110187096Salfred			return 0;
110287096Salfred	} else
110387096Salfred		name = entry->fts_name;
110487096Salfred	return !fnmatch(plan->c_data, name,
110587096Salfred	    plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0);
110687096Salfred}
110787096Salfred
110887096SalfredPLAN *
110987096Salfredc_name(OPTION *option, char ***argvp)
111087096Salfred{
111187096Salfred	char *pattern;
111287096Salfred	PLAN *new;
111387096Salfred
111487096Salfred	pattern = nextarg(option, argvp);
111587096Salfred	new = palloc(option);
111687096Salfred	new->c_data = pattern;
111787096Salfred	return new;
111887096Salfred}
111987096Salfred
112087096Salfred/*
112187096Salfred * -newer file functions --
112287096Salfred *
112387096Salfred *	True if the current file has been modified more recently
112487096Salfred *	then the modification time of the file named by the pathname
112587096Salfred *	file.
112674462Salfred */
112787096Salfredint
112887096Salfredf_newer(PLAN *plan, FTSENT *entry)
112987096Salfred{
113087096Salfred	if (plan->flags & F_TIME_C)
113187096Salfred		return entry->fts_statp->st_ctime > plan->t_data;
113287096Salfred	else if (plan->flags & F_TIME_A)
113387096Salfred		return entry->fts_statp->st_atime > plan->t_data;
113487096Salfred	else if (plan->flags & F_TIME_B)
113587096Salfred		return entry->fts_statp->st_birthtime > plan->t_data;
1136165776Smjacob	else
113787096Salfred		return entry->fts_statp->st_mtime > plan->t_data;
113887096Salfred}
113987096Salfred
114087096SalfredPLAN *
114187096Salfredc_newer(OPTION *option, char ***argvp)
114287096Salfred{
114387096Salfred	char *fn_or_tspec;
114487096Salfred	PLAN *new;
114587096Salfred	struct stat sb;
114687096Salfred
114787096Salfred	fn_or_tspec = nextarg(option, argvp);
114887096Salfred	ftsoptions &= ~FTS_NOSTAT;
114987096Salfred
115074462Salfred	new = palloc(option);
115174462Salfred	/* compare against what */
115274462Salfred	if (option->flags & F_TIME2_T) {
115387096Salfred		new->t_data = get_date(fn_or_tspec);
115487096Salfred		if (new->t_data == (time_t) -1)
115587096Salfred			errx(1, "Can't parse date/time: %s", fn_or_tspec);
115687096Salfred	} else {
115787096Salfred		if (stat(fn_or_tspec, &sb))
115887096Salfred			err(1, "%s", fn_or_tspec);
115987096Salfred		if (option->flags & F_TIME2_C)
116087096Salfred			new->t_data = sb.st_ctime;
116187096Salfred		else if (option->flags & F_TIME2_A)
116287096Salfred			new->t_data = sb.st_atime;
116387096Salfred		else if (option->flags & F_TIME2_B)
116487096Salfred			new->t_data = sb.st_birthtime;
116587096Salfred		else
116687096Salfred			new->t_data = sb.st_mtime;
116787096Salfred	}
116887096Salfred	return new;
116987096Salfred}
117087096Salfred
117187096Salfred/*
117287096Salfred * -nogroup functions --
117387096Salfred *
117487096Salfred *	True if file belongs to a user ID for which the equivalent
117587096Salfred *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
117674462Salfred */
117774462Salfredint
117887096Salfredf_nogroup(PLAN *plan __unused, FTSENT *entry)
117992911Salfred{
118092911Salfred	return group_from_gid(entry->fts_statp->st_gid, 1) == NULL;
118192911Salfred}
118287096Salfred
118387096SalfredPLAN *
118487096Salfredc_nogroup(OPTION *option, char ***argvp __unused)
118587096Salfred{
118687096Salfred	ftsoptions &= ~FTS_NOSTAT;
118787096Salfred
118887096Salfred	return palloc(option);
118987096Salfred}
119087096Salfred
119187096Salfred/*
119287096Salfred * -nouser functions --
119387096Salfred *
119487096Salfred *	True if file belongs to a user ID for which the equivalent
119587096Salfred *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
119687096Salfred */
119787096Salfredint
119887096Salfredf_nouser(PLAN *plan __unused, FTSENT *entry)
1199165775Smjacob{
1200165775Smjacob	return user_from_uid(entry->fts_statp->st_uid, 1) == NULL;
1201165775Smjacob}
1202216603Suqs
1203165775SmjacobPLAN *
1204165775Smjacobc_nouser(OPTION *option, char ***argvp __unused)
1205165775Smjacob{
1206165775Smjacob	ftsoptions &= ~FTS_NOSTAT;
1207165775Smjacob
1208165775Smjacob	return palloc(option);
1209166054Sbrueffer}
1210165775Smjacob
1211165775Smjacob/*
1212165775Smjacob * -path functions --
1213165775Smjacob *
1214165775Smjacob *	True if the path of the filename being examined
1215165775Smjacob *	matches pattern using Pattern Matching Notation S3.14
1216165775Smjacob */
1217165775Smjacobint
1218165775Smjacobf_path(PLAN *plan, FTSENT *entry)
1219165775Smjacob{
1220165775Smjacob	return !fnmatch(plan->c_data, entry->fts_path,
1221165775Smjacob	    plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0);
1222165775Smjacob}
1223165775Smjacob
1224165775Smjacob/* c_path is the same as c_name */
1225165775Smjacob
1226165775Smjacob/*
1227165775Smjacob * -perm functions --
1228165775Smjacob *
122974462Salfred *	The mode argument is used to represent file mode bits.  If it starts
123087096Salfred *	with a leading digit, it's treated as an octal mode, otherwise as a
123174462Salfred *	symbolic mode.
123287096Salfred */
123387096Salfredint
123487096Salfredf_perm(PLAN *plan, FTSENT *entry)
1235165775Smjacob{
1236165775Smjacob	mode_t mode;
1237165775Smjacob
1238165775Smjacob	mode = entry->fts_statp->st_mode &
1239165775Smjacob	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1240165775Smjacob	if (plan->flags & F_ATLEAST)
1241165775Smjacob		return (plan->m_data | mode) == mode;
1242165775Smjacob	else if (plan->flags & F_ANY)
1243165775Smjacob		return (mode & plan->m_data);
1244165775Smjacob	else
1245165775Smjacob		return mode == plan->m_data;
1246165775Smjacob	/* NOTREACHED */
124787096Salfred}
124887096Salfred
124987096SalfredPLAN *
125087096Salfredc_perm(OPTION *option, char ***argvp)
125187096Salfred{
125287096Salfred	char *perm;
125387096Salfred	PLAN *new;
1254165775Smjacob	mode_t *set;
125574462Salfred
125674462Salfred	perm = nextarg(option, argvp);
125774462Salfred	ftsoptions &= ~FTS_NOSTAT;
125887096Salfred
125974462Salfred	new = palloc(option);
126074462Salfred
126187096Salfred	if (*perm == '-') {
126287096Salfred		new->flags |= F_ATLEAST;
126387096Salfred		++perm;
126487096Salfred	} else if (*perm == '+') {
126587096Salfred		new->flags |= F_ANY;
126687096Salfred		++perm;
126787096Salfred	}
126887096Salfred
126987096Salfred	if ((set = setmode(perm)) == NULL)
127087096Salfred		errx(1, "%s: %s: illegal mode string", option->name, perm);
127187096Salfred
127287096Salfred	new->m_data = getmode(set, 0);
127387096Salfred	free(set);
127487096Salfred	return new;
127587096Salfred}
127687096Salfred
127787096Salfred/*
127887096Salfred * -print functions --
127987096Salfred *
128087096Salfred *	Always true, causes the current pathname to be written to
128187096Salfred *	standard output.
128287096Salfred */
128387096Salfredint
128487096Salfredf_print(PLAN *plan __unused, FTSENT *entry)
128587096Salfred{
128687096Salfred	(void)puts(entry->fts_path);
128787096Salfred	return 1;
128874462Salfred}
128987096Salfred
129087096SalfredPLAN *
129187096Salfredc_print(OPTION *option, char ***argvp __unused)
129287096Salfred{
129387096Salfred	isoutput = 1;
129487096Salfred
129587096Salfred	return palloc(option);
129687096Salfred}
129787096Salfred
1298132254Smr/*
129987096Salfred * -print0 functions --
130087096Salfred *
130187096Salfred *	Always true, causes the current pathname to be written to
130287096Salfred *	standard output followed by a NUL character
1303132254Smr */
130487096Salfredint
130587096Salfredf_print0(PLAN *plan __unused, FTSENT *entry)
130687096Salfred{
130787096Salfred	fputs(entry->fts_path, stdout);
130887096Salfred	fputc('\0', stdout);
130987096Salfred	return 1;
131087096Salfred}
131187096Salfred
131287096Salfred/* c_print0 is the same as c_print */
131387096Salfred
131487096Salfred/*
131587096Salfred * -prune functions --
1316165776Smjacob *
131787096Salfred *	Prune a portion of the hierarchy.
131887096Salfred */
131987096Salfredint
132087096Salfredf_prune(PLAN *plan __unused, FTSENT *entry)
132187096Salfred{
1322132254Smr	if (fts_set(tree, entry, FTS_SKIP))
132387096Salfred		err(1, "%s", entry->fts_path);
1324132254Smr	return 1;
132574462Salfred}
132687096Salfred
132787096Salfred/* c_prune == c_simple */
132887096Salfred
132987096Salfred/*
133087096Salfred * -regex functions --
133187096Salfred *
133287096Salfred *	True if the whole path of the file matches pattern using
133387096Salfred *	regular expression.
133487096Salfred */
133587096Salfredint
133687096Salfredf_regex(PLAN *plan, FTSENT *entry)
133787096Salfred{
133887096Salfred	char *str;
133987096Salfred	int len;
134087096Salfred	regex_t *pre;
134187096Salfred	regmatch_t pmatch;
134287096Salfred	int errcode;
134387096Salfred	char errbuf[LINE_MAX];
134487096Salfred	int matched;
134587096Salfred
134687096Salfred	pre = plan->re_data;
134787096Salfred	str = entry->fts_path;
134887096Salfred	len = strlen(str);
134987096Salfred	matched = 0;
135087096Salfred
135187096Salfred	pmatch.rm_so = 0;
135287096Salfred	pmatch.rm_eo = len;
135387096Salfred
135487096Salfred	errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND);
135587096Salfred
135687096Salfred	if (errcode != 0 && errcode != REG_NOMATCH) {
135787096Salfred		regerror(errcode, pre, errbuf, sizeof errbuf);
135887096Salfred		errx(1, "%s: %s",
135987096Salfred		     plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf);
136087096Salfred	}
136187096Salfred
136287096Salfred	if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len)
136387096Salfred		matched = 1;
136487096Salfred
136587096Salfred	return matched;
136687096Salfred}
136787096Salfred
136887096SalfredPLAN *
136987096Salfredc_regex(OPTION *option, char ***argvp)
137087096Salfred{
137187096Salfred	PLAN *new;
137287096Salfred	char *pattern;
137387096Salfred	regex_t *pre;
137487096Salfred	int errcode;
137587096Salfred	char errbuf[LINE_MAX];
137687096Salfred
137787096Salfred	if ((pre = malloc(sizeof(regex_t))) == NULL)
137887096Salfred		err(1, NULL);
137987096Salfred
138087096Salfred	pattern = nextarg(option, argvp);
138187096Salfred
138287096Salfred	if ((errcode = regcomp(pre, pattern,
138387096Salfred	    regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) {
138474462Salfred		regerror(errcode, pre, errbuf, sizeof errbuf);
138587096Salfred		errx(1, "%s: %s: %s",
138687096Salfred		     option->flags & F_IGNCASE ? "-iregex" : "-regex",
138774462Salfred		     pattern, errbuf);
138887096Salfred	}
138987096Salfred
139087096Salfred	new = palloc(option);
139187096Salfred	new->re_data = pre;
139287096Salfred
139387096Salfred	return new;
139487096Salfred}
139587096Salfred
139687096Salfred/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */
1397165776Smjacob
139887096SalfredPLAN *
139987096Salfredc_simple(OPTION *option, char ***argvp __unused)
140087096Salfred{
140187096Salfred	return palloc(option);
140287096Salfred}
140387096Salfred
140487096Salfred/*
140587096Salfred * -size n[c] functions --
140687096Salfred *
140787096Salfred *	True if the file size in bytes, divided by an implementation defined
140887096Salfred *	value and rounded up to the next integer, is n.  If n is followed by
140987096Salfred *      one of c k M G T P, the size is in bytes, kilobytes,
141087096Salfred *      megabytes, gigabytes, terabytes or petabytes respectively.
141187096Salfred */
141287096Salfred#define	FIND_SIZE	512
141387096Salfredstatic int divsize = 1;
141487096Salfred
141587096Salfredint
141687096Salfredf_size(PLAN *plan, FTSENT *entry)
141774462Salfred{
141887096Salfred	off_t size;
141987096Salfred
142087096Salfred	size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
142187096Salfred	    FIND_SIZE : entry->fts_statp->st_size;
142287096Salfred	COMPARE(size, plan->o_data);
142387096Salfred}
142487096Salfred
142587096SalfredPLAN *
142687096Salfredc_size(OPTION *option, char ***argvp)
142787096Salfred{
142887096Salfred	char *size_str;
142987096Salfred	PLAN *new;
143087096Salfred	char endch;
143187096Salfred	off_t scale;
143287096Salfred
143387096Salfred	size_str = nextarg(option, argvp);
143487096Salfred	ftsoptions &= ~FTS_NOSTAT;
143587096Salfred
143687096Salfred	new = palloc(option);
143787096Salfred	endch = 'c';
143887096Salfred	new->o_data = find_parsenum(new, option->name, size_str, &endch);
143987096Salfred	if (endch != '\0') {
144087096Salfred		divsize = 0;
144187096Salfred
144287096Salfred		switch (endch) {
144387096Salfred		case 'c':                       /* characters */
144487096Salfred			scale = 0x1LL;
144587096Salfred			break;
1446115004Srwatson		case 'k':                       /* kilobytes 1<<10 */
1447115004Srwatson			scale = 0x400LL;
1448115004Srwatson			break;
1449115004Srwatson		case 'M':                       /* megabytes 1<<20 */
1450115004Srwatson			scale = 0x100000LL;
1451115004Srwatson			break;
1452115004Srwatson		case 'G':                       /* gigabytes 1<<30 */
1453115004Srwatson			scale = 0x40000000LL;
1454115004Srwatson			break;
1455115004Srwatson		case 'T':                       /* terabytes 1<<40 */
1456115004Srwatson			scale = 0x1000000000LL;
145774462Salfred			break;
145887096Salfred		case 'P':                       /* petabytes 1<<50 */
145987096Salfred			scale = 0x4000000000000LL;
146087096Salfred			break;
146187096Salfred		default:
146287096Salfred			errx(1, "%s: %s: illegal trailing character",
146387096Salfred				option->name, size_str);
146487096Salfred			break;
146587096Salfred		}
146687096Salfred		if (new->o_data > QUAD_MAX / scale)
146787096Salfred			errx(1, "%s: %s: value too large",
146887096Salfred				option->name, size_str);
146987096Salfred		new->o_data *= scale;
147087096Salfred	}
147187096Salfred	return new;
147287096Salfred}
147387096Salfred
147487096Salfred/*
147587096Salfred * -type c functions --
147687096Salfred *
147787096Salfred *	True if the type of the file is c, where c is b, c, d, p, f or w
147887096Salfred *	for block special file, character special file, directory, FIFO,
147987096Salfred *	regular file or whiteout respectively.
148087096Salfred */
148187096Salfredint
148287096Salfredf_type(PLAN *plan, FTSENT *entry)
148387096Salfred{
148487096Salfred	return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data;
148587096Salfred}
148687096Salfred
148787096SalfredPLAN *
148887096Salfredc_type(OPTION *option, char ***argvp)
148987096Salfred{
149087096Salfred	char *typestring;
149187096Salfred	PLAN *new;
149287096Salfred	mode_t  mask;
149387096Salfred
149487096Salfred	typestring = nextarg(option, argvp);
149587096Salfred	ftsoptions &= ~FTS_NOSTAT;
149687096Salfred
149787096Salfred	switch (typestring[0]) {
149887096Salfred	case 'b':
149987096Salfred		mask = S_IFBLK;
150087096Salfred		break;
150187096Salfred	case 'c':
150287096Salfred		mask = S_IFCHR;
150387096Salfred		break;
150487096Salfred	case 'd':
150587096Salfred		mask = S_IFDIR;
150687096Salfred		break;
150787096Salfred	case 'f':
150887096Salfred		mask = S_IFREG;
150987096Salfred		break;
151087096Salfred	case 'l':
151187096Salfred		mask = S_IFLNK;
151287096Salfred		break;
151387096Salfred	case 'p':
151487096Salfred		mask = S_IFIFO;
151587096Salfred		break;
151687096Salfred	case 's':
151787096Salfred		mask = S_IFSOCK;
151887096Salfred		break;
151987096Salfred#ifdef FTS_WHITEOUT
152087096Salfred	case 'w':
152187096Salfred		mask = S_IFWHT;
152287096Salfred		ftsoptions |= FTS_WHITEOUT;
152387096Salfred		break;
152487096Salfred#endif /* FTS_WHITEOUT */
152587096Salfred	default:
152687096Salfred		errx(1, "%s: %s: unknown type", option->name, typestring);
152787096Salfred	}
152887096Salfred
152974462Salfred	new = palloc(option);
153087096Salfred	new->m_data = mask;
153187096Salfred	return new;
153287096Salfred}
153387096Salfred
153487096Salfred/*
153587096Salfred * -user uname functions --
153687096Salfred *
153787096Salfred *	True if the file belongs to the user uname.  If uname is numeric and
153887096Salfred *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
153987096Salfred *	return a valid user name, uname is taken as a user ID.
154087096Salfred */
154187096Salfredint
154287096Salfredf_user(PLAN *plan, FTSENT *entry)
154387096Salfred{
154487096Salfred	COMPARE(entry->fts_statp->st_uid, plan->u_data);
154587096Salfred}
154687096Salfred
154787096SalfredPLAN *
154887096Salfredc_user(OPTION *option, char ***argvp)
154987096Salfred{
1550165776Smjacob	char *username;
155187096Salfred	PLAN *new;
155287096Salfred	struct passwd *p;
155387096Salfred	uid_t uid;
155487096Salfred
155587096Salfred	username = nextarg(option, argvp);
155674462Salfred	ftsoptions &= ~FTS_NOSTAT;
155787096Salfred
155887096Salfred	new = palloc(option);
155987096Salfred	p = getpwnam(username);
156087096Salfred	if (p == NULL) {
156187096Salfred		char* cp = username;
156287096Salfred		if( username[0] == '-' || username[0] == '+' )
156374462Salfred			username++;
156474462Salfred		uid = atoi(username);
156587096Salfred		if (uid == 0 && username[0] != '0')
156687096Salfred			errx(1, "%s: %s: no such user", option->name, username);
156787096Salfred		uid = find_parsenum(new, option->name, cp, NULL);
156887096Salfred	} else
156987096Salfred		uid = p->pw_uid;
157074462Salfred
1571165776Smjacob	new->u_data = uid;
157287096Salfred	return new;
1573165776Smjacob}
157487096Salfred
157587096Salfred/*
157687096Salfred * -xdev functions --
157787096Salfred *
157887096Salfred *	Always true, causes find not to descend past directories that have a
157987096Salfred *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
158087096Salfred */
158187096SalfredPLAN *
158287096Salfredc_xdev(OPTION *option, char ***argvp __unused)
158387096Salfred{
158487096Salfred	ftsoptions |= FTS_XDEV;
158574462Salfred
158687096Salfred	return palloc(option);
158787096Salfred}
158887096Salfred
158987096Salfred/*
159087096Salfred * ( expression ) functions --
159187096Salfred *
159287096Salfred *	True if expression is true.
159387096Salfred */
159487096Salfredint
159587096Salfredf_expr(PLAN *plan, FTSENT *entry)
159687096Salfred{
159787096Salfred	PLAN *p;
159887096Salfred	int state = 0;
159987096Salfred
160087096Salfred	for (p = plan->p_data[0];
160187096Salfred	    p && (state = (p->execute)(p, entry)); p = p->next);
160287096Salfred	return state;
1603165776Smjacob}
160487096Salfred
160587096Salfred/*
160687096Salfred * f_openparen and f_closeparen nodes are temporary place markers.  They are
160787096Salfred * eliminated during phase 2 of find_formplan() --- the '(' node is converted
160887096Salfred * to a f_expr node containing the expression and the ')' node is discarded.
160987096Salfred * The functions themselves are only used as constants.
161087096Salfred */
161187096Salfred
161287096Salfredint
161387096Salfredf_openparen(PLAN *plan __unused, FTSENT *entry __unused)
161487096Salfred{
161587096Salfred	abort();
161687096Salfred}
161774462Salfred
161887096Salfredint
161987096Salfredf_closeparen(PLAN *plan __unused, FTSENT *entry __unused)
162087096Salfred{
162187096Salfred	abort();
162274462Salfred}
162374462Salfred
162474462Salfred/* c_openparen == c_simple */
162587096Salfred/* c_closeparen == c_simple */
162674462Salfred
162787096Salfred/*
162887096Salfred * AND operator. Since AND is implicit, no node is allocated.
162987096Salfred */
163087096SalfredPLAN *
163174462Salfredc_and(OPTION *option __unused, char ***argvp __unused)
163274462Salfred{
1633165776Smjacob	return NULL;
163487096Salfred}
163587096Salfred
163687096Salfred/*
163787096Salfred * ! expression functions --
163887096Salfred *
163987096Salfred *	Negation of a primary; the unary NOT operator.
164087096Salfred */
164187096Salfredint
164287096Salfredf_not(PLAN *plan, FTSENT *entry)
164387096Salfred{
164487096Salfred	PLAN *p;
164587096Salfred	int state = 0;
164687096Salfred
164787096Salfred	for (p = plan->p_data[0];
164887096Salfred	    p && (state = (p->execute)(p, entry)); p = p->next);
164987096Salfred	return !state;
165087096Salfred}
165187096Salfred
165287096Salfred/* c_not == c_simple */
165387096Salfred
165487096Salfred/*
165587096Salfred * expression -o expression functions --
165687096Salfred *
165787096Salfred *	Alternation of primaries; the OR operator.  The second expression is
165887096Salfred * not evaluated if the first expression is true.
165987096Salfred */
166087096Salfredint
166187096Salfredf_or(PLAN *plan, FTSENT *entry)
166287096Salfred{
166387096Salfred	PLAN *p;
166487096Salfred	int state = 0;
166587096Salfred
166687096Salfred	for (p = plan->p_data[0];
166787096Salfred	    p && (state = (p->execute)(p, entry)); p = p->next);
166887096Salfred
166987096Salfred	if (state)
167087096Salfred		return 1;
167187096Salfred
167287096Salfred	for (p = plan->p_data[1];
167387096Salfred	    p && (state = (p->execute)(p, entry)); p = p->next);
167487096Salfred	return state;
167587096Salfred}
167687096Salfred
167787096Salfred/* c_or == c_simple */
167887096Salfred
167987096Salfred/*
168087096Salfred * -false
168187096Salfred *
168287096Salfred *	Always false.
168387096Salfred */
168487096Salfredint
168587096Salfredf_false(PLAN *plan __unused, FTSENT *entry __unused)
168687096Salfred{
168787096Salfred	return 0;
168887096Salfred}
168987096Salfred
169087096Salfred/* c_false == c_simple */
169187096Salfred
169287096Salfred/*
169387096Salfred * -quit
169487096Salfred *
169587096Salfred *	Exits the program
169687096Salfred */
169787096Salfredint
169887096Salfredf_quit(PLAN *plan __unused, FTSENT *entry __unused)
169987096Salfred{
170087096Salfred	exit(0);
170187096Salfred}
170287096Salfred
170387096Salfred/* c_quit == c_simple */
170487096Salfred