File: | src/usr.bin/find/function.c |
Warning: | line 466, column 17 Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process. Replace calls to vfork with calls to the safer 'posix_spawn' function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: function.c,v 1.55 2023/08/11 04:45:05 guenther 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 | |
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_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 | */ |
174 | int |
175 | f_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 | |
183 | PLAN * |
184 | c_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 | */ |
202 | int |
203 | f_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 | |
210 | PLAN * |
211 | c_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 | */ |
229 | int |
230 | f_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 | |
238 | PLAN * |
239 | c_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 | */ |
257 | int |
258 | f_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 | |
265 | PLAN * |
266 | c_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 | */ |
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 | * -delete functions |
301 | */ |
302 | int |
303 | f_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 | |
345 | PLAN * |
346 | c_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 | */ |
361 | int |
362 | f_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 | |
388 | PLAN * |
389 | c_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 | */ |
417 | int |
418 | f_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()) { |
Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process. Replace calls to vfork with calls to the safer 'posix_spawn' function | |
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 | |
484 | static void |
485 | run_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 | */ |
543 | PLAN * |
544 | c_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 | */ |
674 | int |
675 | f_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 | */ |
737 | PLAN * |
738 | c_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) { |
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 | */ |
786 | int |
787 | f_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 | |
803 | PLAN * |
804 | c_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, ¬flags) == 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 | */ |
832 | PLAN * |
833 | c_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 | */ |
846 | int |
847 | f_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 | |
905 | PLAN * |
906 | c_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 | */ |
942 | int |
943 | f_group(PLAN *plan, FTSENT *entry) |
944 | { |
945 | return (entry->fts_statp->st_gid == plan->g_datap_un._g_data); |
946 | } |
947 | |
948 | PLAN * |
949 | c_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_MAX0xffffffffU, &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 | */ |
974 | int |
975 | f_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 | |
980 | PLAN * |
981 | c_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 | */ |
1001 | int |
1002 | f_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 | |
1007 | PLAN * |
1008 | c_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 | */ |
1028 | int |
1029 | f_ls(PLAN *plan, FTSENT *entry) |
1030 | { |
1031 | printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); |
1032 | return (1); |
1033 | } |
1034 | |
1035 | PLAN * |
1036 | c_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 | */ |
1050 | int |
1051 | f_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 | |
1059 | PLAN * |
1060 | c_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 | */ |
1078 | int |
1079 | f_mindepth(PLAN *plan, FTSENT *entry) |
1080 | { |
1081 | |
1082 | return (entry->fts_level >= plan->min_datap_un._min_data); |
1083 | } |
1084 | |
1085 | PLAN * |
1086 | c_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_MAX0x7fffffff, &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 | */ |
1104 | int |
1105 | f_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 | |
1112 | PLAN * |
1113 | c_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 | */ |
1131 | int |
1132 | f_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 | |
1140 | PLAN * |
1141 | c_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 | */ |
1159 | int |
1160 | f_name(PLAN *plan, FTSENT *entry) |
1161 | { |
1162 | return (!fnmatch(plan->c_datap_un._c_data, entry->fts_name, 0)); |
1163 | } |
1164 | |
1165 | PLAN * |
1166 | c_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 | */ |
1181 | int |
1182 | f_iname(PLAN *plan, FTSENT *entry) |
1183 | { |
1184 | return (!fnmatch(plan->c_datap_un._c_data, entry->fts_name, FNM_CASEFOLD0x10)); |
1185 | } |
1186 | |
1187 | PLAN * |
1188 | c_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 | */ |
1204 | int |
1205 | f_newer(PLAN *plan, FTSENT *entry) |
1206 | { |
1207 | |
1208 | return (entry->fts_statp->st_mtim.tv_sec > plan->t_datap_un._t_data.tv_sec || |
1209 | (entry->fts_statp->st_mtim.tv_sec == plan->t_datap_un._t_data.tv_sec && |
1210 | entry->fts_statp->st_mtim.tv_nsec > plan->t_datap_un._t_data.tv_nsec)); |
1211 | } |
1212 | |
1213 | PLAN * |
1214 | c_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_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 | */ |
1235 | int |
1236 | f_anewer(PLAN *plan, FTSENT *entry) |
1237 | { |
1238 | |
1239 | return (entry->fts_statp->st_atim.tv_sec > plan->t_datap_un._t_data.tv_sec || |
1240 | (entry->fts_statp->st_atim.tv_sec == plan->t_datap_un._t_data.tv_sec && |
1241 | entry->fts_statp->st_atim.tv_nsec > plan->t_datap_un._t_data.tv_nsec)); |
1242 | } |
1243 | |
1244 | PLAN * |
1245 | c_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_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 | */ |
1266 | int |
1267 | f_cnewer(PLAN *plan, FTSENT *entry) |
1268 | { |
1269 | |
1270 | return (entry->fts_statp->st_ctim.tv_sec > plan->t_datap_un._t_data.tv_sec || |
1271 | (entry->fts_statp->st_ctim.tv_sec == plan->t_datap_un._t_data.tv_sec && |
1272 | entry->fts_statp->st_ctim.tv_nsec > plan->t_datap_un._t_data.tv_nsec)); |
1273 | } |
1274 | |
1275 | PLAN * |
1276 | c_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_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 | */ |
1296 | int |
1297 | f_nogroup(PLAN *plan, FTSENT *entry) |
1298 | { |
1299 | return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); |
1300 | } |
1301 | |
1302 | PLAN * |
1303 | c_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 | */ |
1316 | int |
1317 | f_nouser(PLAN *plan, FTSENT *entry) |
1318 | { |
1319 | return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); |
1320 | } |
1321 | |
1322 | PLAN * |
1323 | c_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 | */ |
1336 | int |
1337 | f_path(PLAN *plan, FTSENT *entry) |
1338 | { |
1339 | return (!fnmatch(plan->c_datap_un._c_data, entry->fts_path, 0)); |
1340 | } |
1341 | |
1342 | PLAN * |
1343 | c_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 | */ |
1359 | int |
1360 | f_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 | |
1373 | PLAN * |
1374 | c_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 pathname to be written to |
1400 | * standard output. |
1401 | */ |
1402 | int |
1403 | f_print(PLAN *plan, FTSENT *entry) |
1404 | { |
1405 | (void)printf("%s\n", entry->fts_path); |
1406 | return(1); |
1407 | } |
1408 | |
1409 | int |
1410 | f_print0(PLAN *plan, FTSENT *entry) |
1411 | { |
1412 | (void)fputs(entry->fts_path, stdout(&__sF[1])); |
1413 | (void)fputc('\0', stdout(&__sF[1])); |
1414 | return(1); |
1415 | } |
1416 | |
1417 | PLAN * |
1418 | c_print(char *ignore, char ***ignored, int unused) |
1419 | { |
1420 | isoutput = 1; |
1421 | |
1422 | return(palloc(N_PRINT, f_print)); |
1423 | } |
1424 | |
1425 | PLAN * |
1426 | c_print0(char *ignore, char ***ignored, int unused) |
1427 | { |
1428 | isoutput = 1; |
1429 | |
1430 | return(palloc(N_PRINT0, f_print0)); |
1431 | } |
1432 | |
1433 | /* |
1434 | * -prune functions -- |
1435 | * |
1436 | * Prune a portion of the hierarchy. |
1437 | */ |
1438 | int |
1439 | f_prune(PLAN *plan, FTSENT *entry) |
1440 | { |
1441 | |
1442 | if (fts_set(tree, entry, FTS_SKIP4)) |
1443 | err(1, "%s", entry->fts_path); |
1444 | return (1); |
1445 | } |
1446 | |
1447 | PLAN * |
1448 | c_prune(char *ignore, char ***ignored, int unused) |
1449 | { |
1450 | return (palloc(N_PRUNE, f_prune)); |
1451 | } |
1452 | |
1453 | /* |
1454 | * -size n[c] functions -- |
1455 | * |
1456 | * True if the file size in bytes, divided by an implementation defined |
1457 | * value and rounded up to the next integer, is n. If n is followed by |
1458 | * a c, the size is in bytes. |
1459 | */ |
1460 | #define FIND_SIZE512 512 |
1461 | static int divsize = 1; |
1462 | |
1463 | int |
1464 | f_size(PLAN *plan, FTSENT *entry) |
1465 | { |
1466 | off_t size; |
1467 | |
1468 | size = divsize ? (entry->fts_statp->st_size + FIND_SIZE512 - 1) / |
1469 | FIND_SIZE512 : entry->fts_statp->st_size; |
1470 | 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(); } }; |
1471 | } |
1472 | |
1473 | PLAN * |
1474 | c_size(char *arg, char ***ignored, int unused) |
1475 | { |
1476 | PLAN *new; |
1477 | char endch; |
1478 | |
1479 | ftsoptions &= ~FTS_NOSTAT0x0008; |
1480 | |
1481 | new = palloc(N_SIZE, f_size); |
1482 | endch = 'c'; |
1483 | new->o_datap_un._o_data = find_parsenum(new, "-size", arg, &endch); |
1484 | if (endch == 'c') |
1485 | divsize = 0; |
1486 | return (new); |
1487 | } |
1488 | |
1489 | /* |
1490 | * -type c functions -- |
1491 | * |
1492 | * True if the type of the file is c, where c is b, c, d, p, or f for |
1493 | * block special file, character special file, directory, FIFO, or |
1494 | * regular file, respectively. |
1495 | */ |
1496 | int |
1497 | f_type(PLAN *plan, FTSENT *entry) |
1498 | { |
1499 | return ((entry->fts_statp->st_mode & S_IFMT0170000) == plan->m_datap_un._m_data); |
1500 | } |
1501 | |
1502 | PLAN * |
1503 | c_type(char *typestring, char ***ignored, int unused) |
1504 | { |
1505 | PLAN *new; |
1506 | mode_t mask; |
1507 | |
1508 | ftsoptions &= ~FTS_NOSTAT0x0008; |
1509 | |
1510 | switch (typestring[0]) { |
1511 | case 'b': |
1512 | mask = S_IFBLK0060000; |
1513 | break; |
1514 | case 'c': |
1515 | mask = S_IFCHR0020000; |
1516 | break; |
1517 | case 'd': |
1518 | mask = S_IFDIR0040000; |
1519 | break; |
1520 | case 'f': |
1521 | mask = S_IFREG0100000; |
1522 | break; |
1523 | case 'l': |
1524 | mask = S_IFLNK0120000; |
1525 | break; |
1526 | case 'p': |
1527 | mask = S_IFIFO0010000; |
1528 | break; |
1529 | case 's': |
1530 | mask = S_IFSOCK0140000; |
1531 | break; |
1532 | default: |
1533 | errx(1, "-type: %s: unknown type", typestring); |
1534 | } |
1535 | |
1536 | new = palloc(N_TYPE, f_type); |
1537 | new->m_datap_un._m_data = mask; |
1538 | return (new); |
1539 | } |
1540 | |
1541 | /* |
1542 | * -user uname functions -- |
1543 | * |
1544 | * True if the file belongs to the user uname. If uname is numeric and |
1545 | * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not |
1546 | * return a valid user name, uname is taken as a user ID. |
1547 | */ |
1548 | int |
1549 | f_user(PLAN *plan, FTSENT *entry) |
1550 | { |
1551 | return (entry->fts_statp->st_uid == plan->u_datap_un._u_data); |
1552 | } |
1553 | |
1554 | PLAN * |
1555 | c_user(char *username, char ***ignored, int unused) |
1556 | { |
1557 | PLAN *new; |
1558 | uid_t uid; |
1559 | |
1560 | ftsoptions &= ~FTS_NOSTAT0x0008; |
1561 | |
1562 | if (uid_from_user(username, &uid) == -1) { |
1563 | const char *errstr; |
1564 | |
1565 | uid = strtonum(username, 0, UID_MAX0xffffffffU, &errstr); |
1566 | if (errstr) |
1567 | errx(1, "-user: %s: no such user", username); |
1568 | } |
1569 | |
1570 | new = palloc(N_USER, f_user); |
1571 | new->u_datap_un._u_data = uid; |
1572 | return (new); |
1573 | } |
1574 | |
1575 | /* |
1576 | * -xdev functions -- |
1577 | * |
1578 | * Always true, causes find not to descend past directories that have a |
1579 | * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) |
1580 | */ |
1581 | PLAN * |
1582 | c_xdev(char *ignore, char ***ignored, int unused) |
1583 | { |
1584 | ftsoptions |= FTS_XDEV0x0040; |
1585 | |
1586 | return (palloc(N_XDEV, f_always_true)); |
1587 | } |
1588 | |
1589 | /* |
1590 | * ( expression ) functions -- |
1591 | * |
1592 | * True if expression is true. |
1593 | */ |
1594 | int |
1595 | f_expr(PLAN *plan, FTSENT *entry) |
1596 | { |
1597 | PLAN *p; |
1598 | int state; |
1599 | |
1600 | for (p = plan->p_datap_un._p_data[0]; |
1601 | p && (state = (p->eval)(p, entry)); p = p->next); |
1602 | return (state); |
1603 | } |
1604 | |
1605 | /* |
1606 | * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are |
1607 | * eliminated during phase 2 of find_formplan() --- the '(' node is converted |
1608 | * to a N_EXPR node containing the expression and the ')' node is discarded. |
1609 | */ |
1610 | PLAN * |
1611 | c_openparen(char *ignore, char ***ignored, int unused) |
1612 | { |
1613 | return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1)); |
1614 | } |
1615 | |
1616 | PLAN * |
1617 | c_closeparen(char *ignore, char ***ignored, int unused) |
1618 | { |
1619 | return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1)); |
1620 | } |
1621 | |
1622 | /* |
1623 | * ! expression functions -- |
1624 | * |
1625 | * Negation of a primary; the unary NOT operator. |
1626 | */ |
1627 | int |
1628 | f_not(PLAN *plan, FTSENT *entry) |
1629 | { |
1630 | PLAN *p; |
1631 | int state; |
1632 | |
1633 | for (p = plan->p_datap_un._p_data[0]; |
1634 | p && (state = (p->eval)(p, entry)); p = p->next); |
1635 | return (!state); |
1636 | } |
1637 | |
1638 | PLAN * |
1639 | c_not(char *ignore, char ***ignored, int unused) |
1640 | { |
1641 | return (palloc(N_NOT, f_not)); |
1642 | } |
1643 | |
1644 | /* |
1645 | * expression -o expression functions -- |
1646 | * |
1647 | * Alternation of primaries; the OR operator. The second expression is |
1648 | * not evaluated if the first expression is true. |
1649 | */ |
1650 | int |
1651 | f_or(PLAN *plan, FTSENT *entry) |
1652 | { |
1653 | PLAN *p; |
1654 | int state; |
1655 | |
1656 | for (p = plan->p_datap_un._p_data[0]; |
1657 | p && (state = (p->eval)(p, entry)); p = p->next); |
1658 | |
1659 | if (state) |
1660 | return (1); |
1661 | |
1662 | for (p = plan->p_datap_un._p_data[1]; |
1663 | p && (state = (p->eval)(p, entry)); p = p->next); |
1664 | return (state); |
1665 | } |
1666 | |
1667 | PLAN * |
1668 | c_or(char *ignore, char ***ignored, int unused) |
1669 | { |
1670 | return (palloc(N_OR, f_or)); |
1671 | } |
1672 | |
1673 | |
1674 | /* |
1675 | * plan_cleanup -- |
1676 | * Check and see if the specified plan has any residual state, |
1677 | * and if so, clean it up as appropriate. |
1678 | * |
1679 | * At the moment, only N_EXEC has state. Two kinds: 1) |
1680 | * lists of files to feed to subprocesses 2) State on exit |
1681 | * status of past subprocesses. |
1682 | */ |
1683 | int |
1684 | plan_cleanup(PLAN *plan, void *arg) |
1685 | { |
1686 | if (plan->type==N_EXEC && plan->ep_nargp_un.ex._ep_narg) |
1687 | run_f_exec(plan); |
1688 | |
1689 | return plan->ep_rvalp_un.ex._ep_rval; /* Passed save exit-status up chain */ |
1690 | } |
1691 | |
1692 | |
1693 | static PLAN * |
1694 | palloc(enum ntype t, int (*f)(PLAN *, FTSENT *)) |
1695 | { |
1696 | PLAN *new; |
1697 | |
1698 | if ((new = calloc(1, sizeof(PLAN)))) { |
1699 | new->type = t; |
1700 | new->eval = f; |
1701 | return (new); |
1702 | } |
1703 | err(1, NULL((void *)0)); |
1704 | /* NOTREACHED */ |
1705 | } |