GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/find/function.c Lines: 38 475 8.0 %
Date: 2016-12-06 Branches: 7 296 2.4 %

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


5
	switch (typestring[0]) {
1442
	case 'b':
1443
		mask = S_IFBLK;
1444
		break;
1445
	case 'c':
1446
		mask = S_IFCHR;
1447
		break;
1448
	case 'd':
1449
		mask = S_IFDIR;
1450
		break;
1451
	case 'f':
1452
5
		mask = S_IFREG;
1453
5
		break;
1454
	case 'l':
1455
		mask = S_IFLNK;
1456
		break;
1457
	case 'p':
1458
		mask = S_IFIFO;
1459
		break;
1460
	case 's':
1461
		mask = S_IFSOCK;
1462
		break;
1463
	default:
1464
		errx(1, "-type: %s: unknown type", typestring);
1465
	}
1466
1467
5
	new = palloc(N_TYPE, f_type);
1468
5
	new->m_data = mask;
1469
5
	return (new);
1470
}
1471
1472
/*
1473
 * -user uname functions --
1474
 *
1475
 *	True if the file belongs to the user uname.  If uname is numeric and
1476
 *	an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1477
 *	return a valid user name, uname is taken as a user ID.
1478
 */
1479
int
1480
f_user(PLAN *plan, FTSENT *entry)
1481
{
1482
	return (entry->fts_statp->st_uid == plan->u_data);
1483
}
1484
1485
PLAN *
1486
c_user(char *username, char ***ignored, int unused)
1487
{
1488
	PLAN *new;
1489
	struct passwd *p;
1490
	uid_t uid;
1491
1492
	ftsoptions &= ~FTS_NOSTAT;
1493
1494
	p = getpwnam(username);
1495
	if (p == NULL) {
1496
		const char *errstr;
1497
1498
		uid = strtonum(username, 0, UID_MAX, &errstr);
1499
		if (errstr)
1500
			errx(1, "-user: %s: no such user", username);
1501
	} else
1502
		uid = p->pw_uid;
1503
1504
	new = palloc(N_USER, f_user);
1505
	new->u_data = uid;
1506
	return (new);
1507
}
1508
1509
/*
1510
 * -xdev functions --
1511
 *
1512
 *	Always true, causes find not to decend past directories that have a
1513
 *	different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1514
 */
1515
PLAN *
1516
c_xdev(char *ignore, char ***ignored, int unused)
1517
{
1518
	ftsoptions |= FTS_XDEV;
1519
1520
	return (palloc(N_XDEV, f_always_true));
1521
}
1522
1523
/*
1524
 * ( expression ) functions --
1525
 *
1526
 *	True if expression is true.
1527
 */
1528
int
1529
f_expr(PLAN *plan, FTSENT *entry)
1530
91242
{
1531
	PLAN *p;
1532
	int state;
1533
1534
91242
	for (p = plan->p_data[0];
1535

91242
	    p && (state = (p->eval)(p, entry)); p = p->next);
1536
91242
	return (state);
1537
}
1538
1539
/*
1540
 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers.  They are
1541
 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1542
 * to a N_EXPR node containing the expression and the ')' node is discarded.
1543
 */
1544
PLAN *
1545
c_openparen(char *ignore, char ***ignored, int unused)
1546
4
{
1547
4
	return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1));
1548
}
1549
1550
PLAN *
1551
c_closeparen(char *ignore, char ***ignored, int unused)
1552
4
{
1553
4
	return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1));
1554
}
1555
1556
/*
1557
 * ! expression functions --
1558
 *
1559
 *	Negation of a primary; the unary NOT operator.
1560
 */
1561
int
1562
f_not(PLAN *plan, FTSENT *entry)
1563
{
1564
	PLAN *p;
1565
	int state;
1566
1567
	for (p = plan->p_data[0];
1568
	    p && (state = (p->eval)(p, entry)); p = p->next);
1569
	return (!state);
1570
}
1571
1572
PLAN *
1573
c_not(char *ignore, char ***ignored, int unused)
1574
{
1575
	return (palloc(N_NOT, f_not));
1576
}
1577
1578
/*
1579
 * expression -o expression functions --
1580
 *
1581
 *	Alternation of primaries; the OR operator.  The second expression is
1582
 * not evaluated if the first expression is true.
1583
 */
1584
int
1585
f_or(PLAN *plan, FTSENT *entry)
1586
{
1587
	PLAN *p;
1588
	int state;
1589
1590
	for (p = plan->p_data[0];
1591
	    p && (state = (p->eval)(p, entry)); p = p->next);
1592
1593
	if (state)
1594
		return (1);
1595
1596
	for (p = plan->p_data[1];
1597
	    p && (state = (p->eval)(p, entry)); p = p->next);
1598
	return (state);
1599
}
1600
1601
PLAN *
1602
c_or(char *ignore, char ***ignored, int unused)
1603
{
1604
	return (palloc(N_OR, f_or));
1605
}
1606
1607
1608
/*
1609
 * plan_cleanup --
1610
 *	Check and see if the specified plan has any residual state,
1611
 *	and if so, clean it up as appropriate.
1612
 *
1613
 *	At the moment, only N_EXEC has state. Two kinds: 1)
1614
 * 	lists of files to feed to subprocesses 2) State on exit
1615
 *	statusses of past subprocesses.
1616
 */
1617
/* ARGSUSED1 */
1618
int
1619
plan_cleanup(PLAN *plan, void *arg)
1620
19
{
1621

19
	if (plan->type==N_EXEC && plan->ep_narg)
1622
		run_f_exec(plan);
1623
1624
19
	return plan->ep_rval;		/* Passed save exit-status up chain */
1625
}
1626
1627
1628
static PLAN *
1629
palloc(enum ntype t, int (*f)(PLAN *, FTSENT *))
1630
23
{
1631
	PLAN *new;
1632
1633
23
	if ((new = calloc(1, sizeof(PLAN)))) {
1634
23
		new->type = t;
1635
23
		new->eval = f;
1636
23
		return (new);
1637
	}
1638
	err(1, NULL);
1639
	/* NOTREACHED */
1640
}