Bug Summary

File:src/usr.bin/find/function.c
Warning:line 749, column 12
Although the value stored to 'argv' is used in the enclosing expression, the value is never actually read from 'argv'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name function.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/find/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/find/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/find/function.c
1/* $OpenBSD: function.c,v 1.50 2020/11/23 06:21:52 halex 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(*__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){ switch (plan->flags) { case 1: return (a == b); case 2: return
(a < b); case 3: return (a > b); default: abort(); } }
{ \
58 switch (plan->flags) { \
59 case F_EQUAL1: \
60 return (a == b); \
61 case F_LESSTHAN2: \
62 return (a < b); \
63 case F_GREATER3: \
64 return (a > b); \
65 default: \
66 abort(); \
67 } \
68}
69
70static PLAN *palloc(enum ntype, int (*)(PLAN *, FTSENT *));
71static long long find_parsenum(PLAN *plan, char *option, char *vp, char *endch);
72static void run_f_exec(PLAN *plan);
73static PLAN *palloc(enum ntype t, int (*f)(PLAN *, FTSENT *));
74
75int f_amin(PLAN *, FTSENT *);
76int f_atime(PLAN *, FTSENT *);
77int f_cmin(PLAN *, FTSENT *);
78int f_ctime(PLAN *, FTSENT *);
79int f_always_true(PLAN *, FTSENT *);
80int f_empty(PLAN *, FTSENT *);
81int f_exec(PLAN *, FTSENT *);
82int f_execdir(PLAN *, FTSENT *);
83int f_flags(PLAN *, FTSENT *);
84int f_fstype(PLAN *, FTSENT *);
85int f_group(PLAN *, FTSENT *);
86int f_inum(PLAN *, FTSENT *);
87int f_empty(PLAN *, FTSENT *);
88int f_links(PLAN *, FTSENT *);
89int f_ls(PLAN *, FTSENT *);
90int f_maxdepth(PLAN *, FTSENT *);
91int f_mindepth(PLAN *, FTSENT *);
92int f_mtime(PLAN *, FTSENT *);
93int f_mmin(PLAN *, FTSENT *);
94int f_name(PLAN *, FTSENT *);
95int f_iname(PLAN *, FTSENT *);
96int f_newer(PLAN *, FTSENT *);
97int f_anewer(PLAN *, FTSENT *);
98int f_cnewer(PLAN *, FTSENT *);
99int f_nogroup(PLAN *, FTSENT *);
100int f_nouser(PLAN *, FTSENT *);
101int f_path(PLAN *, FTSENT *);
102int f_perm(PLAN *, FTSENT *);
103int f_print(PLAN *, FTSENT *);
104int f_print0(PLAN *, FTSENT *);
105int f_prune(PLAN *, FTSENT *);
106int f_size(PLAN *, FTSENT *);
107int f_type(PLAN *, FTSENT *);
108int f_user(PLAN *, FTSENT *);
109int f_expr(PLAN *, FTSENT *);
110int f_not(PLAN *, FTSENT *);
111int f_or(PLAN *, FTSENT *);
112
113extern int dotfd;
114extern time_t now;
115extern FTS *tree;
116
117/*
118 * find_parsenum --
119 * Parse a string of the form [+-]# and return the value.
120 */
121static long long
122find_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_GREATER3;
133 break;
134 case '-':
135 ++str;
136 plan->flags = F_LESSTHAN2;
137 break;
138 default:
139 plan->flags = F_EQUAL1;
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((void *)0) || 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)if ((p)->type == ttype && (p)->flags == 2) ++((
p)->p_un._t_data.tv_sec);
\
165 if ((p)->type == ttype && (p)->flags == F_LESSTHAN2) \
166 ++((p)->sec_datap_un._t_data.tv_sec);
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 */
174int
175f_amin(PLAN *plan, FTSENT *entry)
176{
177 extern time_t now;
178
179 COMPARE((now - entry->fts_statp->st_atime +{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_atim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_atim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_atim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
180 60 - 1) / 60, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_atim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_atim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_atim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
;
181}
182
183PLAN *
184c_amin(char *arg, char ***ignored, int unused)
185{
186 PLAN *new;
187
188 ftsoptions &= ~FTS_NOSTAT0x0008;
189
190 new = palloc(N_AMIN, f_amin);
191 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-amin", arg, NULL((void *)0));
192 TIME_CORRECT(new, N_AMIN)if ((new)->type == N_AMIN && (new)->flags == 2)
++((new)->p_un._t_data.tv_sec);
;
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 */
202int
203f_atime(PLAN *plan, FTSENT *entry)
204{
205
206 COMPARE((now - entry->fts_statp->st_atime +{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_atim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_atim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_atim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
207 SECSPERDAY - 1) / SECSPERDAY, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_atim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_atim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_atim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
;
208}
209
210PLAN *
211c_atime(char *arg, char ***ignored, int unused)
212{
213 PLAN *new;
214
215 ftsoptions &= ~FTS_NOSTAT0x0008;
216
217 new = palloc(N_ATIME, f_atime);
218 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-atime", arg, NULL((void *)0));
219 TIME_CORRECT(new, N_ATIME)if ((new)->type == N_ATIME && (new)->flags == 2
) ++((new)->p_un._t_data.tv_sec);
;
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 */
229int
230f_cmin(PLAN *plan, FTSENT *entry)
231{
232 extern time_t now;
233
234 COMPARE((now - entry->fts_statp->st_ctime +{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_ctim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_ctim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_ctim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
235 60 - 1) / 60, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_ctim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_ctim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_ctim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
;
236}
237
238PLAN *
239c_cmin(char *arg, char ***ignored, int unused)
240{
241 PLAN *new;
242
243 ftsoptions &= ~FTS_NOSTAT0x0008;
244
245 new = palloc(N_CMIN, f_cmin);
246 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-cmin", arg, NULL((void *)0));
247 TIME_CORRECT(new, N_CMIN)if ((new)->type == N_CMIN && (new)->flags == 2)
++((new)->p_un._t_data.tv_sec);
;
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 */
257int
258f_ctime(PLAN *plan, FTSENT *entry)
259{
260
261 COMPARE((now - entry->fts_statp->st_ctime +{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_ctim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_ctim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_ctim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
262 SECSPERDAY - 1) / SECSPERDAY, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_ctim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_ctim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_ctim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
;
263}
264
265PLAN *
266c_ctime(char *arg, char ***ignored, int unused)
267{
268 PLAN *new;
269
270 ftsoptions &= ~FTS_NOSTAT0x0008;
271
272 new = palloc(N_CTIME, f_ctime);
273 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-ctime", arg, NULL((void *)0));
274 TIME_CORRECT(new, N_CTIME)if ((new)->type == N_CTIME && (new)->flags == 2
) ++((new)->p_un._t_data.tv_sec);
;
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 */
285int
286f_always_true(PLAN *plan, FTSENT *entry)
287{
288 return (1);
289}
290
291PLAN *
292c_depth(char *ignore, char ***ignored, int unused)
293{
294 isdepth = 1;
295
296 return (palloc(N_DEPTH, f_always_true));
297}
298
299/*
300 * -delete functions
301 */
302int
303f_delete(PLAN *plan, FTSENT *entry)
304{
305
306 /* can't delete these */
307 if (strcmp(entry->fts_accpath, ".") == 0 ||
308 strcmp(entry->fts_accpath, "..") == 0)
309 return 1;
310
311 /* sanity check */
312 if (isdepth == 0 || /* depth off */
313 (ftsoptions & FTS_NOSTAT0x0008)) /* not stat()ing */
314 errx(1, "-delete: insecure options got turned on");
315 if (!(ftsoptions & FTS_PHYSICAL0x0010) || /* physical off */
316 (ftsoptions & FTS_LOGICAL0x0002)) /* or finally, logical on */
317 errx(1, "-delete: forbidden when symlinks are followed");
318
319 /* Potentially unsafe - do not accept relative paths whatsoever */
320 if (entry->fts_level > FTS_ROOTLEVEL0 &&
321 strchr(entry->fts_accpath, '/') != NULL((void *)0))
322 errx(1, "-delete: %s: relative path potentially not safe",
323 entry->fts_accpath);
324#if 0
325 /* Turn off user immutable bits if running as root */
326 if ((entry->fts_statp->st_flags & (UF_APPEND0x00000004|UF_IMMUTABLE0x00000002)) &&
327 !(entry->fts_statp->st_flags & (SF_APPEND0x00040000|SF_IMMUTABLE0x00020000)) &&
328 geteuid() == 0)
329 lchflags(entry->fts_accpath,
330 entry->fts_statp->st_flags &= ~(UF_APPEND0x00000004|UF_IMMUTABLE0x00000002));
331#endif
332 /* rmdir directories, unlink everything else */
333 if (S_ISDIR(entry->fts_statp->st_mode)((entry->fts_statp->st_mode & 0170000) == 0040000)) {
334 if (rmdir(entry->fts_accpath) == -1 && errno(*__errno()) != ENOTEMPTY66)
335 warn("-delete: rmdir(%s)", entry->fts_path);
336 } else {
337 if (unlink(entry->fts_accpath) == -1)
338 warn("-delete: unlink(%s)", entry->fts_path);
339
340 }
341
342 return 1;
343}
344
345PLAN *
346c_delete(char *ignore, char ***ignored, int unused)
347{
348 ftsoptions &= ~FTS_NOSTAT0x0008;
349 isoutput = 1;
350 isdelete = 1;
351 isdepth = 1;
352
353 return (palloc(N_DELETE, f_delete));
354}
355
356/*
357 * -empty functions --
358 *
359 * True if the file or directory is empty
360 */
361int
362f_empty(PLAN *plan, FTSENT *entry)
363{
364 if (S_ISREG(entry->fts_statp->st_mode)((entry->fts_statp->st_mode & 0170000) == 0100000) && entry->fts_statp->st_size == 0)
365 return (1);
366 if (S_ISDIR(entry->fts_statp->st_mode)((entry->fts_statp->st_mode & 0170000) == 0040000)) {
367 struct dirent *dp;
368 int empty;
369 DIR *dir;
370
371 empty = 1;
372 dir = opendir(entry->fts_accpath);
373 if (dir == NULL((void *)0))
374 return (0);
375 for (dp = readdir(dir); dp; dp = readdir(dir))
376 if (dp->d_name[0] != '.' ||
377 (dp->d_name[1] != '\0' &&
378 (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
379 empty = 0;
380 break;
381 }
382 closedir(dir);
383 return (empty);
384 }
385 return (0);
386}
387
388PLAN *
389c_empty(char *ignore, char ***ignored, int unused)
390{
391 ftsoptions &= ~FTS_NOSTAT0x0008;
392
393 return (palloc(N_EMPTY, f_empty));
394}
395
396/*
397 * [-exec | -ok] utility [arg ... ] ; functions --
398 * [-exec | -ok] utility [arg ... ] {} + functions --
399 *
400 * If the end of the primary expression is delimited by a
401 * semicolon: true if the executed utility returns a zero value
402 * as exit status. If "{}" occurs anywhere, it gets replaced by
403 * the current pathname.
404 *
405 * If the end of the primary expression is delimited by a plus
406 * sign: always true. Pathnames for which the primary is
407 * evaluated shall be aggregated into sets. The utility will be
408 * executed once per set, with "{}" replaced by the entire set of
409 * pathnames (as if xargs). "{}" must appear last.
410 *
411 * The current directory for the execution of utility is the same
412 * as the current directory when the find utility was started.
413 *
414 * The primary -ok is different in that it requests affirmation
415 * of the user before executing the utility.
416 */
417int
418f_exec(PLAN *plan, FTSENT *entry)
419{
420 int cnt, l;
421 pid_t pid;
422 int status;
423
424 if (plan->flags & F_PLUSSET2) {
425 /*
426 * Confirm sufficient buffer space, then copy the path
427 * to the buffer.
428 */
429 l = strlen(entry->fts_path);
430 if (plan->ep_pp_un.ex._ep_p + l < plan->ep_ebpp_un.ex._ep_ebp) {
431 plan->ep_bxpp_un.ex._ep_bxp[plan->ep_nargp_un.ex._ep_narg++] = plan->ep_pp_un.ex._ep_p;
432 strlcpy(plan->ep_pp_un.ex._ep_p, entry->fts_path, l + 1);
433 plan->ep_pp_un.ex._ep_p += l + 1;
434
435 if (plan->ep_nargp_un.ex._ep_narg == plan->ep_maxargsp_un.ex._ep_maxargs)
436 run_f_exec(plan);
437 } else {
438 /*
439 * Without sufficient space to copy in the next
440 * argument, run the command to empty out the
441 * buffer before re-attepting the copy.
442 */
443 run_f_exec(plan);
444 if (plan->ep_pp_un.ex._ep_p + l < plan->ep_ebpp_un.ex._ep_ebp) {
445 plan->ep_bxpp_un.ex._ep_bxp[plan->ep_nargp_un.ex._ep_narg++] = plan->ep_pp_un.ex._ep_p;
446 strlcpy(plan->ep_pp_un.ex._ep_p, entry->fts_path, l + 1);
447 plan->ep_pp_un.ex._ep_p += l + 1;
448 } else
449 errx(1, "insufficient space for argument");
450 }
451 return (1);
452 } else {
453 for (cnt = 0; plan->e_argvp_un.ex._e_argv[cnt]; ++cnt)
454 if (plan->e_lenp_un.ex._e_len[cnt])
455 brace_subst(plan->e_origp_un.ex._e_orig[cnt],
456 &plan->e_argvp_un.ex._e_argv[cnt],
457 entry->fts_path,
458 plan->e_lenp_un.ex._e_len[cnt]);
459 if (plan->flags & F_NEEDOK1 && !queryuser(plan->e_argvp_un.ex._e_argv))
460 return (0);
461
462 /* don't mix output of command with find output */
463 fflush(stdout(&__sF[1]));
464 fflush(stderr(&__sF[2]));
465
466 switch (pid = vfork()) {
467 case -1:
468 err(1, "fork");
469 /* NOTREACHED */
470 case 0:
471 if (fchdir(dotfd)) {
472 warn("chdir");
473 _exit(1);
474 }
475 execvp(plan->e_argvp_un.ex._e_argv[0], plan->e_argvp_un.ex._e_argv);
476 warn("%s", plan->e_argvp_un.ex._e_argv[0]);
477 _exit(1);
478 }
479 pid = waitpid(pid, &status, 0);
480 return (pid != -1 && WIFEXITED(status)(((status) & 0177) == 0) && !WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff));
481 }
482}
483
484static void
485run_f_exec(PLAN *plan)
486{
487 pid_t pid;
488 int rval, status;
489
490 /* Ensure arg list is null terminated. */
491 plan->ep_bxpp_un.ex._ep_bxp[plan->ep_nargp_un.ex._ep_narg] = NULL((void *)0);
492
493 /* Don't mix output of command with find output. */
494 fflush(stdout(&__sF[1]));
495 fflush(stderr(&__sF[2]));
496
497 switch (pid = vfork()) {
498 case -1:
499 err(1, "vfork");
500 /* NOTREACHED */
501 case 0:
502 if (fchdir(dotfd)) {
503 warn("chdir");
504 _exit(1);
505 }
506 execvp(plan->e_argvp_un.ex._e_argv[0], plan->e_argvp_un.ex._e_argv);
507 warn("%s", plan->e_argvp_un.ex._e_argv[0]);
508 _exit(1);
509 }
510
511 /* Clear out the argument list. */
512 plan->ep_nargp_un.ex._ep_narg = 0;
513 plan->ep_bxpp_un.ex._ep_bxp[plan->ep_nargp_un.ex._ep_narg] = NULL((void *)0);
514 /* As well as the argument buffer. */
515 plan->ep_pp_un.ex._ep_p = plan->ep_bbpp_un.ex._ep_bbp;
516 *plan->ep_pp_un.ex._ep_p = '\0';
517
518 pid = waitpid(pid, &status, 0);
519 if (WIFEXITED(status)(((status) & 0177) == 0))
520 rval = WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff);
521 else
522 rval = -1;
523
524 /*
525 * If we have a non-zero exit status, preserve it so find(1) can
526 * later exit with it.
527 */
528 if (rval)
529 plan->ep_rvalp_un.ex._ep_rval = rval;
530}
531
532/*
533 * c_exec --
534 * build three parallel arrays, one with pointers to the strings passed
535 * on the command line, one with (possibly duplicated) pointers to the
536 * argv array, and one with integer values that are lengths of the
537 * strings, but also flags meaning that the string has to be massaged.
538 *
539 * If -exec ... {} +, use only the first array, but make it large
540 * enough to hold 5000 args (cf. src/usr.bin/xargs/xargs.c for a
541 * discussion), and then allocate space for args.
542 */
543PLAN *
544c_exec(char *unused, char ***argvp, int isok)
545{
546 PLAN *new; /* node returned */
547 int cnt, brace, lastbrace;
548 char **argv, **ap, *p;
549
550 /* make sure the current directory is readable */
551 if (dotfd == -1)
552 errx(1, "%s: cannot open \".\"", isok ? "-ok" : "-exec");
553
554 isoutput = 1;
555
556 new = palloc(N_EXEC, f_exec);
557 if (isok)
558 new->flags |= F_NEEDOK1;
559
560 /*
561 * Terminate if we encounter an arg exactly equal to ";", or an
562 * arg exactly equal to "+" following an arg exactly equal to
563 * "{}".
564 */
565 for (ap = argv = *argvp, brace = 0;; ++ap) {
566 if (!*ap)
567 errx(1, "%s: no terminating \";\" or \"{} +\"",
568 isok ? "-ok" : "-exec");
569 lastbrace = brace;
570 brace = 0;
571 if (strcmp(*ap, "{}") == 0)
572 brace = 1;
573 if (strcmp(*ap, ";") == 0)
574 break;
575 if (strcmp(*ap, "+") == 0 && lastbrace) {
576 new->flags |= F_PLUSSET2;
577 break;
578 }
579 }
580
581
582 /*
583 * POSIX says -ok ... {} + "need not be supported," and it does
584 * not make much sense anyway.
585 */
586 if (new->flags & F_NEEDOK1 && new->flags & F_PLUSSET2)
587 errx(1, "-ok: terminating \"+\" not permitted.");
588
589 if (new->flags & F_PLUSSET2) {
590 long arg_max;
591 extern char **environ;
592 char **ep;
593 u_int c, bufsize;
594
595 cnt = ap - *argvp - 1; /* units are words */
596 new->ep_maxargsp_un.ex._ep_maxargs = 5000;
597 new->e_argvp_un.ex._e_argv = ereallocarray(NULL((void *)0),
598 (size_t)(cnt + new->ep_maxargsp_un.ex._ep_maxargs), sizeof(char **));
599
600 /* We start stuffing arguments after the user's last one. */
601 new->ep_bxpp_un.ex._ep_bxp = &new->e_argvp_un.ex._e_argv[cnt];
602 new->ep_nargp_un.ex._ep_narg = 0;
603
604 /*
605 * Compute the maximum space we can use for arguments
606 * passed to execve(2).
607 */
608 arg_max = sysconf(_SC_ARG_MAX1);
609 if (arg_max == -1)
610 err(1, "-exec: sysconf(_SC_ARG_MAX) failed");
611 for (ep = environ; *ep != NULL((void *)0); ep++) {
612 /* 1 byte for each '\0' */
613 arg_max -= strlen(*ep) + 1 + sizeof(*ep);
614 }
615
616 /*
617 * Count up the space of the user's arguments, and
618 * subtract that from what we allocate.
619 */
620 for (argv = *argvp, c = 0, cnt = 0;
621 argv < ap;
622 ++argv, ++cnt) {
623 c += strlen(*argv) + 1;
624 new->e_argvp_un.ex._e_argv[cnt] = *argv;
625 }
626 if (arg_max < 4 * 1024 + c)
627 errx(1, "-exec: no space left to run child command");
628 bufsize = arg_max - 4 * 1024 - c;
629
630 /*
631 * Allocate, and then initialize current, base, and
632 * end pointers.
633 */
634 new->ep_pp_un.ex._ep_p = new->ep_bbpp_un.ex._ep_bbp = malloc(bufsize + 1);
635 new->ep_ebpp_un.ex._ep_ebp = new->ep_bbpp_un.ex._ep_bbp + bufsize - 1;
636 new->ep_rvalp_un.ex._ep_rval = 0;
637 } else { /* !F_PLUSSET */
638 cnt = ap - *argvp + 1;
639 new->e_argvp_un.ex._e_argv = ereallocarray(NULL((void *)0), cnt, sizeof(char *));
640 new->e_origp_un.ex._e_orig = ereallocarray(NULL((void *)0), cnt, sizeof(char *));
641 new->e_lenp_un.ex._e_len = ereallocarray(NULL((void *)0), cnt, sizeof(int));
642
643 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
644 new->e_origp_un.ex._e_orig[cnt] = *argv;
645 for (p = *argv; *p; ++p)
646 if (p[0] == '{' && p[1] == '}') {
647 new->e_argvp_un.ex._e_argv[cnt] =
648 emalloc((u_int)PATH_MAX1024);
649 new->e_lenp_un.ex._e_len[cnt] = PATH_MAX1024;
650 break;
651 }
652 if (!*p) {
653 new->e_argvp_un.ex._e_argv[cnt] = *argv;
654 new->e_lenp_un.ex._e_len[cnt] = 0;
655 }
656 }
657 new->e_origp_un.ex._e_orig[cnt] = NULL((void *)0);
658 }
659
660 new->e_argvp_un.ex._e_argv[cnt] = NULL((void *)0);
661 *argvp = argv + 1;
662 return (new);
663}
664
665/*
666 * -execdir utility [arg ... ] ; functions --
667 *
668 * True if the executed utility returns a zero value as exit status.
669 * The end of the primary expression is delimited by a semicolon. If
670 * "{}" occurs anywhere, it gets replaced by the unqualified pathname.
671 * The current directory for the execution of utility is the same as
672 * the directory where the file lives.
673 */
674int
675f_execdir(PLAN *plan, FTSENT *entry)
676{
677 int cnt;
678 pid_t pid;
679 int status, fd;
680 char base[PATH_MAX1024];
681
682 /* fts(3) does not chdir for the root level so we do it ourselves. */
683 if (entry->fts_level == FTS_ROOTLEVEL0) {
684 if ((fd = open(".", O_RDONLY0x0000)) == -1) {
685 warn("cannot open \".\"");
686 return (0);
687 }
688 if (chdir(entry->fts_accpath)) {
689 (void) close(fd);
690 return (0);
691 }
692 }
693
694 /* Substitute basename(path) for {} since cwd is it's parent dir */
695 (void)strncpy(base, basename(entry->fts_path), sizeof(base) - 1);
696 base[sizeof(base) - 1] = '\0';
697 for (cnt = 0; plan->e_argvp_un.ex._e_argv[cnt]; ++cnt)
698 if (plan->e_lenp_un.ex._e_len[cnt])
699 brace_subst(plan->e_origp_un.ex._e_orig[cnt], &plan->e_argvp_un.ex._e_argv[cnt],
700 base, plan->e_lenp_un.ex._e_len[cnt]);
701
702 /* don't mix output of command with find output */
703 fflush(stdout(&__sF[1]));
704 fflush(stderr(&__sF[2]));
705
706 switch (pid = vfork()) {
707 case -1:
708 err(1, "fork");
709 /* NOTREACHED */
710 case 0:
711 execvp(plan->e_argvp_un.ex._e_argv[0], plan->e_argvp_un.ex._e_argv);
712 warn("%s", plan->e_argvp_un.ex._e_argv[0]);
713 _exit(1);
714 }
715
716 /* Undo the above... */
717 if (entry->fts_level == FTS_ROOTLEVEL0) {
718 if (fchdir(fd) == -1) {
719 warn("unable to chdir back to starting directory");
720 (void) close(fd);
721 return (0);
722 }
723 (void) close(fd);
724 }
725
726 pid = waitpid(pid, &status, 0);
727 return (pid != -1 && WIFEXITED(status)(((status) & 0177) == 0) && !WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff));
728}
729
730/*
731 * c_execdir --
732 * build three parallel arrays, one with pointers to the strings passed
733 * on the command line, one with (possibly duplicated) pointers to the
734 * argv array, and one with integer values that are lengths of the
735 * strings, but also flags meaning that the string has to be massaged.
736 */
737PLAN *
738c_execdir(char *ignored, char ***argvp, int unused)
739{
740 PLAN *new; /* node returned */
741 int cnt;
742 char **argv, **ap, *p;
743
744 ftsoptions &= ~FTS_NOSTAT0x0008;
745 isoutput = 1;
746
747 new = palloc(N_EXECDIR, f_execdir);
748
749 for (ap = argv = *argvp;; ++ap) {
Although the value stored to 'argv' is used in the enclosing expression, the value is never actually read from 'argv'
750 if (!*ap)
751 errx(1,
752 "-execdir: no terminating \";\"");
753 if (**ap == ';')
754 break;
755 }
756
757 cnt = ap - *argvp + 1;
758 new->e_argvp_un.ex._e_argv = ereallocarray(NULL((void *)0), cnt, sizeof(char *));
759 new->e_origp_un.ex._e_orig = ereallocarray(NULL((void *)0), cnt, sizeof(char *));
760 new->e_lenp_un.ex._e_len = ereallocarray(NULL((void *)0), cnt, sizeof(int));
761
762 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
763 new->e_origp_un.ex._e_orig[cnt] = *argv;
764 for (p = *argv; *p; ++p)
765 if (p[0] == '{' && p[1] == '}') {
766 new->e_argvp_un.ex._e_argv[cnt] = emalloc((u_int)PATH_MAX1024);
767 new->e_lenp_un.ex._e_len[cnt] = PATH_MAX1024;
768 break;
769 }
770 if (!*p) {
771 new->e_argvp_un.ex._e_argv[cnt] = *argv;
772 new->e_lenp_un.ex._e_len[cnt] = 0;
773 }
774 }
775 new->e_argvp_un.ex._e_argv[cnt] = new->e_origp_un.ex._e_orig[cnt] = NULL((void *)0);
776
777 *argvp = argv + 1;
778 return (new);
779}
780
781/*
782 * -flags functions --
783 *
784 * The flags argument is used to represent file flags bits.
785 */
786int
787f_flags(PLAN *plan, FTSENT *entry)
788{
789 u_int flags;
790
791 flags = entry->fts_statp->st_flags &
792 (UF_NODUMP0x00000001 | UF_IMMUTABLE0x00000002 | UF_APPEND0x00000004 | UF_OPAQUE0x00000008 |
793 SF_ARCHIVED0x00010000 | SF_IMMUTABLE0x00020000 | SF_APPEND0x00040000);
794 if (plan->flags == F_ATLEAST1)
795 /* note that plan->fl_flags always is a subset of
796 plan->fl_mask */
797 return ((flags & plan->fl_maskp_un.fl._f_mask) == plan->fl_flagsp_un.fl._f_flags);
798 else
799 return (flags == plan->fl_flagsp_un.fl._f_flags);
800 /* NOTREACHED */
801}
802
803PLAN *
804c_flags(char *flags_str, char ***ignored, int unused)
805{
806 PLAN *new;
807 u_int32_t flags, notflags;
808
809 ftsoptions &= ~FTS_NOSTAT0x0008;
810
811 new = palloc(N_FLAGS, f_flags);
812
813 if (*flags_str == '-') {
814 new->flags = F_ATLEAST1;
815 ++flags_str;
816 }
817
818 if (strtofflags(&flags_str, &flags, &notflags) == 1)
819 errx(1, "-flags: %s: illegal flags string", flags_str);
820
821 new->fl_flagsp_un.fl._f_flags = flags;
822 new->fl_maskp_un.fl._f_mask = flags | notflags;
823 return (new);
824}
825
826/*
827 * -follow functions --
828 *
829 * Always true, causes symbolic links to be followed on a global
830 * basis.
831 */
832PLAN *
833c_follow(char *ignore, char ***ignored, int unused)
834{
835 ftsoptions &= ~FTS_PHYSICAL0x0010;
836 ftsoptions |= FTS_LOGICAL0x0002;
837
838 return (palloc(N_FOLLOW, f_always_true));
839}
840
841/*
842 * -fstype functions --
843 *
844 * True if the file is of a certain type.
845 */
846int
847f_fstype(PLAN *plan, FTSENT *entry)
848{
849 static dev_t curdev; /* need a guaranteed illegal dev value */
850 static int first = 1;
851 struct statfs sb;
852 static short val;
853 static char fstype[MFSNAMELEN16];
854 char *p, save[2];
855
856 /* Only check when we cross mount point. */
857 if (first || curdev != entry->fts_statp->st_dev) {
858 curdev = entry->fts_statp->st_dev;
859
860 /*
861 * Statfs follows symlinks; find wants the link's file system,
862 * not where it points.
863 */
864 if (entry->fts_info == FTS_SL12 ||
865 entry->fts_info == FTS_SLNONE13) {
866 if ((p = strrchr(entry->fts_accpath, '/')))
867 ++p;
868 else
869 p = entry->fts_accpath;
870 save[0] = p[0];
871 p[0] = '.';
872 save[1] = p[1];
873 p[1] = '\0';
874
875 } else
876 p = NULL((void *)0);
877
878 if (statfs(entry->fts_accpath, &sb))
879 err(1, "%s", entry->fts_accpath);
880
881 if (p) {
882 p[0] = save[0];
883 p[1] = save[1];
884 }
885
886 first = 0;
887
888 /*
889 * Further tests may need both of these values, so
890 * always copy both of them.
891 */
892 val = sb.f_flags;
893 strncpy(fstype, sb.f_fstypename, MFSNAMELEN16);
894 }
895 switch (plan->flags) {
896 case F_MTFLAG1:
897 return (val & plan->mt_datap_un._mt_data);
898 case F_MTTYPE2:
899 return (strncmp(fstype, plan->c_datap_un._c_data, MFSNAMELEN16) == 0);
900 default:
901 abort();
902 }
903}
904
905PLAN *
906c_fstype(char *arg, char ***ignored, int unused)
907{
908 PLAN *new;
909
910 ftsoptions &= ~FTS_NOSTAT0x0008;
911
912 new = palloc(N_FSTYPE, f_fstype);
913 switch (*arg) {
914 case 'l':
915 if (!strcmp(arg, "local")) {
916 new->flags = F_MTFLAG1;
917 new->mt_datap_un._mt_data = MNT_LOCAL0x00001000;
918 return (new);
919 }
920 break;
921 case 'r':
922 if (!strcmp(arg, "rdonly")) {
923 new->flags = F_MTFLAG1;
924 new->mt_datap_un._mt_data = MNT_RDONLY0x00000001;
925 return (new);
926 }
927 break;
928 }
929
930 new->flags = F_MTTYPE2;
931 new->c_datap_un._c_data = arg;
932 return (new);
933}
934
935/*
936 * -group gname functions --
937 *
938 * True if the file belongs to the group gname. If gname is numeric and
939 * an equivalent of the getgrnam() function does not return a valid group
940 * name, gname is taken as a group ID.
941 */
942int
943f_group(PLAN *plan, FTSENT *entry)
944{
945 return (entry->fts_statp->st_gid == plan->g_datap_un._g_data);
946}
947
948PLAN *
949c_group(char *gname, char ***ignored, int unused)
950{
951 PLAN *new;
952 gid_t gid;
953
954 ftsoptions &= ~FTS_NOSTAT0x0008;
955
956 if (gid_from_group(gname, &gid) == -1) {
957 const char *errstr;
958
959 gid = strtonum(gname, 0, GID_MAX(2147483647 *2U +1U), &errstr);
960 if (errstr)
961 errx(1, "-group: %s: no such group", gname);
962 }
963
964 new = palloc(N_GROUP, f_group);
965 new->g_datap_un._g_data = gid;
966 return (new);
967}
968
969/*
970 * -inum n functions --
971 *
972 * True if the file has inode # n.
973 */
974int
975f_inum(PLAN *plan, FTSENT *entry)
976{
977 COMPARE(entry->fts_statp->st_ino, plan->i_data){ switch (plan->flags) { case 1: return (entry->fts_statp
->st_ino == plan->p_un._i_data); case 2: return (entry->
fts_statp->st_ino < plan->p_un._i_data); case 3: return
(entry->fts_statp->st_ino > plan->p_un._i_data);
default: abort(); } }
;
978}
979
980PLAN *
981c_inum(char *arg, char ***ignored, int unused)
982{
983 long long inum;
984 PLAN *new;
985
986 ftsoptions &= ~FTS_NOSTAT0x0008;
987
988 new = palloc(N_INUM, f_inum);
989 inum = find_parsenum(new, "-inum", arg, NULL((void *)0));
990 if (inum != (ino_t)inum)
991 errx(1, "-inum: %s: number too great", arg);
992 new->i_datap_un._i_data = inum;
993 return (new);
994}
995
996/*
997 * -links n functions --
998 *
999 * True if the file has n links.
1000 */
1001int
1002f_links(PLAN *plan, FTSENT *entry)
1003{
1004 COMPARE(entry->fts_statp->st_nlink, plan->l_data){ switch (plan->flags) { case 1: return (entry->fts_statp
->st_nlink == plan->p_un._l_data); case 2: return (entry
->fts_statp->st_nlink < plan->p_un._l_data); case
3: return (entry->fts_statp->st_nlink > plan->p_un
._l_data); default: abort(); } }
;
1005}
1006
1007PLAN *
1008c_links(char *arg, char ***ignored, int unused)
1009{
1010 PLAN *new;
1011 long long nlink;
1012
1013 ftsoptions &= ~FTS_NOSTAT0x0008;
1014
1015 new = palloc(N_LINKS, f_links);
1016 nlink = find_parsenum(new, "-links", arg, NULL((void *)0));
1017 if (nlink != (nlink_t)nlink)
1018 errx(1, "-links: %s: number too great", arg);
1019 new->l_datap_un._l_data = nlink;
1020 return (new);
1021}
1022
1023/*
1024 * -ls functions --
1025 *
1026 * Always true - prints the current entry to stdout in "ls" format.
1027 */
1028int
1029f_ls(PLAN *plan, FTSENT *entry)
1030{
1031 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
1032 return (1);
1033}
1034
1035PLAN *
1036c_ls(char *ignore, char ***ignored, int unused)
1037{
1038 ftsoptions &= ~FTS_NOSTAT0x0008;
1039 isoutput = 1;
1040
1041 return (palloc(N_LS, f_ls));
1042}
1043
1044/*
1045 * - maxdepth n functions --
1046 *
1047 * True if the current search depth is less than or equal to the
1048 * maximum depth specified
1049 */
1050int
1051f_maxdepth(PLAN *plan, FTSENT *entry)
1052{
1053
1054 if (entry->fts_level >= plan->max_datap_un._max_data)
1055 fts_set(tree, entry, FTS_SKIP4);
1056 return (entry->fts_level <= plan->max_datap_un._max_data);
1057}
1058
1059PLAN *
1060c_maxdepth(char *arg, char ***ignored, int unused)
1061{
1062 PLAN *new;
1063 const char *errstr = NULL((void *)0);
1064
1065 new = palloc(N_MAXDEPTH, f_maxdepth);
1066 new->max_datap_un._max_data = strtonum(arg, 0, FTS_MAXLEVEL0x7fffffff, &errstr);
1067 if (errstr)
1068 errx(1, "%s: maxdepth value %s", arg, errstr);
1069 return (new);
1070}
1071
1072/*
1073 * - mindepth n functions --
1074 *
1075 * True if the current search depth is greater than or equal to the
1076 * minimum depth specified
1077 */
1078int
1079f_mindepth(PLAN *plan, FTSENT *entry)
1080{
1081
1082 return (entry->fts_level >= plan->min_datap_un._min_data);
1083}
1084
1085PLAN *
1086c_mindepth(char *arg, char ***ignored, int unused)
1087{
1088 PLAN *new;
1089 const char *errstr = NULL((void *)0);
1090
1091 new = palloc(N_MINDEPTH, f_mindepth);
1092 new->min_datap_un._min_data = strtonum(arg, 0, INT_MAX2147483647, &errstr);
1093 if (errstr)
1094 errx(1, "-mindepth: %s: value %s", arg, errstr);
1095 return (new);
1096}
1097
1098/*
1099 * -mtime n functions --
1100 *
1101 * True if the difference between the file modification time and the
1102 * current time is n 24 hour periods.
1103 */
1104int
1105f_mtime(PLAN *plan, FTSENT *entry)
1106{
1107
1108 COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_mtim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_mtim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_mtim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
1109 SECSPERDAY, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_mtim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_mtim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_mtim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
;
1110}
1111
1112PLAN *
1113c_mtime(char *arg, char ***ignored, int unused)
1114{
1115 PLAN *new;
1116
1117 ftsoptions &= ~FTS_NOSTAT0x0008;
1118
1119 new = palloc(N_MTIME, f_mtime);
1120 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-mtime", arg, NULL((void *)0));
1121 TIME_CORRECT(new, N_MTIME)if ((new)->type == N_MTIME && (new)->flags == 2
) ++((new)->p_un._t_data.tv_sec);
;
1122 return (new);
1123}
1124
1125/*
1126 * -mmin n functions --
1127 *
1128 * True if the difference between the file modification time and the
1129 * current time is n min periods.
1130 */
1131int
1132f_mmin(PLAN *plan, FTSENT *entry)
1133{
1134 extern time_t now;
1135
1136 COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_mtim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_mtim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_mtim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
1137 60, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_mtim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_mtim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_mtim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
;
1138}
1139
1140PLAN *
1141c_mmin(char *arg, char ***ignored, int unused)
1142{
1143 PLAN *new;
1144
1145 ftsoptions &= ~FTS_NOSTAT0x0008;
1146
1147 new = palloc(N_MMIN, f_mmin);
1148 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-mmin", arg, NULL((void *)0));
1149 TIME_CORRECT(new, N_MMIN)if ((new)->type == N_MMIN && (new)->flags == 2)
++((new)->p_un._t_data.tv_sec);
;
1150 return (new);
1151}
1152
1153/*
1154 * -name functions --
1155 *
1156 * True if the basename of the filename being examined
1157 * matches pattern using Pattern Matching Notation S3.14
1158 */
1159int
1160f_name(PLAN *plan, FTSENT *entry)
1161{
1162 return (!fnmatch(plan->c_datap_un._c_data, entry->fts_name, 0));
1163}
1164
1165PLAN *
1166c_name(char *pattern, char ***ignored, int unused)
1167{
1168 PLAN *new;
1169
1170 new = palloc(N_NAME, f_name);
1171 new->c_datap_un._c_data = pattern;
1172 return (new);
1173}
1174
1175/*
1176 * -iname functions --
1177 *
1178 * Similar to -name, but does case insensitive matching
1179 *
1180 */
1181int
1182f_iname(PLAN *plan, FTSENT *entry)
1183{
1184 return (!fnmatch(plan->c_datap_un._c_data, entry->fts_name, FNM_CASEFOLD0x10));
1185}
1186
1187PLAN *
1188c_iname(char *pattern, char ***ignored, int unused)
1189{
1190 PLAN *new;
1191
1192 new = palloc(N_INAME, f_iname);
1193 new->c_datap_un._c_data = pattern;
1194 return (new);
1195}
1196
1197/*
1198 * -newer file functions --
1199 *
1200 * True if the current file has been modified more recently
1201 * then the modification time of the file named by the pathname
1202 * file.
1203 */
1204int
1205f_newer(PLAN *plan, FTSENT *entry)
1206{
1207
1208 return (entry->fts_statp->st_mtimespecst_mtim.tv_sec > plan->t_datap_un._t_data.tv_sec ||
1209 (entry->fts_statp->st_mtimespecst_mtim.tv_sec == plan->t_datap_un._t_data.tv_sec &&
1210 entry->fts_statp->st_mtimespecst_mtim.tv_nsec > plan->t_datap_un._t_data.tv_nsec));
1211}
1212
1213PLAN *
1214c_newer(char *filename, char ***ignored, int unused)
1215{
1216 PLAN *new;
1217 struct stat sb;
1218
1219 ftsoptions &= ~FTS_NOSTAT0x0008;
1220
1221 if (stat(filename, &sb))
1222 err(1, "%s", filename);
1223 new = palloc(N_NEWER, f_newer);
1224 memcpy(&new->t_datap_un._t_data, &sb.st_mtimespecst_mtim, sizeof(struct timespec));
1225 return (new);
1226}
1227
1228/*
1229 * -anewer file functions --
1230 *
1231 * True if the current file has been accessed more recently
1232 * then the access time of the file named by the pathname
1233 * file.
1234 */
1235int
1236f_anewer(PLAN *plan, FTSENT *entry)
1237{
1238
1239 return (entry->fts_statp->st_atimespecst_atim.tv_sec > plan->t_datap_un._t_data.tv_sec ||
1240 (entry->fts_statp->st_atimespecst_atim.tv_sec == plan->t_datap_un._t_data.tv_sec &&
1241 entry->fts_statp->st_atimespecst_atim.tv_nsec > plan->t_datap_un._t_data.tv_nsec));
1242}
1243
1244PLAN *
1245c_anewer(char *filename, char ***ignored, int unused)
1246{
1247 PLAN *new;
1248 struct stat sb;
1249
1250 ftsoptions &= ~FTS_NOSTAT0x0008;
1251
1252 if (stat(filename, &sb))
1253 err(1, "%s", filename);
1254 new = palloc(N_NEWER, f_anewer);
1255 memcpy(&new->t_datap_un._t_data, &sb.st_atimespecst_atim, sizeof(struct timespec));
1256 return (new);
1257}
1258
1259/*
1260 * -cnewer file functions --
1261 *
1262 * True if the current file has been changed more recently
1263 * then the inode change time of the file named by the pathname
1264 * file.
1265 */
1266int
1267f_cnewer(PLAN *plan, FTSENT *entry)
1268{
1269
1270 return (entry->fts_statp->st_ctimespecst_ctim.tv_sec > plan->t_datap_un._t_data.tv_sec ||
1271 (entry->fts_statp->st_ctimespecst_ctim.tv_sec == plan->t_datap_un._t_data.tv_sec &&
1272 entry->fts_statp->st_ctimespecst_ctim.tv_nsec > plan->t_datap_un._t_data.tv_nsec));
1273}
1274
1275PLAN *
1276c_cnewer(char *filename, char ***ignored, int unused)
1277{
1278 PLAN *new;
1279 struct stat sb;
1280
1281 ftsoptions &= ~FTS_NOSTAT0x0008;
1282
1283 if (stat(filename, &sb))
1284 err(1, "%s", filename);
1285 new = palloc(N_NEWER, f_cnewer);
1286 memcpy(&new->t_datap_un._t_data, &sb.st_ctimespecst_ctim, sizeof(struct timespec));
1287 return (new);
1288}
1289
1290/*
1291 * -nogroup functions --
1292 *
1293 * True if file belongs to a user ID for which the equivalent
1294 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
1295 */
1296int
1297f_nogroup(PLAN *plan, FTSENT *entry)
1298{
1299 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
1300}
1301
1302PLAN *
1303c_nogroup(char *ignore, char ***ignored, int unused)
1304{
1305 ftsoptions &= ~FTS_NOSTAT0x0008;
1306
1307 return (palloc(N_NOGROUP, f_nogroup));
1308}
1309
1310/*
1311 * -nouser functions --
1312 *
1313 * True if file belongs to a user ID for which the equivalent
1314 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
1315 */
1316int
1317f_nouser(PLAN *plan, FTSENT *entry)
1318{
1319 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
1320}
1321
1322PLAN *
1323c_nouser(char *ignore, char ***ignored, int unused)
1324{
1325 ftsoptions &= ~FTS_NOSTAT0x0008;
1326
1327 return (palloc(N_NOUSER, f_nouser));
1328}
1329
1330/*
1331 * -path functions --
1332 *
1333 * True if the path of the filename being examined
1334 * matches pattern using Pattern Matching Notation S3.14
1335 */
1336int
1337f_path(PLAN *plan, FTSENT *entry)
1338{
1339 return (!fnmatch(plan->c_datap_un._c_data, entry->fts_path, 0));
1340}
1341
1342PLAN *
1343c_path(char *pattern, char ***ignored, int unused)
1344{
1345 PLAN *new;
1346
1347 new = palloc(N_NAME, f_path);
1348 new->c_datap_un._c_data = pattern;
1349 return (new);
1350}
1351
1352/*
1353 * -perm functions --
1354 *
1355 * The mode argument is used to represent file mode bits. If it starts
1356 * with a leading digit, it's treated as an octal mode, otherwise as a
1357 * symbolic mode.
1358 */
1359int
1360f_perm(PLAN *plan, FTSENT *entry)
1361{
1362 mode_t mode;
1363
1364 mode = entry->fts_statp->st_mode &
1365 (S_ISUID0004000|S_ISGID0002000|S_ISTXT0001000|S_IRWXU0000700|S_IRWXG0000070|S_IRWXO0000007);
1366 if (plan->flags == F_ATLEAST1)
1367 return ((plan->m_datap_un._m_data | mode) == mode);
1368 else
1369 return (mode == plan->m_datap_un._m_data);
1370 /* NOTREACHED */
1371}
1372
1373PLAN *
1374c_perm(char *perm, char ***ignored, int unused)
1375{
1376 PLAN *new;
1377 void *set;
1378
1379 ftsoptions &= ~FTS_NOSTAT0x0008;
1380
1381 new = palloc(N_PERM, f_perm);
1382
1383 if (*perm == '-') {
1384 new->flags = F_ATLEAST1;
1385 ++perm;
1386 }
1387
1388 if ((set = setmode(perm)) == NULL((void *)0))
1389 errx(1, "-perm: %s: illegal mode string", perm);
1390
1391 new->m_datap_un._m_data = getmode(set, 0);
1392 free(set);
1393 return (new);
1394}
1395
1396/*
1397 * -print functions --
1398 *
1399 * Always true, causes the current pathame to be written to
1400 * standard output.
1401 */
1402int
1403f_print(PLAN *plan, FTSENT *entry)
1404{
1405 (void)printf("%s\n", entry->fts_path);
1406 return(1);
1407}
1408
1409/* ARGSUSED */
1410int
1411f_print0(PLAN *plan, FTSENT *entry)
1412{
1413 (void)fputs(entry->fts_path, stdout(&__sF[1]));
1414 (void)fputc('\0', stdout(&__sF[1]));
1415 return(1);
1416}
1417
1418PLAN *
1419c_print(char *ignore, char ***ignored, int unused)
1420{
1421 isoutput = 1;
1422
1423 return(palloc(N_PRINT, f_print));
1424}
1425
1426PLAN *
1427c_print0(char *ignore, char ***ignored, int unused)
1428{
1429 isoutput = 1;
1430
1431 return(palloc(N_PRINT0, f_print0));
1432}
1433
1434/*
1435 * -prune functions --
1436 *
1437 * Prune a portion of the hierarchy.
1438 */
1439int
1440f_prune(PLAN *plan, FTSENT *entry)
1441{
1442
1443 if (fts_set(tree, entry, FTS_SKIP4))
1444 err(1, "%s", entry->fts_path);
1445 return (1);
1446}
1447
1448PLAN *
1449c_prune(char *ignore, char ***ignored, int unused)
1450{
1451 return (palloc(N_PRUNE, f_prune));
1452}
1453
1454/*
1455 * -size n[c] functions --
1456 *
1457 * True if the file size in bytes, divided by an implementation defined
1458 * value and rounded up to the next integer, is n. If n is followed by
1459 * a c, the size is in bytes.
1460 */
1461#define FIND_SIZE512 512
1462static int divsize = 1;
1463
1464int
1465f_size(PLAN *plan, FTSENT *entry)
1466{
1467 off_t size;
1468
1469 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE512 - 1) /
1470 FIND_SIZE512 : entry->fts_statp->st_size;
1471 COMPARE(size, plan->o_data){ switch (plan->flags) { case 1: return (size == plan->
p_un._o_data); case 2: return (size < plan->p_un._o_data
); case 3: return (size > plan->p_un._o_data); default:
abort(); } }
;
1472}
1473
1474PLAN *
1475c_size(char *arg, char ***ignored, int unused)
1476{
1477 PLAN *new;
1478 char endch;
1479
1480 ftsoptions &= ~FTS_NOSTAT0x0008;
1481
1482 new = palloc(N_SIZE, f_size);
1483 endch = 'c';
1484 new->o_datap_un._o_data = find_parsenum(new, "-size", arg, &endch);
1485 if (endch == 'c')
1486 divsize = 0;
1487 return (new);
1488}
1489
1490/*
1491 * -type c functions --
1492 *
1493 * True if the type of the file is c, where c is b, c, d, p, or f for
1494 * block special file, character special file, directory, FIFO, or
1495 * regular file, respectively.
1496 */
1497int
1498f_type(PLAN *plan, FTSENT *entry)
1499{
1500 return ((entry->fts_statp->st_mode & S_IFMT0170000) == plan->m_datap_un._m_data);
1501}
1502
1503PLAN *
1504c_type(char *typestring, char ***ignored, int unused)
1505{
1506 PLAN *new;
1507 mode_t mask;
1508
1509 ftsoptions &= ~FTS_NOSTAT0x0008;
1510
1511 switch (typestring[0]) {
1512 case 'b':
1513 mask = S_IFBLK0060000;
1514 break;
1515 case 'c':
1516 mask = S_IFCHR0020000;
1517 break;
1518 case 'd':
1519 mask = S_IFDIR0040000;
1520 break;
1521 case 'f':
1522 mask = S_IFREG0100000;
1523 break;
1524 case 'l':
1525 mask = S_IFLNK0120000;
1526 break;
1527 case 'p':
1528 mask = S_IFIFO0010000;
1529 break;
1530 case 's':
1531 mask = S_IFSOCK0140000;
1532 break;
1533 default:
1534 errx(1, "-type: %s: unknown type", typestring);
1535 }
1536
1537 new = palloc(N_TYPE, f_type);
1538 new->m_datap_un._m_data = mask;
1539 return (new);
1540}
1541
1542/*
1543 * -user uname functions --
1544 *
1545 * True if the file belongs to the user uname. If uname is numeric and
1546 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1547 * return a valid user name, uname is taken as a user ID.
1548 */
1549int
1550f_user(PLAN *plan, FTSENT *entry)
1551{
1552 return (entry->fts_statp->st_uid == plan->u_datap_un._u_data);
1553}
1554
1555PLAN *
1556c_user(char *username, char ***ignored, int unused)
1557{
1558 PLAN *new;
1559 uid_t uid;
1560
1561 ftsoptions &= ~FTS_NOSTAT0x0008;
1562
1563 if (uid_from_user(username, &uid) == -1) {
1564 const char *errstr;
1565
1566 uid = strtonum(username, 0, UID_MAX(2147483647 *2U +1U), &errstr);
1567 if (errstr)
1568 errx(1, "-user: %s: no such user", username);
1569 }
1570
1571 new = palloc(N_USER, f_user);
1572 new->u_datap_un._u_data = uid;
1573 return (new);
1574}
1575
1576/*
1577 * -xdev functions --
1578 *
1579 * Always true, causes find not to decend past directories that have a
1580 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1581 */
1582PLAN *
1583c_xdev(char *ignore, char ***ignored, int unused)
1584{
1585 ftsoptions |= FTS_XDEV0x0040;
1586
1587 return (palloc(N_XDEV, f_always_true));
1588}
1589
1590/*
1591 * ( expression ) functions --
1592 *
1593 * True if expression is true.
1594 */
1595int
1596f_expr(PLAN *plan, FTSENT *entry)
1597{
1598 PLAN *p;
1599 int state;
1600
1601 for (p = plan->p_datap_un._p_data[0];
1602 p && (state = (p->eval)(p, entry)); p = p->next);
1603 return (state);
1604}
1605
1606/*
1607 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1608 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1609 * to a N_EXPR node containing the expression and the ')' node is discarded.
1610 */
1611PLAN *
1612c_openparen(char *ignore, char ***ignored, int unused)
1613{
1614 return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1));
1615}
1616
1617PLAN *
1618c_closeparen(char *ignore, char ***ignored, int unused)
1619{
1620 return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1));
1621}
1622
1623/*
1624 * ! expression functions --
1625 *
1626 * Negation of a primary; the unary NOT operator.
1627 */
1628int
1629f_not(PLAN *plan, FTSENT *entry)
1630{
1631 PLAN *p;
1632 int state;
1633
1634 for (p = plan->p_datap_un._p_data[0];
1635 p && (state = (p->eval)(p, entry)); p = p->next);
1636 return (!state);
1637}
1638
1639PLAN *
1640c_not(char *ignore, char ***ignored, int unused)
1641{
1642 return (palloc(N_NOT, f_not));
1643}
1644
1645/*
1646 * expression -o expression functions --
1647 *
1648 * Alternation of primaries; the OR operator. The second expression is
1649 * not evaluated if the first expression is true.
1650 */
1651int
1652f_or(PLAN *plan, FTSENT *entry)
1653{
1654 PLAN *p;
1655 int state;
1656
1657 for (p = plan->p_datap_un._p_data[0];
1658 p && (state = (p->eval)(p, entry)); p = p->next);
1659
1660 if (state)
1661 return (1);
1662
1663 for (p = plan->p_datap_un._p_data[1];
1664 p && (state = (p->eval)(p, entry)); p = p->next);
1665 return (state);
1666}
1667
1668PLAN *
1669c_or(char *ignore, char ***ignored, int unused)
1670{
1671 return (palloc(N_OR, f_or));
1672}
1673
1674
1675/*
1676 * plan_cleanup --
1677 * Check and see if the specified plan has any residual state,
1678 * and if so, clean it up as appropriate.
1679 *
1680 * At the moment, only N_EXEC has state. Two kinds: 1)
1681 * lists of files to feed to subprocesses 2) State on exit
1682 * statusses of past subprocesses.
1683 */
1684/* ARGSUSED1 */
1685int
1686plan_cleanup(PLAN *plan, void *arg)
1687{
1688 if (plan->type==N_EXEC && plan->ep_nargp_un.ex._ep_narg)
1689 run_f_exec(plan);
1690
1691 return plan->ep_rvalp_un.ex._ep_rval; /* Passed save exit-status up chain */
1692}
1693
1694
1695static PLAN *
1696palloc(enum ntype t, int (*f)(PLAN *, FTSENT *))
1697{
1698 PLAN *new;
1699
1700 if ((new = calloc(1, sizeof(PLAN)))) {
1701 new->type = t;
1702 new->eval = f;
1703 return (new);
1704 }
1705 err(1, NULL((void *)0));
1706 /* NOTREACHED */
1707}