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