File: | src/usr.bin/find/function.c |
Warning: | line 502, column 7 This function call is prohibited after a successful vfork |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: function.c,v 1.50 2020/11/23 06:21:52 halex Exp $ */ | |||
2 | ||||
3 | /*- | |||
4 | * Copyright (c) 1990, 1993 | |||
5 | * The Regents of the University of California. All rights reserved. | |||
6 | * | |||
7 | * This code is derived from software contributed to Berkeley by | |||
8 | * Cimarron D. Taylor of the University of California, Berkeley. | |||
9 | * | |||
10 | * Redistribution and use in source and binary forms, with or without | |||
11 | * modification, are permitted provided that the following conditions | |||
12 | * are met: | |||
13 | * 1. Redistributions of source code must retain the above copyright | |||
14 | * notice, this list of conditions and the following disclaimer. | |||
15 | * 2. Redistributions in binary form must reproduce the above copyright | |||
16 | * notice, this list of conditions and the following disclaimer in the | |||
17 | * documentation and/or other materials provided with the distribution. | |||
18 | * 3. Neither the name of the University nor the names of its contributors | |||
19 | * may be used to endorse or promote products derived from this software | |||
20 | * without specific prior written permission. | |||
21 | * | |||
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |||
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |||
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
32 | * SUCH DAMAGE. | |||
33 | */ | |||
34 | ||||
35 | #include <sys/stat.h> | |||
36 | #include <sys/wait.h> | |||
37 | #include <sys/mount.h> | |||
38 | ||||
39 | #include <dirent.h> | |||
40 | #include <err.h> | |||
41 | #include <errno(*__errno()).h> | |||
42 | #include <fcntl.h> | |||
43 | #include <fnmatch.h> | |||
44 | #include <fts.h> | |||
45 | #include <grp.h> | |||
46 | #include <libgen.h> | |||
47 | #include <limits.h> | |||
48 | #include <pwd.h> | |||
49 | #include <stdio.h> | |||
50 | #include <stdlib.h> | |||
51 | #include <string.h> | |||
52 | #include <unistd.h> | |||
53 | ||||
54 | #include "find.h" | |||
55 | #include "extern.h" | |||
56 | ||||
57 | #define COMPARE(a, b){ switch (plan->flags) { case 1: return (a == b); case 2: return (a < b); case 3: return (a > b); default: abort(); } } { \ | |||
58 | switch (plan->flags) { \ | |||
59 | case F_EQUAL1: \ | |||
60 | return (a == b); \ | |||
61 | case F_LESSTHAN2: \ | |||
62 | return (a < b); \ | |||
63 | case F_GREATER3: \ | |||
64 | return (a > b); \ | |||
65 | default: \ | |||
66 | abort(); \ | |||
67 | } \ | |||
68 | } | |||
69 | ||||
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) { | |||
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_MAX(2147483647 *2U +1U), &errstr); | |||
960 | if (errstr) | |||
961 | errx(1, "-group: %s: no such group", gname); | |||
962 | } | |||
963 | ||||
964 | new = palloc(N_GROUP, f_group); | |||
965 | new->g_datap_un._g_data = gid; | |||
966 | return (new); | |||
967 | } | |||
968 | ||||
969 | /* | |||
970 | * -inum n functions -- | |||
971 | * | |||
972 | * True if the file has inode # n. | |||
973 | */ | |||
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_MAX2147483647, &errstr); | |||
1093 | if (errstr) | |||
1094 | errx(1, "-mindepth: %s: value %s", arg, errstr); | |||
1095 | return (new); | |||
1096 | } | |||
1097 | ||||
1098 | /* | |||
1099 | * -mtime n functions -- | |||
1100 | * | |||
1101 | * True if the difference between the file modification time and the | |||
1102 | * current time is n 24 hour periods. | |||
1103 | */ | |||
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_mtimespecst_mtim.tv_sec > plan->t_datap_un._t_data.tv_sec || | |||
1209 | (entry->fts_statp->st_mtimespecst_mtim.tv_sec == plan->t_datap_un._t_data.tv_sec && | |||
1210 | entry->fts_statp->st_mtimespecst_mtim.tv_nsec > plan->t_datap_un._t_data.tv_nsec)); | |||
1211 | } | |||
1212 | ||||
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_mtimespecst_mtim, sizeof(struct timespec)); | |||
1225 | return (new); | |||
1226 | } | |||
1227 | ||||
1228 | /* | |||
1229 | * -anewer file functions -- | |||
1230 | * | |||
1231 | * True if the current file has been accessed more recently | |||
1232 | * then the access time of the file named by the pathname | |||
1233 | * file. | |||
1234 | */ | |||
1235 | int | |||
1236 | f_anewer(PLAN *plan, FTSENT *entry) | |||
1237 | { | |||
1238 | ||||
1239 | return (entry->fts_statp->st_atimespecst_atim.tv_sec > plan->t_datap_un._t_data.tv_sec || | |||
1240 | (entry->fts_statp->st_atimespecst_atim.tv_sec == plan->t_datap_un._t_data.tv_sec && | |||
1241 | entry->fts_statp->st_atimespecst_atim.tv_nsec > plan->t_datap_un._t_data.tv_nsec)); | |||
1242 | } | |||
1243 | ||||
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_atimespecst_atim, sizeof(struct timespec)); | |||
1256 | return (new); | |||
1257 | } | |||
1258 | ||||
1259 | /* | |||
1260 | * -cnewer file functions -- | |||
1261 | * | |||
1262 | * True if the current file has been changed more recently | |||
1263 | * then the inode change time of the file named by the pathname | |||
1264 | * file. | |||
1265 | */ | |||
1266 | int | |||
1267 | f_cnewer(PLAN *plan, FTSENT *entry) | |||
1268 | { | |||
1269 | ||||
1270 | return (entry->fts_statp->st_ctimespecst_ctim.tv_sec > plan->t_datap_un._t_data.tv_sec || | |||
1271 | (entry->fts_statp->st_ctimespecst_ctim.tv_sec == plan->t_datap_un._t_data.tv_sec && | |||
1272 | entry->fts_statp->st_ctimespecst_ctim.tv_nsec > plan->t_datap_un._t_data.tv_nsec)); | |||
1273 | } | |||
1274 | ||||
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_ctimespecst_ctim, sizeof(struct timespec)); | |||
1287 | return (new); | |||
1288 | } | |||
1289 | ||||
1290 | /* | |||
1291 | * -nogroup functions -- | |||
1292 | * | |||
1293 | * True if file belongs to a user ID for which the equivalent | |||
1294 | * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. | |||
1295 | */ | |||
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 pathame 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 | /* ARGSUSED */ | |||
1410 | int | |||
1411 | f_print0(PLAN *plan, FTSENT *entry) | |||
1412 | { | |||
1413 | (void)fputs(entry->fts_path, stdout(&__sF[1])); | |||
1414 | (void)fputc('\0', stdout(&__sF[1])); | |||
1415 | return(1); | |||
1416 | } | |||
1417 | ||||
1418 | PLAN * | |||
1419 | c_print(char *ignore, char ***ignored, int unused) | |||
1420 | { | |||
1421 | isoutput = 1; | |||
1422 | ||||
1423 | return(palloc(N_PRINT, f_print)); | |||
1424 | } | |||
1425 | ||||
1426 | PLAN * | |||
1427 | c_print0(char *ignore, char ***ignored, int unused) | |||
1428 | { | |||
1429 | isoutput = 1; | |||
1430 | ||||
1431 | return(palloc(N_PRINT0, f_print0)); | |||
1432 | } | |||
1433 | ||||
1434 | /* | |||
1435 | * -prune functions -- | |||
1436 | * | |||
1437 | * Prune a portion of the hierarchy. | |||
1438 | */ | |||
1439 | int | |||
1440 | f_prune(PLAN *plan, FTSENT *entry) | |||
1441 | { | |||
1442 | ||||
1443 | if (fts_set(tree, entry, FTS_SKIP4)) | |||
1444 | err(1, "%s", entry->fts_path); | |||
1445 | return (1); | |||
1446 | } | |||
1447 | ||||
1448 | PLAN * | |||
1449 | c_prune(char *ignore, char ***ignored, int unused) | |||
1450 | { | |||
1451 | return (palloc(N_PRUNE, f_prune)); | |||
1452 | } | |||
1453 | ||||
1454 | /* | |||
1455 | * -size n[c] functions -- | |||
1456 | * | |||
1457 | * True if the file size in bytes, divided by an implementation defined | |||
1458 | * value and rounded up to the next integer, is n. If n is followed by | |||
1459 | * a c, the size is in bytes. | |||
1460 | */ | |||
1461 | #define FIND_SIZE512 512 | |||
1462 | static int divsize = 1; | |||
1463 | ||||
1464 | int | |||
1465 | f_size(PLAN *plan, FTSENT *entry) | |||
1466 | { | |||
1467 | off_t size; | |||
1468 | ||||
1469 | size = divsize ? (entry->fts_statp->st_size + FIND_SIZE512 - 1) / | |||
1470 | FIND_SIZE512 : entry->fts_statp->st_size; | |||
1471 | COMPARE(size, plan->o_data){ switch (plan->flags) { case 1: return (size == plan-> p_un._o_data); case 2: return (size < plan->p_un._o_data ); case 3: return (size > plan->p_un._o_data); default: abort(); } }; | |||
1472 | } | |||
1473 | ||||
1474 | PLAN * | |||
1475 | c_size(char *arg, char ***ignored, int unused) | |||
1476 | { | |||
1477 | PLAN *new; | |||
1478 | char endch; | |||
1479 | ||||
1480 | ftsoptions &= ~FTS_NOSTAT0x0008; | |||
1481 | ||||
1482 | new = palloc(N_SIZE, f_size); | |||
1483 | endch = 'c'; | |||
1484 | new->o_datap_un._o_data = find_parsenum(new, "-size", arg, &endch); | |||
1485 | if (endch == 'c') | |||
1486 | divsize = 0; | |||
1487 | return (new); | |||
1488 | } | |||
1489 | ||||
1490 | /* | |||
1491 | * -type c functions -- | |||
1492 | * | |||
1493 | * True if the type of the file is c, where c is b, c, d, p, or f for | |||
1494 | * block special file, character special file, directory, FIFO, or | |||
1495 | * regular file, respectively. | |||
1496 | */ | |||
1497 | int | |||
1498 | f_type(PLAN *plan, FTSENT *entry) | |||
1499 | { | |||
1500 | return ((entry->fts_statp->st_mode & S_IFMT0170000) == plan->m_datap_un._m_data); | |||
1501 | } | |||
1502 | ||||
1503 | PLAN * | |||
1504 | c_type(char *typestring, char ***ignored, int unused) | |||
1505 | { | |||
1506 | PLAN *new; | |||
1507 | mode_t mask; | |||
1508 | ||||
1509 | ftsoptions &= ~FTS_NOSTAT0x0008; | |||
1510 | ||||
1511 | switch (typestring[0]) { | |||
1512 | case 'b': | |||
1513 | mask = S_IFBLK0060000; | |||
1514 | break; | |||
1515 | case 'c': | |||
1516 | mask = S_IFCHR0020000; | |||
1517 | break; | |||
1518 | case 'd': | |||
1519 | mask = S_IFDIR0040000; | |||
1520 | break; | |||
1521 | case 'f': | |||
1522 | mask = S_IFREG0100000; | |||
1523 | break; | |||
1524 | case 'l': | |||
1525 | mask = S_IFLNK0120000; | |||
1526 | break; | |||
1527 | case 'p': | |||
1528 | mask = S_IFIFO0010000; | |||
1529 | break; | |||
1530 | case 's': | |||
1531 | mask = S_IFSOCK0140000; | |||
1532 | break; | |||
1533 | default: | |||
1534 | errx(1, "-type: %s: unknown type", typestring); | |||
1535 | } | |||
1536 | ||||
1537 | new = palloc(N_TYPE, f_type); | |||
1538 | new->m_datap_un._m_data = mask; | |||
1539 | return (new); | |||
1540 | } | |||
1541 | ||||
1542 | /* | |||
1543 | * -user uname functions -- | |||
1544 | * | |||
1545 | * True if the file belongs to the user uname. If uname is numeric and | |||
1546 | * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not | |||
1547 | * return a valid user name, uname is taken as a user ID. | |||
1548 | */ | |||
1549 | int | |||
1550 | f_user(PLAN *plan, FTSENT *entry) | |||
1551 | { | |||
1552 | return (entry->fts_statp->st_uid == plan->u_datap_un._u_data); | |||
1553 | } | |||
1554 | ||||
1555 | PLAN * | |||
1556 | c_user(char *username, char ***ignored, int unused) | |||
1557 | { | |||
1558 | PLAN *new; | |||
1559 | uid_t uid; | |||
1560 | ||||
1561 | ftsoptions &= ~FTS_NOSTAT0x0008; | |||
1562 | ||||
1563 | if (uid_from_user(username, &uid) == -1) { | |||
1564 | const char *errstr; | |||
1565 | ||||
1566 | uid = strtonum(username, 0, UID_MAX(2147483647 *2U +1U), &errstr); | |||
1567 | if (errstr) | |||
1568 | errx(1, "-user: %s: no such user", username); | |||
1569 | } | |||
1570 | ||||
1571 | new = palloc(N_USER, f_user); | |||
1572 | new->u_datap_un._u_data = uid; | |||
1573 | return (new); | |||
1574 | } | |||
1575 | ||||
1576 | /* | |||
1577 | * -xdev functions -- | |||
1578 | * | |||
1579 | * Always true, causes find not to decend past directories that have a | |||
1580 | * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) | |||
1581 | */ | |||
1582 | PLAN * | |||
1583 | c_xdev(char *ignore, char ***ignored, int unused) | |||
1584 | { | |||
1585 | ftsoptions |= FTS_XDEV0x0040; | |||
1586 | ||||
1587 | return (palloc(N_XDEV, f_always_true)); | |||
1588 | } | |||
1589 | ||||
1590 | /* | |||
1591 | * ( expression ) functions -- | |||
1592 | * | |||
1593 | * True if expression is true. | |||
1594 | */ | |||
1595 | int | |||
1596 | f_expr(PLAN *plan, FTSENT *entry) | |||
1597 | { | |||
1598 | PLAN *p; | |||
1599 | int state; | |||
1600 | ||||
1601 | for (p = plan->p_datap_un._p_data[0]; | |||
1602 | p && (state = (p->eval)(p, entry)); p = p->next); | |||
1603 | return (state); | |||
1604 | } | |||
1605 | ||||
1606 | /* | |||
1607 | * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are | |||
1608 | * eliminated during phase 2 of find_formplan() --- the '(' node is converted | |||
1609 | * to a N_EXPR node containing the expression and the ')' node is discarded. | |||
1610 | */ | |||
1611 | PLAN * | |||
1612 | c_openparen(char *ignore, char ***ignored, int unused) | |||
1613 | { | |||
1614 | return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1)); | |||
1615 | } | |||
1616 | ||||
1617 | PLAN * | |||
1618 | c_closeparen(char *ignore, char ***ignored, int unused) | |||
1619 | { | |||
1620 | return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1)); | |||
1621 | } | |||
1622 | ||||
1623 | /* | |||
1624 | * ! expression functions -- | |||
1625 | * | |||
1626 | * Negation of a primary; the unary NOT operator. | |||
1627 | */ | |||
1628 | int | |||
1629 | f_not(PLAN *plan, FTSENT *entry) | |||
1630 | { | |||
1631 | PLAN *p; | |||
1632 | int state; | |||
1633 | ||||
1634 | for (p = plan->p_datap_un._p_data[0]; | |||
1635 | p && (state = (p->eval)(p, entry)); p = p->next); | |||
1636 | return (!state); | |||
1637 | } | |||
1638 | ||||
1639 | PLAN * | |||
1640 | c_not(char *ignore, char ***ignored, int unused) | |||
1641 | { | |||
1642 | return (palloc(N_NOT, f_not)); | |||
1643 | } | |||
1644 | ||||
1645 | /* | |||
1646 | * expression -o expression functions -- | |||
1647 | * | |||
1648 | * Alternation of primaries; the OR operator. The second expression is | |||
1649 | * not evaluated if the first expression is true. | |||
1650 | */ | |||
1651 | int | |||
1652 | f_or(PLAN *plan, FTSENT *entry) | |||
1653 | { | |||
1654 | PLAN *p; | |||
1655 | int state; | |||
1656 | ||||
1657 | for (p = plan->p_datap_un._p_data[0]; | |||
1658 | p && (state = (p->eval)(p, entry)); p = p->next); | |||
1659 | ||||
1660 | if (state) | |||
1661 | return (1); | |||
1662 | ||||
1663 | for (p = plan->p_datap_un._p_data[1]; | |||
1664 | p && (state = (p->eval)(p, entry)); p = p->next); | |||
1665 | return (state); | |||
1666 | } | |||
1667 | ||||
1668 | PLAN * | |||
1669 | c_or(char *ignore, char ***ignored, int unused) | |||
1670 | { | |||
1671 | return (palloc(N_OR, f_or)); | |||
1672 | } | |||
1673 | ||||
1674 | ||||
1675 | /* | |||
1676 | * plan_cleanup -- | |||
1677 | * Check and see if the specified plan has any residual state, | |||
1678 | * and if so, clean it up as appropriate. | |||
1679 | * | |||
1680 | * At the moment, only N_EXEC has state. Two kinds: 1) | |||
1681 | * lists of files to feed to subprocesses 2) State on exit | |||
1682 | * statusses of past subprocesses. | |||
1683 | */ | |||
1684 | /* ARGSUSED1 */ | |||
1685 | int | |||
1686 | plan_cleanup(PLAN *plan, void *arg) | |||
1687 | { | |||
1688 | if (plan->type==N_EXEC && plan->ep_nargp_un.ex._ep_narg) | |||
| ||||
1689 | run_f_exec(plan); | |||
1690 | ||||
1691 | return plan->ep_rvalp_un.ex._ep_rval; /* Passed save exit-status up chain */ | |||
1692 | } | |||
1693 | ||||
1694 | ||||
1695 | static PLAN * | |||
1696 | palloc(enum ntype t, int (*f)(PLAN *, FTSENT *)) | |||
1697 | { | |||
1698 | PLAN *new; | |||
1699 | ||||
1700 | if ((new = calloc(1, sizeof(PLAN)))) { | |||
1701 | new->type = t; | |||
1702 | new->eval = f; | |||
1703 | return (new); | |||
1704 | } | |||
1705 | err(1, NULL((void *)0)); | |||
1706 | /* NOTREACHED */ | |||
1707 | } |