Home | History | Annotate | Line # | Download | only in find
function.c revision 1.30
      1 /*	$NetBSD: function.c,v 1.30 1999/02/04 16:41:17 kleink Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1990, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Cimarron D. Taylor of the University of California, Berkeley.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the University of
     21  *	California, Berkeley and its contributors.
     22  * 4. Neither the name of the University nor the names of its contributors
     23  *    may be used to endorse or promote products derived from this software
     24  *    without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     36  * SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 #ifndef lint
     41 #if 0
     42 static char sccsid[] = "from: @(#)function.c	8.10 (Berkeley) 5/4/95";
     43 #else
     44 __RCSID("$NetBSD: function.c,v 1.30 1999/02/04 16:41:17 kleink Exp $");
     45 #endif
     46 #endif /* not lint */
     47 
     48 #include <sys/param.h>
     49 #include <sys/stat.h>
     50 #include <sys/wait.h>
     51 #include <sys/mount.h>
     52 
     53 #include <err.h>
     54 #include <errno.h>
     55 #include <fnmatch.h>
     56 #include <fts.h>
     57 #include <grp.h>
     58 #include <inttypes.h>
     59 #include <pwd.h>
     60 #include <stdio.h>
     61 #include <stdlib.h>
     62 #include <string.h>
     63 #include <tzfile.h>
     64 #include <unistd.h>
     65 
     66 #include "find.h"
     67 #include "stat_flags.h"
     68 
     69 #define	COMPARE(a, b) {							\
     70 	switch (plan->flags) {						\
     71 	case F_EQUAL:							\
     72 		return (a == b);					\
     73 	case F_LESSTHAN:						\
     74 		return (a < b);						\
     75 	case F_GREATER:							\
     76 		return (a > b);						\
     77 	default:							\
     78 		abort();						\
     79 	}								\
     80 }
     81 
     82 static	int64_t	find_parsenum __P((PLAN *, char *, char *, char *));
     83 	int	f_always_true __P((PLAN *, FTSENT *));
     84 	int	f_amin __P((PLAN *, FTSENT *));
     85 	int	f_atime __P((PLAN *, FTSENT *));
     86 	int	f_cmin __P((PLAN *, FTSENT *));
     87 	int	f_ctime __P((PLAN *, FTSENT *));
     88 	int	f_exec __P((PLAN *, FTSENT *));
     89 	int	f_flags __P((PLAN *, FTSENT *));
     90 	int	f_fstype __P((PLAN *, FTSENT *));
     91 	int	f_group __P((PLAN *, FTSENT *));
     92 	int	f_inum __P((PLAN *, FTSENT *));
     93 	int	f_links __P((PLAN *, FTSENT *));
     94 	int	f_ls __P((PLAN *, FTSENT *));
     95 	int	f_mmin __P((PLAN *, FTSENT *));
     96 	int	f_mtime __P((PLAN *, FTSENT *));
     97 	int	f_name __P((PLAN *, FTSENT *));
     98 	int	f_newer __P((PLAN *, FTSENT *));
     99 	int	f_nogroup __P((PLAN *, FTSENT *));
    100 	int	f_nouser __P((PLAN *, FTSENT *));
    101 	int	f_path __P((PLAN *, FTSENT *));
    102 	int	f_perm __P((PLAN *, FTSENT *));
    103 	int	f_print __P((PLAN *, FTSENT *));
    104 	int	f_print0 __P((PLAN *, FTSENT *));
    105 	int	f_printx __P((PLAN *, FTSENT *));
    106 	int	f_prune __P((PLAN *, FTSENT *));
    107 	int	f_size __P((PLAN *, FTSENT *));
    108 	int	f_type __P((PLAN *, FTSENT *));
    109 	int	f_user __P((PLAN *, FTSENT *));
    110 	int	f_not __P((PLAN *, FTSENT *));
    111 	int	f_or __P((PLAN *, FTSENT *));
    112 static	PLAN   *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *))));
    113 
    114 /*
    115  * find_parsenum --
    116  *	Parse a string of the form [+-]# and return the value.
    117  */
    118 static int64_t
    119 find_parsenum(plan, option, vp, endch)
    120 	PLAN *plan;
    121 	char *option, *vp, *endch;
    122 {
    123 	int64_t value;
    124 	char *endchar, *str;	/* Pointer to character ending conversion. */
    125 
    126 	/* Determine comparison from leading + or -. */
    127 	str = vp;
    128 	switch (*str) {
    129 	case '+':
    130 		++str;
    131 		plan->flags = F_GREATER;
    132 		break;
    133 	case '-':
    134 		++str;
    135 		plan->flags = F_LESSTHAN;
    136 		break;
    137 	default:
    138 		plan->flags = F_EQUAL;
    139 		break;
    140 	}
    141 
    142 	/*
    143 	 * Convert the string with strtol().  Note, if strtol() returns zero
    144 	 * and endchar points to the beginning of the string we know we have
    145 	 * a syntax error.
    146 	 */
    147 	value = strtoq(str, &endchar, 10);
    148 	if (value == 0 && endchar == str)
    149 		errx(1, "%s: %s: illegal numeric value", option, vp);
    150 	if (endchar[0] && (endch == NULL || endchar[0] != *endch))
    151 		errx(1, "%s: %s: illegal trailing character", option, vp);
    152 	if (endch)
    153 		*endch = endchar[0];
    154 	return (value);
    155 }
    156 
    157 /*
    158  * The value of n for the inode times (atime, ctime, and mtime) is a range,
    159  * i.e. n matches from (n - 1) to n 24 hour periods.  This interacts with
    160  * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
    161  * user wanted.  Correct so that -1 is "less than 1".
    162  */
    163 #define	TIME_CORRECT(p, ttype)						\
    164 	if ((p)->type == ttype && (p)->flags == F_LESSTHAN)		\
    165 		++((p)->t_data);
    166 
    167 /*
    168  * -amin n functions --
    169  *
    170  *	True if the difference between the file access time and the
    171  *	current time is n 1 minute periods.
    172  */
    173 int
    174 f_amin(plan, entry)
    175 	PLAN *plan;
    176 	FTSENT *entry;
    177 {
    178 	extern time_t now;
    179 
    180 	COMPARE((now - entry->fts_statp->st_atime +
    181 	    SECSPERMIN - 1) / SECSPERMIN, plan->t_data);
    182 }
    183 
    184 PLAN *
    185 c_amin(argvp, isok)
    186 	char ***argvp;
    187 	int isok;
    188 {
    189 	char *arg = **argvp;
    190 	PLAN *new;
    191 
    192 	(*argvp)++;
    193 	ftsoptions &= ~FTS_NOSTAT;
    194 
    195 	new = palloc(N_AMIN, f_amin);
    196 	new->t_data = find_parsenum(new, "-amin", arg, NULL);
    197 	TIME_CORRECT(new, N_AMIN);
    198 	return (new);
    199 }
    200 /*
    201  * -atime n functions --
    202  *
    203  *	True if the difference between the file access time and the
    204  *	current time is n 24 hour periods.
    205  */
    206 int
    207 f_atime(plan, entry)
    208 	PLAN *plan;
    209 	FTSENT *entry;
    210 {
    211 	extern time_t now;
    212 
    213 	COMPARE((now - entry->fts_statp->st_atime +
    214 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
    215 }
    216 
    217 PLAN *
    218 c_atime(argvp, isok)
    219 	char ***argvp;
    220 	int isok;
    221 {
    222 	char *arg = **argvp;
    223 	PLAN *new;
    224 
    225 	(*argvp)++;
    226 	ftsoptions &= ~FTS_NOSTAT;
    227 
    228 	new = palloc(N_ATIME, f_atime);
    229 	new->t_data = find_parsenum(new, "-atime", arg, NULL);
    230 	TIME_CORRECT(new, N_ATIME);
    231 	return (new);
    232 }
    233 /*
    234  * -cmin n functions --
    235  *
    236  *	True if the difference between the last change of file
    237  *	status information and the current time is n 24 hour periods.
    238  */
    239 int
    240 f_cmin(plan, entry)
    241 	PLAN *plan;
    242 	FTSENT *entry;
    243 {
    244 	extern time_t now;
    245 
    246 	COMPARE((now - entry->fts_statp->st_ctime +
    247 	    SECSPERMIN - 1) / SECSPERMIN, plan->t_data);
    248 }
    249 
    250 PLAN *
    251 c_cmin(argvp, isok)
    252 	char ***argvp;
    253 	int isok;
    254 {
    255 	char *arg = **argvp;
    256 	PLAN *new;
    257 
    258 	(*argvp)++;
    259 	ftsoptions &= ~FTS_NOSTAT;
    260 
    261 	new = palloc(N_CMIN, f_cmin);
    262 	new->t_data = find_parsenum(new, "-cmin", arg, NULL);
    263 	TIME_CORRECT(new, N_CMIN);
    264 	return (new);
    265 }
    266 /*
    267  * -ctime n functions --
    268  *
    269  *	True if the difference between the last change of file
    270  *	status information and the current time is n 24 hour periods.
    271  */
    272 int
    273 f_ctime(plan, entry)
    274 	PLAN *plan;
    275 	FTSENT *entry;
    276 {
    277 	extern time_t now;
    278 
    279 	COMPARE((now - entry->fts_statp->st_ctime +
    280 	    SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
    281 }
    282 
    283 PLAN *
    284 c_ctime(argvp, isok)
    285 	char ***argvp;
    286 	int isok;
    287 {
    288 	char *arg = **argvp;
    289 	PLAN *new;
    290 
    291 	(*argvp)++;
    292 	ftsoptions &= ~FTS_NOSTAT;
    293 
    294 	new = palloc(N_CTIME, f_ctime);
    295 	new->t_data = find_parsenum(new, "-ctime", arg, NULL);
    296 	TIME_CORRECT(new, N_CTIME);
    297 	return (new);
    298 }
    299 
    300 /*
    301  * -depth functions --
    302  *
    303  *	Always true, causes descent of the directory hierarchy to be done
    304  *	so that all entries in a directory are acted on before the directory
    305  *	itself.
    306  */
    307 int
    308 f_always_true(plan, entry)
    309 	PLAN *plan;
    310 	FTSENT *entry;
    311 {
    312 	return (1);
    313 }
    314 
    315 PLAN *
    316 c_depth(argvp, isok)
    317 	char ***argvp;
    318 	int isok;
    319 {
    320 	isdepth = 1;
    321 
    322 	return (palloc(N_DEPTH, f_always_true));
    323 }
    324 
    325 /*
    326  * [-exec | -ok] utility [arg ... ] ; functions --
    327  *
    328  *	True if the executed utility returns a zero value as exit status.
    329  *	The end of the primary expression is delimited by a semicolon.  If
    330  *	"{}" occurs anywhere, it gets replaced by the current pathname.
    331  *	The current directory for the execution of utility is the same as
    332  *	the current directory when the find utility was started.
    333  *
    334  *	The primary -ok is different in that it requests affirmation of the
    335  *	user before executing the utility.
    336  */
    337 int
    338 f_exec(plan, entry)
    339 	PLAN *plan;
    340 	FTSENT *entry;
    341 {
    342 	extern int dotfd;
    343 	int cnt;
    344 	pid_t pid;
    345 	int status;
    346 
    347 	for (cnt = 0; plan->e_argv[cnt]; ++cnt)
    348 		if (plan->e_len[cnt])
    349 			brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
    350 			    entry->fts_path, plan->e_len[cnt]);
    351 
    352 	if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
    353 		return (0);
    354 
    355 	/* don't mix output of command with find output */
    356 	fflush(stdout);
    357 	fflush(stderr);
    358 
    359 	switch (pid = vfork()) {
    360 	case -1:
    361 		err(1, "fork");
    362 		/* NOTREACHED */
    363 	case 0:
    364 		if (fchdir(dotfd)) {
    365 			warn("chdir");
    366 			_exit(1);
    367 		}
    368 		execvp(plan->e_argv[0], plan->e_argv);
    369 		warn("%s", plan->e_argv[0]);
    370 		_exit(1);
    371 	}
    372 	pid = waitpid(pid, &status, 0);
    373 	return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
    374 }
    375 
    376 /*
    377  * c_exec --
    378  *	build three parallel arrays, one with pointers to the strings passed
    379  *	on the command line, one with (possibly duplicated) pointers to the
    380  *	argv array, and one with integer values that are lengths of the
    381  *	strings, but also flags meaning that the string has to be massaged.
    382  */
    383 PLAN *
    384 c_exec(argvp, isok)
    385 	char ***argvp;
    386 	int isok;
    387 {
    388 	PLAN *new;			/* node returned */
    389 	int cnt;
    390 	char **argv, **ap, *p;
    391 
    392 	isoutput = 1;
    393 
    394 	new = palloc(N_EXEC, f_exec);
    395 	if (isok)
    396 		new->flags = F_NEEDOK;
    397 
    398 	for (ap = argv = *argvp;; ++ap) {
    399 		if (!*ap)
    400 			errx(1,
    401 			    "%s: no terminating \";\"", isok ? "-ok" : "-exec");
    402 		if (**ap == ';')
    403 			break;
    404 	}
    405 
    406 	cnt = ap - *argvp + 1;
    407 	new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
    408 	new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
    409 	new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
    410 
    411 	for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
    412 		new->e_orig[cnt] = *argv;
    413 		for (p = *argv; *p; ++p)
    414 			if (p[0] == '{' && p[1] == '}') {
    415 				new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
    416 				new->e_len[cnt] = MAXPATHLEN;
    417 				break;
    418 			}
    419 		if (!*p) {
    420 			new->e_argv[cnt] = *argv;
    421 			new->e_len[cnt] = 0;
    422 		}
    423 	}
    424 	new->e_argv[cnt] = new->e_orig[cnt] = NULL;
    425 
    426 	*argvp = argv + 1;
    427 	return (new);
    428 }
    429 
    430 /*
    431  * -flags [-]flags functions --
    432  */
    433 int
    434 f_flags(plan, entry)
    435 	PLAN *plan;
    436 	FTSENT *entry;
    437 {
    438 	u_int32_t flags;
    439 
    440 	flags = entry->fts_statp->st_flags;
    441 	if (plan->flags == F_ATLEAST)
    442 		return ((plan->f_data | flags) == flags);
    443 	else
    444 		return (flags == plan->f_data);
    445 	/* NOTREACHED */
    446 }
    447 
    448 PLAN *
    449 c_flags(argvp, isok)
    450 	char ***argvp;
    451 	int isok;
    452 {
    453 	char *flags = **argvp;
    454 	PLAN *new;
    455 	u_long flagset;
    456 
    457 	(*argvp)++;
    458 	ftsoptions &= ~FTS_NOSTAT;
    459 
    460 	new = palloc(N_FLAGS, f_flags);
    461 
    462 	if (*flags == '-') {
    463 		new->flags = F_ATLEAST;
    464 		++flags;
    465 	}
    466 
    467 	flagset = 0;
    468 	if ((strcmp(flags, "none") != 0) &&
    469 	    (string_to_flags(&flags, &flagset, NULL) != 0))
    470 		errx(1, "-flags: %s: illegal flags string", flags);
    471 	new->f_data = flagset;
    472 	return (new);
    473 }
    474 
    475 
    476 /*
    477  * -follow functions --
    478  *
    479  *	Always true, causes symbolic links to be followed on a global
    480  *	basis.
    481  */
    482 PLAN *
    483 c_follow(argvp, isok)
    484 	char ***argvp;
    485 	int isok;
    486 {
    487 	ftsoptions &= ~FTS_PHYSICAL;
    488 	ftsoptions |= FTS_LOGICAL;
    489 
    490 	return (palloc(N_FOLLOW, f_always_true));
    491 }
    492 
    493 /*
    494  * -fstype functions --
    495  *
    496  *	True if the file is of a certain type.
    497  */
    498 int
    499 f_fstype(plan, entry)
    500 	PLAN *plan;
    501 	FTSENT *entry;
    502 {
    503 	static dev_t curdev;	/* need a guaranteed illegal dev value */
    504 	static int first = 1;
    505 	struct statfs sb;
    506 	static short val;
    507 	static char fstype[MFSNAMELEN];
    508 	char *p, save[2];
    509 
    510 	/* Only check when we cross mount point. */
    511 	if (first || curdev != entry->fts_statp->st_dev) {
    512 		curdev = entry->fts_statp->st_dev;
    513 
    514 		/*
    515 		 * Statfs follows symlinks; find wants the link's file system,
    516 		 * not where it points.
    517 		 */
    518 		if (entry->fts_info == FTS_SL ||
    519 		    entry->fts_info == FTS_SLNONE) {
    520 			if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
    521 				++p;
    522 			else
    523 				p = entry->fts_accpath;
    524 			save[0] = p[0];
    525 			p[0] = '.';
    526 			save[1] = p[1];
    527 			p[1] = '\0';
    528 
    529 		} else
    530 			p = NULL;
    531 
    532 		if (statfs(entry->fts_accpath, &sb))
    533 			err(1, "%s", entry->fts_accpath);
    534 
    535 		if (p) {
    536 			p[0] = save[0];
    537 			p[1] = save[1];
    538 		}
    539 
    540 		first = 0;
    541 
    542 		/*
    543 		 * Further tests may need both of these values, so
    544 		 * always copy both of them.
    545 		 */
    546 		val = sb.f_flags;
    547 		strncpy(fstype, sb.f_fstypename, MFSNAMELEN);
    548 	}
    549 	switch (plan->flags) {
    550 	case F_MTFLAG:
    551 		return (val & plan->mt_data);
    552 	case F_MTTYPE:
    553 		return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
    554 	default:
    555 		abort();
    556 	}
    557 }
    558 
    559 PLAN *
    560 c_fstype(argvp, isok)
    561 	char ***argvp;
    562 	int isok;
    563 {
    564 	char *arg = **argvp;
    565 	PLAN *new;
    566 
    567 	(*argvp)++;
    568 	ftsoptions &= ~FTS_NOSTAT;
    569 
    570 	new = palloc(N_FSTYPE, f_fstype);
    571 
    572 	switch (*arg) {
    573 	case 'l':
    574 		if (!strcmp(arg, "local")) {
    575 			new->flags = F_MTFLAG;
    576 			new->mt_data = MNT_LOCAL;
    577 			return (new);
    578 		}
    579 		break;
    580 	case 'r':
    581 		if (!strcmp(arg, "rdonly")) {
    582 			new->flags = F_MTFLAG;
    583 			new->mt_data = MNT_RDONLY;
    584 			return (new);
    585 		}
    586 		break;
    587 	}
    588 
    589 	new->flags = F_MTTYPE;
    590 	new->c_data = arg;
    591 	return (new);
    592 }
    593 
    594 /*
    595  * -group gname functions --
    596  *
    597  *	True if the file belongs to the group gname.  If gname is numeric and
    598  *	an equivalent of the getgrnam() function does not return a valid group
    599  *	name, gname is taken as a group ID.
    600  */
    601 int
    602 f_group(plan, entry)
    603 	PLAN *plan;
    604 	FTSENT *entry;
    605 {
    606 	return (entry->fts_statp->st_gid == plan->g_data);
    607 }
    608 
    609 PLAN *
    610 c_group(argvp, isok)
    611 	char ***argvp;
    612 	int isok;
    613 {
    614 	char *gname = **argvp;
    615 	PLAN *new;
    616 	struct group *g;
    617 	gid_t gid;
    618 
    619 	(*argvp)++;
    620 	ftsoptions &= ~FTS_NOSTAT;
    621 
    622 	g = getgrnam(gname);
    623 	if (g == NULL) {
    624 		gid = atoi(gname);
    625 		if (gid == 0 && gname[0] != '0')
    626 			errx(1, "-group: %s: no such group", gname);
    627 	} else
    628 		gid = g->gr_gid;
    629 
    630 	new = palloc(N_GROUP, f_group);
    631 	new->g_data = gid;
    632 	return (new);
    633 }
    634 
    635 /*
    636  * -inum n functions --
    637  *
    638  *	True if the file has inode # n.
    639  */
    640 int
    641 f_inum(plan, entry)
    642 	PLAN *plan;
    643 	FTSENT *entry;
    644 {
    645 	COMPARE(entry->fts_statp->st_ino, plan->i_data);
    646 }
    647 
    648 PLAN *
    649 c_inum(argvp, isok)
    650 	char ***argvp;
    651 	int isok;
    652 {
    653 	char *arg = **argvp;
    654 	PLAN *new;
    655 
    656 	(*argvp)++;
    657 	ftsoptions &= ~FTS_NOSTAT;
    658 
    659 	new = palloc(N_INUM, f_inum);
    660 	new->i_data = find_parsenum(new, "-inum", arg, NULL);
    661 	return (new);
    662 }
    663 
    664 /*
    665  * -links n functions --
    666  *
    667  *	True if the file has n links.
    668  */
    669 int
    670 f_links(plan, entry)
    671 	PLAN *plan;
    672 	FTSENT *entry;
    673 {
    674 	COMPARE(entry->fts_statp->st_nlink, plan->l_data);
    675 }
    676 
    677 PLAN *
    678 c_links(argvp, isok)
    679 	char ***argvp;
    680 	int isok;
    681 {
    682 	char *arg = **argvp;
    683 	PLAN *new;
    684 
    685 	(*argvp)++;
    686 	ftsoptions &= ~FTS_NOSTAT;
    687 
    688 	new = palloc(N_LINKS, f_links);
    689 	new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
    690 	return (new);
    691 }
    692 
    693 /*
    694  * -ls functions --
    695  *
    696  *	Always true - prints the current entry to stdout in "ls" format.
    697  */
    698 int
    699 f_ls(plan, entry)
    700 	PLAN *plan;
    701 	FTSENT *entry;
    702 {
    703 	printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
    704 	return (1);
    705 }
    706 
    707 PLAN *
    708 c_ls(argvp, isok)
    709 	char ***argvp;
    710 	int isok;
    711 {
    712 	ftsoptions &= ~FTS_NOSTAT;
    713 	isoutput = 1;
    714 
    715 	return (palloc(N_LS, f_ls));
    716 }
    717 
    718 /*
    719  * -mmin n functions --
    720  *
    721  *	True if the difference between the file modification time and the
    722  *	current time is n 24 hour periods.
    723  */
    724 int
    725 f_mmin(plan, entry)
    726 	PLAN *plan;
    727 	FTSENT *entry;
    728 {
    729 	extern time_t now;
    730 
    731 	COMPARE((now - entry->fts_statp->st_mtime + SECSPERMIN - 1) /
    732 	    SECSPERMIN, plan->t_data);
    733 }
    734 
    735 PLAN *
    736 c_mmin(argvp, isok)
    737 	char ***argvp;
    738 	int isok;
    739 {
    740 	char *arg = **argvp;
    741 	PLAN *new;
    742 
    743 	(*argvp)++;
    744 	ftsoptions &= ~FTS_NOSTAT;
    745 
    746 	new = palloc(N_MMIN, f_mmin);
    747 	new->t_data = find_parsenum(new, "-mmin", arg, NULL);
    748 	TIME_CORRECT(new, N_MMIN);
    749 	return (new);
    750 }
    751 /*
    752  * -mtime n functions --
    753  *
    754  *	True if the difference between the file modification time and the
    755  *	current time is n 24 hour periods.
    756  */
    757 int
    758 f_mtime(plan, entry)
    759 	PLAN *plan;
    760 	FTSENT *entry;
    761 {
    762 	extern time_t now;
    763 
    764 	COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
    765 	    SECSPERDAY, plan->t_data);
    766 }
    767 
    768 PLAN *
    769 c_mtime(argvp, isok)
    770 	char ***argvp;
    771 	int isok;
    772 {
    773 	char *arg = **argvp;
    774 	PLAN *new;
    775 
    776 	(*argvp)++;
    777 	ftsoptions &= ~FTS_NOSTAT;
    778 
    779 	new = palloc(N_MTIME, f_mtime);
    780 	new->t_data = find_parsenum(new, "-mtime", arg, NULL);
    781 	TIME_CORRECT(new, N_MTIME);
    782 	return (new);
    783 }
    784 
    785 /*
    786  * -name functions --
    787  *
    788  *	True if the basename of the filename being examined
    789  *	matches pattern using Pattern Matching Notation S3.14
    790  */
    791 int
    792 f_name(plan, entry)
    793 	PLAN *plan;
    794 	FTSENT *entry;
    795 {
    796 	return (!fnmatch(plan->c_data, entry->fts_name, 0));
    797 }
    798 
    799 PLAN *
    800 c_name(argvp, isok)
    801 	char ***argvp;
    802 	int isok;
    803 {
    804 	char *pattern = **argvp;
    805 	PLAN *new;
    806 
    807 	(*argvp)++;
    808 	new = palloc(N_NAME, f_name);
    809 	new->c_data = pattern;
    810 	return (new);
    811 }
    812 
    813 /*
    814  * -newer file functions --
    815  *
    816  *	True if the current file has been modified more recently
    817  *	then the modification time of the file named by the pathname
    818  *	file.
    819  */
    820 int
    821 f_newer(plan, entry)
    822 	PLAN *plan;
    823 	FTSENT *entry;
    824 {
    825 	return (entry->fts_statp->st_mtime > plan->t_data);
    826 }
    827 
    828 PLAN *
    829 c_newer(argvp, isok)
    830 	char ***argvp;
    831 	int isok;
    832 {
    833 	char *filename = **argvp;
    834 	PLAN *new;
    835 	struct stat sb;
    836 
    837 	(*argvp)++;
    838 	ftsoptions &= ~FTS_NOSTAT;
    839 
    840 	if (stat(filename, &sb))
    841 		err(1, "%s", filename);
    842 	new = palloc(N_NEWER, f_newer);
    843 	new->t_data = sb.st_mtime;
    844 	return (new);
    845 }
    846 
    847 /*
    848  * -nogroup functions --
    849  *
    850  *	True if file belongs to a user ID for which the equivalent
    851  *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
    852  */
    853 int
    854 f_nogroup(plan, entry)
    855 	PLAN *plan;
    856 	FTSENT *entry;
    857 {
    858 
    859 	return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
    860 }
    861 
    862 PLAN *
    863 c_nogroup(argvp, isok)
    864 	char ***argvp;
    865 	int isok;
    866 {
    867 	ftsoptions &= ~FTS_NOSTAT;
    868 
    869 	return (palloc(N_NOGROUP, f_nogroup));
    870 }
    871 
    872 /*
    873  * -nouser functions --
    874  *
    875  *	True if file belongs to a user ID for which the equivalent
    876  *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
    877  */
    878 int
    879 f_nouser(plan, entry)
    880 	PLAN *plan;
    881 	FTSENT *entry;
    882 {
    883 
    884 	return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
    885 }
    886 
    887 PLAN *
    888 c_nouser(argvp, isok)
    889 	char ***argvp;
    890 	int isok;
    891 {
    892 	ftsoptions &= ~FTS_NOSTAT;
    893 
    894 	return (palloc(N_NOUSER, f_nouser));
    895 }
    896 
    897 /*
    898  * -path functions --
    899  *
    900  *	True if the path of the filename being examined
    901  *	matches pattern using Pattern Matching Notation S3.14
    902  */
    903 int
    904 f_path(plan, entry)
    905 	PLAN *plan;
    906 	FTSENT *entry;
    907 {
    908 	return (!fnmatch(plan->c_data, entry->fts_path, 0));
    909 }
    910 
    911 PLAN *
    912 c_path(argvp, isok)
    913 	char ***argvp;
    914 	int isok;
    915 {
    916 	char *pattern = **argvp;
    917 	PLAN *new;
    918 
    919 	(*argvp)++;
    920 	new = palloc(N_NAME, f_path);
    921 	new->c_data = pattern;
    922 	return (new);
    923 }
    924 
    925 /*
    926  * -perm functions --
    927  *
    928  *	The mode argument is used to represent file mode bits.  If it starts
    929  *	with a leading digit, it's treated as an octal mode, otherwise as a
    930  *	symbolic mode.
    931  */
    932 int
    933 f_perm(plan, entry)
    934 	PLAN *plan;
    935 	FTSENT *entry;
    936 {
    937 	mode_t mode;
    938 
    939 	mode = entry->fts_statp->st_mode &
    940 	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
    941 	if (plan->flags == F_ATLEAST)
    942 		return ((plan->m_data | mode) == mode);
    943 	else
    944 		return (mode == plan->m_data);
    945 	/* NOTREACHED */
    946 }
    947 
    948 PLAN *
    949 c_perm(argvp, isok)
    950 	char ***argvp;
    951 	int isok;
    952 {
    953 	char *perm = **argvp;
    954 	PLAN *new;
    955 	mode_t *set;
    956 
    957 	(*argvp)++;
    958 	ftsoptions &= ~FTS_NOSTAT;
    959 
    960 	new = palloc(N_PERM, f_perm);
    961 
    962 	if (*perm == '-') {
    963 		new->flags = F_ATLEAST;
    964 		++perm;
    965 	}
    966 
    967 	if ((set = setmode(perm)) == NULL)
    968 		err(1, "-perm: %s: illegal mode string", perm);
    969 
    970 	new->m_data = getmode(set, 0);
    971 	return (new);
    972 }
    973 
    974 /*
    975  * -print functions --
    976  *
    977  *	Always true, causes the current pathame to be written to
    978  *	standard output.
    979  */
    980 int
    981 f_print(plan, entry)
    982 	PLAN *plan;
    983 	FTSENT *entry;
    984 {
    985 	(void)printf("%s\n", entry->fts_path);
    986 	return (1);
    987 }
    988 
    989 int
    990 f_print0(plan, entry)
    991 	PLAN *plan;
    992 	FTSENT *entry;
    993 {
    994 	(void)fputs(entry->fts_path, stdout);
    995 	(void)fputc('\0', stdout);
    996 	return (1);
    997 }
    998 
    999 int
   1000 f_printx(plan, entry)
   1001 	PLAN *plan;
   1002 	FTSENT *entry;
   1003 {
   1004 	char *cp;
   1005 
   1006 	for (cp = entry->fts_path; *cp; cp++) {
   1007 		if (*cp == '\'' || *cp == '\"' || *cp == ' ' ||
   1008 		    *cp == '\t' || *cp == '\n' || *cp == '\\')
   1009 			fputc('\\', stdout);
   1010 
   1011 		fputc(*cp, stdout);
   1012 	}
   1013 
   1014 	fputc('\n', stdout);
   1015 	return 1;
   1016 }
   1017 
   1018 PLAN *
   1019 c_print(argvp, isok)
   1020 	char ***argvp;
   1021 	int isok;
   1022 {
   1023 	isoutput = 1;
   1024 
   1025 	return (palloc(N_PRINT, f_print));
   1026 }
   1027 
   1028 PLAN *
   1029 c_print0(argvp, isok)
   1030 	char ***argvp;
   1031 	int isok;
   1032 {
   1033 	isoutput = 1;
   1034 
   1035 	return (palloc(N_PRINT0, f_print0));
   1036 }
   1037 
   1038 PLAN *
   1039 c_printx(argvp, isok)
   1040 	char ***argvp;
   1041 	int isok;
   1042 {
   1043 	isoutput = 1;
   1044 
   1045 	return palloc(N_PRINTX, f_printx);
   1046 }
   1047 
   1048 /*
   1049  * -prune functions --
   1050  *
   1051  *	Prune a portion of the hierarchy.
   1052  */
   1053 int
   1054 f_prune(plan, entry)
   1055 	PLAN *plan;
   1056 	FTSENT *entry;
   1057 {
   1058 	extern FTS *tree;
   1059 
   1060 	if (fts_set(tree, entry, FTS_SKIP))
   1061 		err(1, "%s", entry->fts_path);
   1062 	return (1);
   1063 }
   1064 
   1065 PLAN *
   1066 c_prune(argvp, isok)
   1067 	char ***argvp;
   1068 	int isok;
   1069 {
   1070 	return (palloc(N_PRUNE, f_prune));
   1071 }
   1072 
   1073 /*
   1074  * -size n[c] functions --
   1075  *
   1076  *	True if the file size in bytes, divided by an implementation defined
   1077  *	value and rounded up to the next integer, is n.  If n is followed by
   1078  *	a c, the size is in bytes.
   1079  */
   1080 #define	FIND_SIZE	512
   1081 static int divsize = 1;
   1082 
   1083 int
   1084 f_size(plan, entry)
   1085 	PLAN *plan;
   1086 	FTSENT *entry;
   1087 {
   1088 	off_t size;
   1089 
   1090 	size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
   1091 	    FIND_SIZE : entry->fts_statp->st_size;
   1092 	COMPARE(size, plan->o_data);
   1093 }
   1094 
   1095 PLAN *
   1096 c_size(argvp, isok)
   1097 	char ***argvp;
   1098 	int isok;
   1099 {
   1100 	char *arg = **argvp;
   1101 	PLAN *new;
   1102 	char endch;
   1103 
   1104 	(*argvp)++;
   1105 	ftsoptions &= ~FTS_NOSTAT;
   1106 
   1107 	new = palloc(N_SIZE, f_size);
   1108 	endch = 'c';
   1109 	new->o_data = find_parsenum(new, "-size", arg, &endch);
   1110 	if (endch == 'c')
   1111 		divsize = 0;
   1112 	return (new);
   1113 }
   1114 
   1115 /*
   1116  * -type c functions --
   1117  *
   1118  *	True if the type of the file is c, where c is b, c, d, p, f or w
   1119  *	for block special file, character special file, directory, FIFO,
   1120  *	regular file or whiteout respectively.
   1121  */
   1122 int
   1123 f_type(plan, entry)
   1124 	PLAN *plan;
   1125 	FTSENT *entry;
   1126 {
   1127 	return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
   1128 }
   1129 
   1130 PLAN *
   1131 c_type(argvp, isok)
   1132 	char ***argvp;
   1133 	int isok;
   1134 {
   1135 	char *typestring = **argvp;
   1136 	PLAN *new;
   1137 	mode_t  mask = (mode_t)0;
   1138 
   1139 	(*argvp)++;
   1140 	ftsoptions &= ~FTS_NOSTAT;
   1141 
   1142 	switch (typestring[0]) {
   1143 #ifdef S_IFWHT
   1144       case 'W':
   1145 #ifdef FTS_WHITEOUT
   1146 	      ftsoptions |= FTS_WHITEOUT;
   1147 #endif
   1148               mask = S_IFWHT;
   1149               break;
   1150 #endif
   1151 	case 'b':
   1152 		mask = S_IFBLK;
   1153 		break;
   1154 	case 'c':
   1155 		mask = S_IFCHR;
   1156 		break;
   1157 	case 'd':
   1158 		mask = S_IFDIR;
   1159 		break;
   1160 	case 'f':
   1161 		mask = S_IFREG;
   1162 		break;
   1163 	case 'l':
   1164 		mask = S_IFLNK;
   1165 		break;
   1166 	case 'p':
   1167 		mask = S_IFIFO;
   1168 		break;
   1169 	case 's':
   1170 		mask = S_IFSOCK;
   1171 		break;
   1172 #ifdef FTS_WHITEOUT
   1173 	case 'w':
   1174 		mask = S_IFWHT;
   1175 		ftsoptions |= FTS_WHITEOUT;
   1176 		break;
   1177 #endif /* FTS_WHITEOUT */
   1178 	default:
   1179 		errx(1, "-type: %s: unknown type", typestring);
   1180 	}
   1181 
   1182 	new = palloc(N_TYPE, f_type);
   1183 	new->m_data = mask;
   1184 	return (new);
   1185 }
   1186 
   1187 /*
   1188  * -user uname functions --
   1189  *
   1190  *	True if the file belongs to the user uname.  If uname is numeric and
   1191  *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
   1192  *	return a valid user name, uname is taken as a user ID.
   1193  */
   1194 int
   1195 f_user(plan, entry)
   1196 	PLAN *plan;
   1197 	FTSENT *entry;
   1198 {
   1199 	return (entry->fts_statp->st_uid == plan->u_data);
   1200 }
   1201 
   1202 PLAN *
   1203 c_user(argvp, isok)
   1204 	char ***argvp;
   1205 	int isok;
   1206 {
   1207 	char *username = **argvp;
   1208 	PLAN *new;
   1209 	struct passwd *p;
   1210 	uid_t uid;
   1211 
   1212 	(*argvp)++;
   1213 	ftsoptions &= ~FTS_NOSTAT;
   1214 
   1215 	p = getpwnam(username);
   1216 	if (p == NULL) {
   1217 		uid = atoi(username);
   1218 		if (uid == 0 && username[0] != '0')
   1219 			errx(1, "-user: %s: no such user", username);
   1220 	} else
   1221 		uid = p->pw_uid;
   1222 
   1223 	new = palloc(N_USER, f_user);
   1224 	new->u_data = uid;
   1225 	return (new);
   1226 }
   1227 
   1228 /*
   1229  * -xdev functions --
   1230  *
   1231  *	Always true, causes find not to decend past directories that have a
   1232  *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
   1233  */
   1234 PLAN *
   1235 c_xdev(argvp, isok)
   1236 	char ***argvp;
   1237 	int isok;
   1238 {
   1239 	ftsoptions |= FTS_XDEV;
   1240 
   1241 	return (palloc(N_XDEV, f_always_true));
   1242 }
   1243 
   1244 /*
   1245  * ( expression ) functions --
   1246  *
   1247  *	True if expression is true.
   1248  */
   1249 int
   1250 f_expr(plan, entry)
   1251 	PLAN *plan;
   1252 	FTSENT *entry;
   1253 {
   1254 	PLAN *p;
   1255 	int state;
   1256 
   1257 	state = 0;
   1258 	for (p = plan->p_data[0];
   1259 	    p && (state = (p->eval)(p, entry)); p = p->next);
   1260 	return (state);
   1261 }
   1262 
   1263 /*
   1264  * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
   1265  * eliminated during phase 2 of find_formplan() --- the '(' node is converted
   1266  * to a N_EXPR node containing the expression and the ')' node is discarded.
   1267  */
   1268 PLAN *
   1269 c_openparen(argvp, isok)
   1270 	char ***argvp;
   1271 	int isok;
   1272 {
   1273 	return (palloc(N_OPENPAREN, (int (*) __P((PLAN *, FTSENT *)))-1));
   1274 }
   1275 
   1276 PLAN *
   1277 c_closeparen(argvp, isok)
   1278 	char ***argvp;
   1279 	int isok;
   1280 {
   1281 	return (palloc(N_CLOSEPAREN, (int (*) __P((PLAN *, FTSENT *)))-1));
   1282 }
   1283 
   1284 /*
   1285  * ! expression functions --
   1286  *
   1287  *	Negation of a primary; the unary NOT operator.
   1288  */
   1289 int
   1290 f_not(plan, entry)
   1291 	PLAN *plan;
   1292 	FTSENT *entry;
   1293 {
   1294 	PLAN *p;
   1295 	int state;
   1296 
   1297 	state = 0;
   1298 	for (p = plan->p_data[0];
   1299 	    p && (state = (p->eval)(p, entry)); p = p->next);
   1300 	return (!state);
   1301 }
   1302 
   1303 PLAN *
   1304 c_not(argvp, isok)
   1305 	char ***argvp;
   1306 	int isok;
   1307 {
   1308 	return (palloc(N_NOT, f_not));
   1309 }
   1310 
   1311 /*
   1312  * expression -o expression functions --
   1313  *
   1314  *	Alternation of primaries; the OR operator.  The second expression is
   1315  * not evaluated if the first expression is true.
   1316  */
   1317 int
   1318 f_or(plan, entry)
   1319 	PLAN *plan;
   1320 	FTSENT *entry;
   1321 {
   1322 	PLAN *p;
   1323 	int state;
   1324 
   1325 	state = 0;
   1326 	for (p = plan->p_data[0];
   1327 	    p && (state = (p->eval)(p, entry)); p = p->next);
   1328 
   1329 	if (state)
   1330 		return (1);
   1331 
   1332 	for (p = plan->p_data[1];
   1333 	    p && (state = (p->eval)(p, entry)); p = p->next);
   1334 	return (state);
   1335 }
   1336 
   1337 PLAN *
   1338 c_or(argvp, isok)
   1339 	char ***argvp;
   1340 	int isok;
   1341 {
   1342 	return (palloc(N_OR, f_or));
   1343 }
   1344 
   1345 PLAN *
   1346 c_null(argvp, isok)
   1347 	char ***argvp;
   1348 	int isok;
   1349 {
   1350 	return NULL;
   1351 }
   1352 
   1353 static PLAN *
   1354 palloc(t, f)
   1355 	enum ntype t;
   1356 	int (*f) __P((PLAN *, FTSENT *));
   1357 {
   1358 	PLAN *new;
   1359 
   1360 	if ((new = malloc(sizeof(PLAN))) == NULL)
   1361 		err(1, "%s", "");
   1362 	new->type = t;
   1363 	new->eval = f;
   1364 	new->flags = 0;
   1365 	new->next = NULL;
   1366 	return (new);
   1367 }
   1368