File: | src/usr.bin/find/function.c |
Warning: | line 565, column 12 Although the value stored to 'argv' is used in the enclosing expression, the value is never actually read from 'argv' |
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()) { |
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) { |
Although the value stored to 'argv' is used in the enclosing expression, the value is never actually read from 'argv' | |
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 | } |