function.c revision 24313
174462Salfred/*-
274462Salfred * Copyright (c) 1990, 1993
3261057Smav *	The Regents of the University of California.  All rights reserved.
4261057Smav *
5261057Smav * This code is derived from software contributed to Berkeley by
68870Srgrimes * Cimarron D. Taylor of the University of California, Berkeley.
7261057Smav *
8261057Smav * Redistribution and use in source and binary forms, with or without
9261057Smav * modification, are permitted provided that the following conditions
10261057Smav * are met:
11261057Smav * 1. Redistributions of source code must retain the above copyright
12261057Smav *    notice, this list of conditions and the following disclaimer.
13261057Smav * 2. Redistributions in binary form must reproduce the above copyright
14261057Smav *    notice, this list of conditions and the following disclaimer in the
15261057Smav *    documentation and/or other materials provided with the distribution.
16261057Smav * 3. All advertising materials mentioning features or use of this software
17261057Smav *    must display the following acknowledgement:
18261057Smav *	This product includes software developed by the University of
19261057Smav *	California, Berkeley and its contributors.
20261057Smav * 4. Neither the name of the University nor the names of its contributors
21261057Smav *    may be used to endorse or promote products derived from this software
22261057Smav *    without specific prior written permission.
23261057Smav *
24261057Smav * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25261057Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26261057Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27261057Smav * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28261057Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291901Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301901Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311901Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32136581Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33136581Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341901Swollman * SUCH DAMAGE.
35136581Sobrien */
3692990Sobrien
3792990Sobrien#ifndef lint
381901Swollmanstatic char sccsid[] = "@(#)function.c	8.10 (Berkeley) 5/4/95";
391901Swollman#endif /* not lint */
401901Swollman
4117542Speter#include <sys/param.h>
4217542Speter#include <sys/ucred.h>
431901Swollman#include <sys/stat.h>
441901Swollman#include <sys/wait.h>
4571579Sdeischen#include <sys/mount.h>
461901Swollman
471901Swollman#include <err.h>
4874462Salfred#include <errno.h>
491901Swollman#include <fnmatch.h>
5074462Salfred#include <fts.h>
5174462Salfred#include <grp.h>
5274462Salfred#include <pwd.h>
5311666Sphk#include <stdio.h>
5474462Salfred#include <stdlib.h>
5574462Salfred#include <string.h>
5674462Salfred#include <unistd.h>
5711666Sphk
5871579Sdeischen#include "find.h"
591901Swollman
601901Swollman#define	COMPARE(a, b) {							\
611901Swollman	switch (plan->flags) {						\
621901Swollman	case F_EQUAL:							\
6317542Speter		return (a == b);					\
6417542Speter	case F_LESSTHAN:						\
651901Swollman		return (a < b);						\
661901Swollman	case F_GREATER:							\
671901Swollman		return (a > b);						\
6856629Sshin	default:							\
6955918Sshin		abort();						\
7055918Sshin	}								\
7156629Sshin}
7274462Salfred
7356629Sshinstatic PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *))));
7455918Sshin
7556629Sshin/*
7655918Sshin * find_parsenum --
7755918Sshin *	Parse a string of the form [+-]# and return the value.
7855918Sshin */
7956629Sshinstatic long long
8056629Sshinfind_parsenum(plan, option, vp, endch)
8156629Sshin	PLAN *plan;
8274462Salfred	char *option, *vp, *endch;
8356629Sshin{
8474462Salfred	long long value;
8556629Sshin	char *endchar, *str;	/* Pointer to character ending conversion. */
8674462Salfred
8774462Salfred	/* Determine comparison from leading + or -. */
8855918Sshin	str = vp;
8955918Sshin	switch (*str) {
9056629Sshin	case '+':
9156629Sshin		++str;
9256629Sshin		plan->flags = F_GREATER;
9371579Sdeischen		break;
9456629Sshin	case '-':
9556629Sshin		++str;
9656629Sshin		plan->flags = F_LESSTHAN;
9774462Salfred		break;
9856629Sshin	default:
9956629Sshin		plan->flags = F_EQUAL;
10056629Sshin		break;
10174462Salfred	}
10274462Salfred
10356629Sshin	/*
10456629Sshin	 * Convert the string with strtoq().  Note, if strtoq() returns zero
10556629Sshin	 * and endchar points to the beginning of the string we know we have
10656629Sshin	 * a syntax error.
10756629Sshin	 */
10874462Salfred	value = strtoq(str, &endchar, 10);
10974462Salfred	if (value == 0 && endchar == str)
11074462Salfred		errx(1, "%s: %s: illegal numeric value", option, vp);
11174462Salfred	if (endchar[0] && (endch == NULL || endchar[0] != *endch))
11256629Sshin		errx(1, "%s: %s: illegal trailing character", option, vp);
11356629Sshin	if (endch)
11456629Sshin		*endch = endchar[0];
11556629Sshin	return (value);
11656629Sshin}
11774462Salfred
11874462Salfred/*
11974462Salfred * The value of n for the inode times (atime, ctime, and mtime) is a range,
12074462Salfred * i.e. n matches from (n - 1) to n 24 hour periods.  This interacts with
12156629Sshin * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
12255918Sshin * user wanted.  Correct so that -1 is "less than 1".
12355918Sshin */
12456629Sshin#define	TIME_CORRECT(p, ttype)						\
12556629Sshin	if ((p)->type == ttype && (p)->flags == F_LESSTHAN)		\
12655918Sshin		++((p)->t_data);
12774462Salfred
12874462Salfred/*
12956629Sshin * -atime n functions --
13071579Sdeischen *
13117542Speter *	True if the difference between the file access time and the
13256629Sshin *	current time is n 24 hour periods.
13317542Speter */
13471579Sdeischenint
13556629Sshinf_atime(plan, entry)
13617542Speter	PLAN *plan;
13756629Sshin	FTSENT *entry;
1381901Swollman{
13917542Speter	extern time_t now;
14071579Sdeischen
14117542Speter	COMPARE((now - entry->fts_statp->st_atime +
14274462Salfred	    86400 - 1) / 86400, plan->t_data);
14317542Speter}
14417542Speter
14574462SalfredPLAN *
14671579Sdeischenc_atime(arg)
14756629Sshin	char *arg;
14817542Speter{
14917542Speter	PLAN *new;
1501901Swollman
15117542Speter	ftsoptions &= ~FTS_NOSTAT;
15256629Sshin
15374462Salfred	new = palloc(N_ATIME, f_atime);
15471579Sdeischen	new->t_data = find_parsenum(new, "-atime", arg, NULL);
15556629Sshin	TIME_CORRECT(new, N_ATIME);
15656629Sshin	return (new);
15756629Sshin}
1581901Swollman/*
15917542Speter * -ctime n functions --
1601901Swollman *
161 *	True if the difference between the last change of file
162 *	status information and the current time is n 24 hour periods.
163 */
164int
165f_ctime(plan, entry)
166	PLAN *plan;
167	FTSENT *entry;
168{
169	extern time_t now;
170
171	COMPARE((now - entry->fts_statp->st_ctime +
172	    86400 - 1) / 86400, plan->t_data);
173}
174
175PLAN *
176c_ctime(arg)
177	char *arg;
178{
179	PLAN *new;
180
181	ftsoptions &= ~FTS_NOSTAT;
182
183	new = palloc(N_CTIME, f_ctime);
184	new->t_data = find_parsenum(new, "-ctime", arg, NULL);
185	TIME_CORRECT(new, N_CTIME);
186	return (new);
187}
188
189/*
190 * -depth functions --
191 *
192 *	Always true, causes descent of the directory hierarchy to be done
193 *	so that all entries in a directory are acted on before the directory
194 *	itself.
195 */
196int
197f_always_true(plan, entry)
198	PLAN *plan;
199	FTSENT *entry;
200{
201	return (1);
202}
203
204PLAN *
205c_depth()
206{
207	isdepth = 1;
208
209	return (palloc(N_DEPTH, f_always_true));
210}
211
212/*
213 * [-exec | -ok] utility [arg ... ] ; functions --
214 *
215 *	True if the executed utility returns a zero value as exit status.
216 *	The end of the primary expression is delimited by a semicolon.  If
217 *	"{}" occurs anywhere, it gets replaced by the current pathname.
218 *	The current directory for the execution of utility is the same as
219 *	the current directory when the find utility was started.
220 *
221 *	The primary -ok is different in that it requests affirmation of the
222 *	user before executing the utility.
223 */
224int
225f_exec(plan, entry)
226	register PLAN *plan;
227	FTSENT *entry;
228{
229	extern int dotfd;
230	register int cnt;
231	pid_t pid;
232	int status;
233
234	for (cnt = 0; plan->e_argv[cnt]; ++cnt)
235		if (plan->e_len[cnt])
236			brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
237			    entry->fts_path, plan->e_len[cnt]);
238
239	if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
240		return (0);
241
242	/* make sure find output is interspersed correctly with subprocesses */
243	fflush(stdout);
244
245	switch (pid = vfork()) {
246	case -1:
247		err(1, "fork");
248		/* NOTREACHED */
249	case 0:
250		if (fchdir(dotfd)) {
251			warn("chdir");
252			_exit(1);
253		}
254		execvp(plan->e_argv[0], plan->e_argv);
255		warn("%s", plan->e_argv[0]);
256		_exit(1);
257	}
258	pid = waitpid(pid, &status, 0);
259	return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
260}
261
262/*
263 * c_exec --
264 *	build three parallel arrays, one with pointers to the strings passed
265 *	on the command line, one with (possibly duplicated) pointers to the
266 *	argv array, and one with integer values that are lengths of the
267 *	strings, but also flags meaning that the string has to be massaged.
268 */
269PLAN *
270c_exec(argvp, isok)
271	char ***argvp;
272	int isok;
273{
274	PLAN *new;			/* node returned */
275	register int cnt;
276	register char **argv, **ap, *p;
277
278	isoutput = 1;
279
280	new = palloc(N_EXEC, f_exec);
281	if (isok)
282		new->flags = F_NEEDOK;
283
284	for (ap = argv = *argvp;; ++ap) {
285		if (!*ap)
286			errx(1,
287			    "%s: no terminating \";\"", isok ? "-ok" : "-exec");
288		if (**ap == ';')
289			break;
290	}
291
292	cnt = ap - *argvp + 1;
293	new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
294	new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
295	new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
296
297	for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
298		new->e_orig[cnt] = *argv;
299		for (p = *argv; *p; ++p)
300			if (p[0] == '{' && p[1] == '}') {
301				new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
302				new->e_len[cnt] = MAXPATHLEN;
303				break;
304			}
305		if (!*p) {
306			new->e_argv[cnt] = *argv;
307			new->e_len[cnt] = 0;
308		}
309	}
310	new->e_argv[cnt] = new->e_orig[cnt] = NULL;
311
312	*argvp = argv + 1;
313	return (new);
314}
315
316/*
317 * -follow functions --
318 *
319 *	Always true, causes symbolic links to be followed on a global
320 *	basis.
321 */
322PLAN *
323c_follow()
324{
325	ftsoptions &= ~FTS_PHYSICAL;
326	ftsoptions |= FTS_LOGICAL;
327
328	return (palloc(N_FOLLOW, f_always_true));
329}
330
331/*
332 * -fstype functions --
333 *
334 *	True if the file is of a certain type.
335 */
336int
337f_fstype(plan, entry)
338	PLAN *plan;
339	FTSENT *entry;
340{
341	static dev_t curdev;	/* need a guaranteed illegal dev value */
342	static int first = 1;
343	struct statfs sb;
344	static int val_type, val_flags;
345	char *p, save[2];
346
347	/* Only check when we cross mount point. */
348	if (first || curdev != entry->fts_statp->st_dev) {
349		curdev = entry->fts_statp->st_dev;
350
351		/*
352		 * Statfs follows symlinks; find wants the link's file system,
353		 * not where it points.
354		 */
355		if (entry->fts_info == FTS_SL ||
356		    entry->fts_info == FTS_SLNONE) {
357			if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
358				++p;
359			else
360				p = entry->fts_accpath;
361			save[0] = p[0];
362			p[0] = '.';
363			save[1] = p[1];
364			p[1] = '\0';
365
366		} else
367			p = NULL;
368
369		if (statfs(entry->fts_accpath, &sb))
370			err(1, "%s", entry->fts_accpath);
371
372		if (p) {
373			p[0] = save[0];
374			p[1] = save[1];
375		}
376
377		first = 0;
378
379		/*
380		 * Further tests may need both of these values, so
381		 * always copy both of them.
382		 */
383		val_flags = sb.f_flags;
384		val_type = sb.f_type;
385	}
386	switch (plan->flags) {
387	case F_MTFLAG:
388		return (val_flags & plan->mt_data) != 0;
389	case F_MTTYPE:
390		return (val_type == plan->mt_data);
391	default:
392		abort();
393	}
394}
395
396PLAN *
397c_fstype(arg)
398	char *arg;
399{
400	register PLAN *new;
401	struct vfsconf vfc;
402
403	ftsoptions &= ~FTS_NOSTAT;
404
405	new = palloc(N_FSTYPE, f_fstype);
406
407	/*
408	 * Check first for a filesystem name.
409	 */
410	if (getvfsbyname(arg, &vfc) == 0) {
411		new->flags = F_MTTYPE;
412		new->mt_data = vfc.vfc_typenum;
413		return (new);
414	}
415
416	switch (*arg) {
417	case 'l':
418		if (!strcmp(arg, "local")) {
419			new->flags = F_MTFLAG;
420			new->mt_data = MNT_LOCAL;
421			return (new);
422		}
423		break;
424	case 'r':
425		if (!strcmp(arg, "rdonly")) {
426			new->flags = F_MTFLAG;
427			new->mt_data = MNT_RDONLY;
428			return (new);
429		}
430		break;
431	}
432	errx(1, "%s: unknown file type", arg);
433	/* NOTREACHED */
434}
435
436/*
437 * -group gname functions --
438 *
439 *	True if the file belongs to the group gname.  If gname is numeric and
440 *	an equivalent of the getgrnam() function does not return a valid group
441 *	name, gname is taken as a group ID.
442 */
443int
444f_group(plan, entry)
445	PLAN *plan;
446	FTSENT *entry;
447{
448	return (entry->fts_statp->st_gid == plan->g_data);
449}
450
451PLAN *
452c_group(gname)
453	char *gname;
454{
455	PLAN *new;
456	struct group *g;
457	gid_t gid;
458
459	ftsoptions &= ~FTS_NOSTAT;
460
461	g = getgrnam(gname);
462	if (g == NULL) {
463		gid = atoi(gname);
464		if (gid == 0 && gname[0] != '0')
465			errx(1, "-group: %s: no such group", gname);
466	} else
467		gid = g->gr_gid;
468
469	new = palloc(N_GROUP, f_group);
470	new->g_data = gid;
471	return (new);
472}
473
474/*
475 * -inum n functions --
476 *
477 *	True if the file has inode # n.
478 */
479int
480f_inum(plan, entry)
481	PLAN *plan;
482	FTSENT *entry;
483{
484	COMPARE(entry->fts_statp->st_ino, plan->i_data);
485}
486
487PLAN *
488c_inum(arg)
489	char *arg;
490{
491	PLAN *new;
492
493	ftsoptions &= ~FTS_NOSTAT;
494
495	new = palloc(N_INUM, f_inum);
496	new->i_data = find_parsenum(new, "-inum", arg, NULL);
497	return (new);
498}
499
500/*
501 * -links n functions --
502 *
503 *	True if the file has n links.
504 */
505int
506f_links(plan, entry)
507	PLAN *plan;
508	FTSENT *entry;
509{
510	COMPARE(entry->fts_statp->st_nlink, plan->l_data);
511}
512
513PLAN *
514c_links(arg)
515	char *arg;
516{
517	PLAN *new;
518
519	ftsoptions &= ~FTS_NOSTAT;
520
521	new = palloc(N_LINKS, f_links);
522	new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
523	return (new);
524}
525
526/*
527 * -ls functions --
528 *
529 *	Always true - prints the current entry to stdout in "ls" format.
530 */
531int
532f_ls(plan, entry)
533	PLAN *plan;
534	FTSENT *entry;
535{
536	printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
537	return (1);
538}
539
540PLAN *
541c_ls()
542{
543	ftsoptions &= ~FTS_NOSTAT;
544	isoutput = 1;
545
546	return (palloc(N_LS, f_ls));
547}
548
549/*
550 * -mtime n functions --
551 *
552 *	True if the difference between the file modification time and the
553 *	current time is n 24 hour periods.
554 */
555int
556f_mtime(plan, entry)
557	PLAN *plan;
558	FTSENT *entry;
559{
560	extern time_t now;
561
562	COMPARE((now - entry->fts_statp->st_mtime + 86400 - 1) /
563	    86400, plan->t_data);
564}
565
566PLAN *
567c_mtime(arg)
568	char *arg;
569{
570	PLAN *new;
571
572	ftsoptions &= ~FTS_NOSTAT;
573
574	new = palloc(N_MTIME, f_mtime);
575	new->t_data = find_parsenum(new, "-mtime", arg, NULL);
576	TIME_CORRECT(new, N_MTIME);
577	return (new);
578}
579
580/*
581 * -name functions --
582 *
583 *	True if the basename of the filename being examined
584 *	matches pattern using Pattern Matching Notation S3.14
585 */
586int
587f_name(plan, entry)
588	PLAN *plan;
589	FTSENT *entry;
590{
591	return (!fnmatch(plan->c_data, entry->fts_name, 0));
592}
593
594PLAN *
595c_name(pattern)
596	char *pattern;
597{
598	PLAN *new;
599
600	new = palloc(N_NAME, f_name);
601	new->c_data = pattern;
602	return (new);
603}
604
605/*
606 * -newer file functions --
607 *
608 *	True if the current file has been modified more recently
609 *	then the modification time of the file named by the pathname
610 *	file.
611 */
612int
613f_newer(plan, entry)
614	PLAN *plan;
615	FTSENT *entry;
616{
617	return (entry->fts_statp->st_mtime > plan->t_data);
618}
619
620PLAN *
621c_newer(filename)
622	char *filename;
623{
624	PLAN *new;
625	struct stat sb;
626
627	ftsoptions &= ~FTS_NOSTAT;
628
629	if (stat(filename, &sb))
630		err(1, "%s", filename);
631	new = palloc(N_NEWER, f_newer);
632	new->t_data = sb.st_mtime;
633	return (new);
634}
635
636/*
637 * -nogroup functions --
638 *
639 *	True if file belongs to a user ID for which the equivalent
640 *	of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
641 */
642int
643f_nogroup(plan, entry)
644	PLAN *plan;
645	FTSENT *entry;
646{
647	char *group_from_gid();
648
649	return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
650}
651
652PLAN *
653c_nogroup()
654{
655	ftsoptions &= ~FTS_NOSTAT;
656
657	return (palloc(N_NOGROUP, f_nogroup));
658}
659
660/*
661 * -nouser functions --
662 *
663 *	True if file belongs to a user ID for which the equivalent
664 *	of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
665 */
666int
667f_nouser(plan, entry)
668	PLAN *plan;
669	FTSENT *entry;
670{
671	char *user_from_uid();
672
673	return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
674}
675
676PLAN *
677c_nouser()
678{
679	ftsoptions &= ~FTS_NOSTAT;
680
681	return (palloc(N_NOUSER, f_nouser));
682}
683
684/*
685 * -path functions --
686 *
687 *	True if the path of the filename being examined
688 *	matches pattern using Pattern Matching Notation S3.14
689 */
690int
691f_path(plan, entry)
692	PLAN *plan;
693	FTSENT *entry;
694{
695	return (!fnmatch(plan->c_data, entry->fts_path, 0));
696}
697
698PLAN *
699c_path(pattern)
700	char *pattern;
701{
702	PLAN *new;
703
704	new = palloc(N_NAME, f_path);
705	new->c_data = pattern;
706	return (new);
707}
708
709/*
710 * -perm functions --
711 *
712 *	The mode argument is used to represent file mode bits.  If it starts
713 *	with a leading digit, it's treated as an octal mode, otherwise as a
714 *	symbolic mode.
715 */
716int
717f_perm(plan, entry)
718	PLAN *plan;
719	FTSENT *entry;
720{
721	mode_t mode;
722
723	mode = entry->fts_statp->st_mode &
724	    (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
725	if (plan->flags == F_ATLEAST)
726		return ((plan->m_data | mode) == mode);
727	else
728		return (mode == plan->m_data);
729	/* NOTREACHED */
730}
731
732PLAN *
733c_perm(perm)
734	char *perm;
735{
736	PLAN *new;
737	mode_t *set;
738
739	ftsoptions &= ~FTS_NOSTAT;
740
741	new = palloc(N_PERM, f_perm);
742
743	if (*perm == '-') {
744		new->flags = F_ATLEAST;
745		++perm;
746	}
747
748	if ((set = setmode(perm)) == NULL)
749		err(1, "-perm: %s: illegal mode string", perm);
750
751	new->m_data = getmode(set, 0);
752	return (new);
753}
754
755/*
756 * -print functions --
757 *
758 *	Always true, causes the current pathame to be written to
759 *	standard output.
760 */
761int
762f_print(plan, entry)
763	PLAN *plan;
764	FTSENT *entry;
765{
766	(void)puts(entry->fts_path);
767	return (1);
768}
769
770PLAN *
771c_print()
772{
773	isoutput = 1;
774
775	return (palloc(N_PRINT, f_print));
776}
777
778/*
779 * -print0 functions --
780 *
781 *	Always true, causes the current pathame to be written to
782 *	standard output followed by a NUL character
783 */
784int
785f_print0(plan, entry)
786	PLAN *plan;
787	FTSENT *entry;
788{
789	fputs(entry->fts_path, stdout);
790	fputc('\0', stdout);
791	return (1);
792}
793
794PLAN *
795c_print0()
796{
797	isoutput = 1;
798
799	return (palloc(N_PRINT0, f_print0));
800}
801
802/*
803 * -prune functions --
804 *
805 *	Prune a portion of the hierarchy.
806 */
807int
808f_prune(plan, entry)
809	PLAN *plan;
810	FTSENT *entry;
811{
812	extern FTS *tree;
813
814	if (fts_set(tree, entry, FTS_SKIP))
815		err(1, "%s", entry->fts_path);
816	return (1);
817}
818
819PLAN *
820c_prune()
821{
822	return (palloc(N_PRUNE, f_prune));
823}
824
825/*
826 * -size n[c] functions --
827 *
828 *	True if the file size in bytes, divided by an implementation defined
829 *	value and rounded up to the next integer, is n.  If n is followed by
830 *	a c, the size is in bytes.
831 */
832#define	FIND_SIZE	512
833static int divsize = 1;
834
835int
836f_size(plan, entry)
837	PLAN *plan;
838	FTSENT *entry;
839{
840	off_t size;
841
842	size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
843	    FIND_SIZE : entry->fts_statp->st_size;
844	COMPARE(size, plan->o_data);
845}
846
847PLAN *
848c_size(arg)
849	char *arg;
850{
851	PLAN *new;
852	char endch;
853
854	ftsoptions &= ~FTS_NOSTAT;
855
856	new = palloc(N_SIZE, f_size);
857	endch = 'c';
858	new->o_data = find_parsenum(new, "-size", arg, &endch);
859	if (endch == 'c')
860		divsize = 0;
861	return (new);
862}
863
864/*
865 * -type c functions --
866 *
867 *	True if the type of the file is c, where c is b, c, d, p, f or w
868 *	for block special file, character special file, directory, FIFO,
869 *	regular file or whiteout respectively.
870 */
871int
872f_type(plan, entry)
873	PLAN *plan;
874	FTSENT *entry;
875{
876	return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
877}
878
879PLAN *
880c_type(typestring)
881	char *typestring;
882{
883	PLAN *new;
884	mode_t  mask;
885
886	ftsoptions &= ~FTS_NOSTAT;
887
888	switch (typestring[0]) {
889	case 'b':
890		mask = S_IFBLK;
891		break;
892	case 'c':
893		mask = S_IFCHR;
894		break;
895	case 'd':
896		mask = S_IFDIR;
897		break;
898	case 'f':
899		mask = S_IFREG;
900		break;
901	case 'l':
902		mask = S_IFLNK;
903		break;
904	case 'p':
905		mask = S_IFIFO;
906		break;
907	case 's':
908		mask = S_IFSOCK;
909		break;
910#ifdef FTS_WHITEOUT
911	case 'w':
912		mask = S_IFWHT;
913		ftsoptions |= FTS_WHITEOUT;
914		break;
915#endif /* FTS_WHITEOUT */
916	default:
917		errx(1, "-type: %s: unknown type", typestring);
918	}
919
920	new = palloc(N_TYPE, f_type);
921	new->m_data = mask;
922	return (new);
923}
924
925/*
926 * -delete functions --
927 *
928 *	True always.  Makes it's best shot and continues on regardless.
929 */
930int
931f_delete(plan, entry)
932	PLAN *plan;
933	FTSENT *entry;
934{
935	/* ignore these from fts */
936	if (strcmp(entry->fts_accpath, ".") == 0 ||
937	    strcmp(entry->fts_accpath, "..") == 0)
938		return (1);
939
940	/* sanity check */
941	if (isdepth == 0 ||			/* depth off */
942	    (ftsoptions & FTS_NOSTAT) ||	/* not stat()ing */
943	    !(ftsoptions & FTS_PHYSICAL) ||	/* physical off */
944	    (ftsoptions & FTS_LOGICAL))		/* or finally, logical on */
945		errx(1, "-delete: insecure options got turned on");
946
947	/* Potentially unsafe - do not accept relative paths whatsoever */
948	if (strchr(entry->fts_accpath, '/') != NULL)
949		errx(1, "-delete: %s: relative path potentially not safe",
950			entry->fts_accpath);
951
952	/* Turn off user immutable bits if running as root */
953	if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
954	    !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
955	    geteuid() == 0)
956		chflags(entry->fts_accpath,
957		       entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
958
959	/* rmdir directories, unlink everything else */
960	if (S_ISDIR(entry->fts_statp->st_mode)) {
961		if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
962			warn("-delete: rmdir(%s)", entry->fts_path);
963	} else {
964		if (unlink(entry->fts_accpath) < 0)
965			warn("-delete: unlink(%s)", entry->fts_path);
966	}
967
968	/* "succeed" */
969	return (1);
970}
971
972PLAN *
973c_delete()
974{
975
976	ftsoptions &= ~FTS_NOSTAT;	/* no optimise */
977	ftsoptions |= FTS_PHYSICAL;	/* disable -follow */
978	ftsoptions &= ~FTS_LOGICAL;	/* disable -follow */
979	isoutput = 1;			/* possible output */
980	isdepth = 1;			/* -depth implied */
981
982	return (palloc(N_DELETE, f_delete));
983}
984
985/*
986 * -user uname functions --
987 *
988 *	True if the file belongs to the user uname.  If uname is numeric and
989 *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
990 *	return a valid user name, uname is taken as a user ID.
991 */
992int
993f_user(plan, entry)
994	PLAN *plan;
995	FTSENT *entry;
996{
997	return (entry->fts_statp->st_uid == plan->u_data);
998}
999
1000PLAN *
1001c_user(username)
1002	char *username;
1003{
1004	PLAN *new;
1005	struct passwd *p;
1006	uid_t uid;
1007
1008	ftsoptions &= ~FTS_NOSTAT;
1009
1010	p = getpwnam(username);
1011	if (p == NULL) {
1012		uid = atoi(username);
1013		if (uid == 0 && username[0] != '0')
1014			errx(1, "-user: %s: no such user", username);
1015	} else
1016		uid = p->pw_uid;
1017
1018	new = palloc(N_USER, f_user);
1019	new->u_data = uid;
1020	return (new);
1021}
1022
1023/*
1024 * -xdev functions --
1025 *
1026 *	Always true, causes find not to decend past directories that have a
1027 *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1028 */
1029PLAN *
1030c_xdev()
1031{
1032	ftsoptions |= FTS_XDEV;
1033
1034	return (palloc(N_XDEV, f_always_true));
1035}
1036
1037/*
1038 * ( expression ) functions --
1039 *
1040 *	True if expression is true.
1041 */
1042int
1043f_expr(plan, entry)
1044	PLAN *plan;
1045	FTSENT *entry;
1046{
1047	register PLAN *p;
1048	register int state;
1049
1050	for (p = plan->p_data[0];
1051	    p && (state = (p->eval)(p, entry)); p = p->next);
1052	return (state);
1053}
1054
1055/*
1056 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
1057 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1058 * to a N_EXPR node containing the expression and the ')' node is discarded.
1059 */
1060PLAN *
1061c_openparen()
1062{
1063	return (palloc(N_OPENPAREN, (int (*)())-1));
1064}
1065
1066PLAN *
1067c_closeparen()
1068{
1069	return (palloc(N_CLOSEPAREN, (int (*)())-1));
1070}
1071
1072/*
1073 * ! expression functions --
1074 *
1075 *	Negation of a primary; the unary NOT operator.
1076 */
1077int
1078f_not(plan, entry)
1079	PLAN *plan;
1080	FTSENT *entry;
1081{
1082	register PLAN *p;
1083	register int state;
1084
1085	for (p = plan->p_data[0];
1086	    p && (state = (p->eval)(p, entry)); p = p->next);
1087	return (!state);
1088}
1089
1090PLAN *
1091c_not()
1092{
1093	return (palloc(N_NOT, f_not));
1094}
1095
1096/*
1097 * expression -o expression functions --
1098 *
1099 *	Alternation of primaries; the OR operator.  The second expression is
1100 * not evaluated if the first expression is true.
1101 */
1102int
1103f_or(plan, entry)
1104	PLAN *plan;
1105	FTSENT *entry;
1106{
1107	register PLAN *p;
1108	register int state;
1109
1110	for (p = plan->p_data[0];
1111	    p && (state = (p->eval)(p, entry)); p = p->next);
1112
1113	if (state)
1114		return (1);
1115
1116	for (p = plan->p_data[1];
1117	    p && (state = (p->eval)(p, entry)); p = p->next);
1118	return (state);
1119}
1120
1121PLAN *
1122c_or()
1123{
1124	return (palloc(N_OR, f_or));
1125}
1126
1127static PLAN *
1128palloc(t, f)
1129	enum ntype t;
1130	int (*f) __P((PLAN *, FTSENT *));
1131{
1132	PLAN *new;
1133
1134	if ((new = malloc(sizeof(PLAN))) == NULL)
1135		err(1, NULL);
1136	new->type = t;
1137	new->eval = f;
1138	new->flags = 0;
1139	new->next = NULL;
1140	return (new);
1141}
1142