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