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