]>
Commit | Line | Data |
---|---|---|
44bd5ea7 A |
1 | /*- |
2 | * Copyright (c) 1990, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Cimarron D. Taylor of the University of California, Berkeley. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. All advertising materials mentioning features or use of this software | |
17 | * must display the following acknowledgement: | |
18 | * This product includes software developed by the University of | |
19 | * California, Berkeley and its contributors. | |
20 | * 4. Neither the name of the University nor the names of its contributors | |
21 | * may be used to endorse or promote products derived from this software | |
22 | * without specific prior written permission. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | */ | |
36 | ||
44bd5ea7 A |
37 | #ifndef lint |
38 | #if 0 | |
c0fcf4e1 | 39 | static const char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95"; |
44bd5ea7 | 40 | #else |
c0fcf4e1 A |
41 | static const char rcsid[] = |
42 | "$FreeBSD: src/usr.bin/find/function.c,v 1.22.2.9 2001/09/19 09:44:24 ru Exp $"; | |
44bd5ea7 A |
43 | #endif |
44 | #endif /* not lint */ | |
45 | ||
46 | #include <sys/param.h> | |
47 | #include <sys/ucred.h> | |
48 | #include <sys/stat.h> | |
49 | #include <sys/wait.h> | |
50 | #include <sys/mount.h> | |
c0fcf4e1 | 51 | #include <sys/timeb.h> |
44bd5ea7 | 52 | |
c0fcf4e1 | 53 | #include <dirent.h> |
44bd5ea7 A |
54 | #include <err.h> |
55 | #include <errno.h> | |
56 | #include <fnmatch.h> | |
57 | #include <fts.h> | |
58 | #include <grp.h> | |
59 | #include <pwd.h> | |
c0fcf4e1 | 60 | #include <regex.h> |
44bd5ea7 A |
61 | #include <stdio.h> |
62 | #include <stdlib.h> | |
63 | #include <string.h> | |
44bd5ea7 A |
64 | #include <unistd.h> |
65 | ||
66 | #include "find.h" | |
67 | ||
c0fcf4e1 A |
68 | time_t get_date __P((char *date, struct timeb *now)); |
69 | ||
44bd5ea7 | 70 | #define COMPARE(a, b) { \ |
c0fcf4e1 | 71 | switch (plan->flags & F_ELG_MASK) { \ |
44bd5ea7 A |
72 | case F_EQUAL: \ |
73 | return (a == b); \ | |
74 | case F_LESSTHAN: \ | |
75 | return (a < b); \ | |
76 | case F_GREATER: \ | |
77 | return (a > b); \ | |
78 | default: \ | |
79 | abort(); \ | |
80 | } \ | |
81 | } | |
82 | ||
c0fcf4e1 A |
83 | static PLAN * |
84 | palloc(option) | |
85 | OPTION *option; | |
86 | { | |
87 | PLAN *new; | |
88 | ||
89 | if ((new = malloc(sizeof(PLAN))) == NULL) | |
90 | err(1, NULL); | |
91 | new->execute = option->execute; | |
92 | new->flags = option->flags; | |
93 | new->next = NULL; | |
94 | return new; | |
95 | } | |
44bd5ea7 A |
96 | |
97 | /* | |
98 | * find_parsenum -- | |
99 | * Parse a string of the form [+-]# and return the value. | |
100 | */ | |
c0fcf4e1 | 101 | static long long |
44bd5ea7 A |
102 | find_parsenum(plan, option, vp, endch) |
103 | PLAN *plan; | |
104 | char *option, *vp, *endch; | |
105 | { | |
c0fcf4e1 | 106 | long long value; |
44bd5ea7 | 107 | char *endchar, *str; /* Pointer to character ending conversion. */ |
c0fcf4e1 | 108 | |
44bd5ea7 A |
109 | /* Determine comparison from leading + or -. */ |
110 | str = vp; | |
111 | switch (*str) { | |
112 | case '+': | |
113 | ++str; | |
c0fcf4e1 | 114 | plan->flags |= F_GREATER; |
44bd5ea7 A |
115 | break; |
116 | case '-': | |
117 | ++str; | |
c0fcf4e1 | 118 | plan->flags |= F_LESSTHAN; |
44bd5ea7 A |
119 | break; |
120 | default: | |
c0fcf4e1 | 121 | plan->flags |= F_EQUAL; |
44bd5ea7 A |
122 | break; |
123 | } | |
c0fcf4e1 | 124 | |
44bd5ea7 | 125 | /* |
c0fcf4e1 | 126 | * Convert the string with strtoq(). Note, if strtoq() returns zero |
44bd5ea7 A |
127 | * and endchar points to the beginning of the string we know we have |
128 | * a syntax error. | |
129 | */ | |
c0fcf4e1 | 130 | value = strtoq(str, &endchar, 10); |
44bd5ea7 A |
131 | if (value == 0 && endchar == str) |
132 | errx(1, "%s: %s: illegal numeric value", option, vp); | |
133 | if (endchar[0] && (endch == NULL || endchar[0] != *endch)) | |
134 | errx(1, "%s: %s: illegal trailing character", option, vp); | |
135 | if (endch) | |
136 | *endch = endchar[0]; | |
c0fcf4e1 | 137 | return value; |
44bd5ea7 A |
138 | } |
139 | ||
c0fcf4e1 A |
140 | /* |
141 | * nextarg -- | |
142 | * Check that another argument still exists, return a pointer to it, | |
143 | * and increment the argument vector pointer. | |
144 | */ | |
145 | static char * | |
146 | nextarg(option, argvp) | |
147 | OPTION *option; | |
148 | char ***argvp; | |
149 | { | |
150 | char *arg; | |
151 | ||
152 | if ((arg = **argvp) == 0) | |
153 | errx(1, "%s: requires additional arguments", option->name); | |
154 | (*argvp)++; | |
155 | return arg; | |
156 | } /* nextarg() */ | |
157 | ||
44bd5ea7 A |
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 | */ | |
c0fcf4e1 A |
164 | #define TIME_CORRECT(p) \ |
165 | if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \ | |
44bd5ea7 A |
166 | ++((p)->t_data); |
167 | ||
168 | /* | |
c0fcf4e1 | 169 | * -[acm]min n functions -- |
44bd5ea7 | 170 | * |
c0fcf4e1 A |
171 | * True if the difference between the |
172 | * file access time (-amin) | |
173 | * last change of file status information (-cmin) | |
174 | * file modification time (-mmin) | |
175 | * and the current time is n min periods. | |
44bd5ea7 A |
176 | */ |
177 | int | |
c0fcf4e1 | 178 | f_Xmin(plan, entry) |
44bd5ea7 A |
179 | PLAN *plan; |
180 | FTSENT *entry; | |
181 | { | |
182 | extern time_t now; | |
183 | ||
c0fcf4e1 A |
184 | if (plan->flags & F_TIME_C) { |
185 | COMPARE((now - entry->fts_statp->st_ctime + | |
186 | 60 - 1) / 60, plan->t_data); | |
187 | } else if (plan->flags & F_TIME_A) { | |
188 | COMPARE((now - entry->fts_statp->st_atime + | |
189 | 60 - 1) / 60, plan->t_data); | |
190 | } else { | |
191 | COMPARE((now - entry->fts_statp->st_mtime + | |
192 | 60 - 1) / 60, plan->t_data); | |
193 | } | |
44bd5ea7 | 194 | } |
c0fcf4e1 | 195 | |
44bd5ea7 | 196 | PLAN * |
c0fcf4e1 A |
197 | c_Xmin(option, argvp) |
198 | OPTION *option; | |
44bd5ea7 | 199 | char ***argvp; |
44bd5ea7 | 200 | { |
c0fcf4e1 | 201 | char *nmins; |
44bd5ea7 A |
202 | PLAN *new; |
203 | ||
c0fcf4e1 | 204 | nmins = nextarg(option, argvp); |
44bd5ea7 A |
205 | ftsoptions &= ~FTS_NOSTAT; |
206 | ||
c0fcf4e1 A |
207 | new = palloc(option); |
208 | new->t_data = find_parsenum(new, option->name, nmins, NULL); | |
209 | TIME_CORRECT(new); | |
210 | return new; | |
44bd5ea7 | 211 | } |
c0fcf4e1 | 212 | |
44bd5ea7 | 213 | /* |
c0fcf4e1 | 214 | * -[acm]time n functions -- |
44bd5ea7 | 215 | * |
c0fcf4e1 A |
216 | * True if the difference between the |
217 | * file access time (-atime) | |
218 | * last change of file status information (-ctime) | |
219 | * file modification time (-mtime) | |
220 | * and the current time is n 24 hour periods. | |
44bd5ea7 | 221 | */ |
c0fcf4e1 | 222 | |
44bd5ea7 | 223 | int |
c0fcf4e1 | 224 | f_Xtime(plan, entry) |
44bd5ea7 A |
225 | PLAN *plan; |
226 | FTSENT *entry; | |
227 | { | |
228 | extern time_t now; | |
229 | ||
c0fcf4e1 A |
230 | if (plan->flags & F_TIME_C) { |
231 | COMPARE((now - entry->fts_statp->st_ctime + | |
232 | 86400 - 1) / 86400, plan->t_data); | |
233 | } else if (plan->flags & F_TIME_A) { | |
234 | COMPARE((now - entry->fts_statp->st_atime + | |
235 | 86400 - 1) / 86400, plan->t_data); | |
236 | } else { | |
237 | COMPARE((now - entry->fts_statp->st_mtime + | |
238 | 86400 - 1) / 86400, plan->t_data); | |
239 | } | |
44bd5ea7 | 240 | } |
c0fcf4e1 | 241 | |
44bd5ea7 | 242 | PLAN * |
c0fcf4e1 A |
243 | c_Xtime(option, argvp) |
244 | OPTION *option; | |
44bd5ea7 | 245 | char ***argvp; |
44bd5ea7 | 246 | { |
c0fcf4e1 | 247 | char *ndays; |
44bd5ea7 A |
248 | PLAN *new; |
249 | ||
c0fcf4e1 | 250 | ndays = nextarg(option, argvp); |
44bd5ea7 A |
251 | ftsoptions &= ~FTS_NOSTAT; |
252 | ||
c0fcf4e1 A |
253 | new = palloc(option); |
254 | new->t_data = find_parsenum(new, option->name, ndays, NULL); | |
255 | TIME_CORRECT(new); | |
256 | return new; | |
44bd5ea7 A |
257 | } |
258 | ||
c0fcf4e1 A |
259 | /* |
260 | * -maxdepth/-mindepth n functions -- | |
261 | * | |
262 | * Does the same as -prune if the level of the current file is | |
263 | * greater/less than the specified maximum/minimum depth. | |
264 | * | |
265 | * Note that -maxdepth and -mindepth are handled specially in | |
266 | * find_execute() so their f_* functions are set to f_always_true(). | |
267 | */ | |
268 | PLAN * | |
269 | c_mXXdepth(option, argvp) | |
270 | OPTION *option; | |
271 | char ***argvp; | |
272 | { | |
273 | char *dstr; | |
274 | PLAN *new; | |
275 | ||
276 | dstr = nextarg(option, argvp); | |
277 | if (dstr[0] == '-') | |
278 | /* all other errors handled by find_parsenum() */ | |
279 | errx(1, "%s: %s: value must be positive", option->name, dstr); | |
280 | ||
281 | new = palloc(option); | |
282 | if (option->flags & F_MAXDEPTH) | |
283 | maxdepth = find_parsenum(new, option->name, dstr, NULL); | |
284 | else | |
285 | mindepth = find_parsenum(new, option->name, dstr, NULL); | |
286 | return new; | |
287 | } | |
288 | ||
289 | /* | |
290 | * -delete functions -- | |
291 | * | |
292 | * True always. Makes its best shot and continues on regardless. | |
293 | */ | |
294 | int | |
295 | f_delete(plan, entry) | |
296 | PLAN *plan; | |
297 | FTSENT *entry; | |
298 | { | |
299 | /* ignore these from fts */ | |
300 | if (strcmp(entry->fts_accpath, ".") == 0 || | |
301 | strcmp(entry->fts_accpath, "..") == 0) | |
302 | return 1; | |
303 | ||
304 | /* sanity check */ | |
305 | if (isdepth == 0 || /* depth off */ | |
306 | (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ | |
307 | !(ftsoptions & FTS_PHYSICAL) || /* physical off */ | |
308 | (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ | |
309 | errx(1, "-delete: insecure options got turned on"); | |
310 | ||
311 | /* Potentially unsafe - do not accept relative paths whatsoever */ | |
312 | if (strchr(entry->fts_accpath, '/') != NULL) | |
313 | errx(1, "-delete: %s: relative path potentially not safe", | |
314 | entry->fts_accpath); | |
315 | ||
316 | /* Turn off user immutable bits if running as root */ | |
317 | if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && | |
318 | !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && | |
319 | geteuid() == 0) | |
320 | chflags(entry->fts_accpath, | |
321 | entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); | |
322 | ||
323 | /* rmdir directories, unlink everything else */ | |
324 | if (S_ISDIR(entry->fts_statp->st_mode)) { | |
325 | if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) | |
326 | warn("-delete: rmdir(%s)", entry->fts_path); | |
327 | } else { | |
328 | if (unlink(entry->fts_accpath) < 0) | |
329 | warn("-delete: unlink(%s)", entry->fts_path); | |
330 | } | |
331 | ||
332 | /* "succeed" */ | |
333 | return 1; | |
334 | } | |
335 | ||
336 | PLAN * | |
337 | c_delete(option, argvp) | |
338 | OPTION *option; | |
339 | char ***argvp; | |
340 | { | |
341 | ||
342 | ftsoptions &= ~FTS_NOSTAT; /* no optimise */ | |
343 | ftsoptions |= FTS_PHYSICAL; /* disable -follow */ | |
344 | ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ | |
345 | isoutput = 1; /* possible output */ | |
346 | isdepth = 1; /* -depth implied */ | |
347 | ||
348 | return palloc(option); | |
349 | } | |
350 | ||
351 | ||
44bd5ea7 A |
352 | /* |
353 | * -depth functions -- | |
354 | * | |
355 | * Always true, causes descent of the directory hierarchy to be done | |
356 | * so that all entries in a directory are acted on before the directory | |
357 | * itself. | |
358 | */ | |
359 | int | |
360 | f_always_true(plan, entry) | |
361 | PLAN *plan; | |
362 | FTSENT *entry; | |
363 | { | |
c0fcf4e1 | 364 | return 1; |
44bd5ea7 | 365 | } |
c0fcf4e1 | 366 | |
44bd5ea7 | 367 | PLAN * |
c0fcf4e1 A |
368 | c_depth(option, argvp) |
369 | OPTION *option; | |
44bd5ea7 | 370 | char ***argvp; |
44bd5ea7 A |
371 | { |
372 | isdepth = 1; | |
373 | ||
c0fcf4e1 A |
374 | return palloc(option); |
375 | } | |
376 | ||
377 | /* | |
378 | * -empty functions -- | |
379 | * | |
380 | * True if the file or directory is empty | |
381 | */ | |
382 | int | |
383 | f_empty(plan, entry) | |
384 | PLAN *plan; | |
385 | FTSENT *entry; | |
386 | { | |
387 | if (S_ISREG(entry->fts_statp->st_mode) && | |
388 | entry->fts_statp->st_size == 0) | |
389 | return 1; | |
390 | if (S_ISDIR(entry->fts_statp->st_mode)) { | |
391 | struct dirent *dp; | |
392 | int empty; | |
393 | DIR *dir; | |
394 | ||
395 | empty = 1; | |
396 | dir = opendir(entry->fts_accpath); | |
397 | if (dir == NULL) | |
398 | err(1, "%s", entry->fts_accpath); | |
399 | for (dp = readdir(dir); dp; dp = readdir(dir)) | |
400 | if (dp->d_name[0] != '.' || | |
401 | (dp->d_name[1] != '\0' && | |
402 | (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { | |
403 | empty = 0; | |
404 | break; | |
405 | } | |
406 | closedir(dir); | |
407 | return empty; | |
408 | } | |
409 | return 0; | |
410 | } | |
411 | ||
412 | PLAN * | |
413 | c_empty(option, argvp) | |
414 | OPTION *option; | |
415 | char ***argvp; | |
416 | { | |
417 | ftsoptions &= ~FTS_NOSTAT; | |
418 | ||
419 | return palloc(option); | |
44bd5ea7 | 420 | } |
c0fcf4e1 | 421 | |
44bd5ea7 | 422 | /* |
c0fcf4e1 | 423 | * [-exec | -execdir | -ok] utility [arg ... ] ; functions -- |
44bd5ea7 A |
424 | * |
425 | * True if the executed utility returns a zero value as exit status. | |
426 | * The end of the primary expression is delimited by a semicolon. If | |
c0fcf4e1 A |
427 | * "{}" occurs anywhere, it gets replaced by the current pathname, |
428 | * or, in the case of -execdir, the current basename (filename | |
429 | * without leading directory prefix). For -exec and -ok, | |
430 | * the current directory for the execution of utility is the same as | |
431 | * the current directory when the find utility was started, whereas | |
432 | * for -execdir, it is the directory the file resides in. | |
44bd5ea7 | 433 | * |
c0fcf4e1 A |
434 | * The primary -ok differs from -exec in that it requests affirmation |
435 | * of the user before executing the utility. | |
44bd5ea7 A |
436 | */ |
437 | int | |
438 | f_exec(plan, entry) | |
c0fcf4e1 | 439 | register PLAN *plan; |
44bd5ea7 A |
440 | FTSENT *entry; |
441 | { | |
442 | extern int dotfd; | |
c0fcf4e1 | 443 | register int cnt; |
44bd5ea7 A |
444 | pid_t pid; |
445 | int status; | |
c0fcf4e1 A |
446 | char *file; |
447 | ||
448 | /* XXX - if file/dir ends in '/' this will not work -- can it? */ | |
449 | if ((plan->flags & F_EXECDIR) && \ | |
450 | (file = strrchr(entry->fts_path, '/'))) | |
451 | file++; | |
452 | else | |
453 | file = entry->fts_path; | |
44bd5ea7 A |
454 | |
455 | for (cnt = 0; plan->e_argv[cnt]; ++cnt) | |
456 | if (plan->e_len[cnt]) | |
457 | brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], | |
c0fcf4e1 | 458 | file, plan->e_len[cnt]); |
44bd5ea7 | 459 | |
c0fcf4e1 A |
460 | if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv)) |
461 | return 0; | |
44bd5ea7 | 462 | |
c0fcf4e1 | 463 | /* make sure find output is interspersed correctly with subprocesses */ |
44bd5ea7 A |
464 | fflush(stdout); |
465 | fflush(stderr); | |
466 | ||
c0fcf4e1 | 467 | switch (pid = fork()) { |
44bd5ea7 A |
468 | case -1: |
469 | err(1, "fork"); | |
470 | /* NOTREACHED */ | |
471 | case 0: | |
c0fcf4e1 A |
472 | /* change dir back from where we started */ |
473 | if (!(plan->flags & F_EXECDIR) && fchdir(dotfd)) { | |
44bd5ea7 A |
474 | warn("chdir"); |
475 | _exit(1); | |
476 | } | |
477 | execvp(plan->e_argv[0], plan->e_argv); | |
478 | warn("%s", plan->e_argv[0]); | |
479 | _exit(1); | |
480 | } | |
481 | pid = waitpid(pid, &status, 0); | |
482 | return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); | |
483 | } | |
c0fcf4e1 | 484 | |
44bd5ea7 | 485 | /* |
c0fcf4e1 | 486 | * c_exec, c_execdir, c_ok -- |
44bd5ea7 A |
487 | * build three parallel arrays, one with pointers to the strings passed |
488 | * on the command line, one with (possibly duplicated) pointers to the | |
489 | * argv array, and one with integer values that are lengths of the | |
490 | * strings, but also flags meaning that the string has to be massaged. | |
491 | */ | |
492 | PLAN * | |
c0fcf4e1 A |
493 | c_exec(option, argvp) |
494 | OPTION *option; | |
44bd5ea7 | 495 | char ***argvp; |
44bd5ea7 A |
496 | { |
497 | PLAN *new; /* node returned */ | |
c0fcf4e1 A |
498 | register int cnt; |
499 | register char **argv, **ap, *p; | |
44bd5ea7 | 500 | |
c0fcf4e1 A |
501 | /* XXX - was in c_execdir, but seems unnecessary!? |
502 | ftsoptions &= ~FTS_NOSTAT; | |
503 | */ | |
44bd5ea7 | 504 | isoutput = 1; |
c0fcf4e1 A |
505 | |
506 | /* XXX - this is a change from the previous coding */ | |
507 | new = palloc(option); | |
44bd5ea7 A |
508 | |
509 | for (ap = argv = *argvp;; ++ap) { | |
510 | if (!*ap) | |
511 | errx(1, | |
c0fcf4e1 | 512 | "%s: no terminating \";\"", option->name); |
44bd5ea7 A |
513 | if (**ap == ';') |
514 | break; | |
515 | } | |
516 | ||
517 | cnt = ap - *argvp + 1; | |
518 | new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); | |
519 | new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); | |
520 | new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); | |
521 | ||
522 | for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { | |
523 | new->e_orig[cnt] = *argv; | |
524 | for (p = *argv; *p; ++p) | |
525 | if (p[0] == '{' && p[1] == '}') { | |
526 | new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); | |
527 | new->e_len[cnt] = MAXPATHLEN; | |
528 | break; | |
529 | } | |
530 | if (!*p) { | |
531 | new->e_argv[cnt] = *argv; | |
532 | new->e_len[cnt] = 0; | |
533 | } | |
534 | } | |
535 | new->e_argv[cnt] = new->e_orig[cnt] = NULL; | |
536 | ||
537 | *argvp = argv + 1; | |
c0fcf4e1 A |
538 | return new; |
539 | } | |
540 | ||
541 | int | |
542 | f_flags(plan, entry) | |
543 | PLAN *plan; | |
544 | FTSENT *entry; | |
545 | { | |
546 | u_long flags; | |
547 | ||
548 | flags = entry->fts_statp->st_flags; | |
549 | if (plan->flags & F_ATLEAST) | |
550 | return (flags | plan->fl_flags) == flags && | |
551 | !(flags & plan->fl_notflags); | |
552 | else if (plan->flags & F_ANY) | |
553 | return (flags & plan->fl_flags) || | |
554 | (flags | plan->fl_notflags) != flags; | |
555 | else | |
556 | return flags == plan->fl_flags && | |
557 | !(plan->fl_flags & plan->fl_notflags); | |
558 | } | |
559 | ||
560 | PLAN * | |
561 | c_flags(option, argvp) | |
562 | OPTION *option; | |
563 | char ***argvp; | |
564 | { | |
565 | char *flags_str; | |
566 | PLAN *new; | |
567 | u_long flags, notflags; | |
568 | ||
569 | flags_str = nextarg(option, argvp); | |
570 | ftsoptions &= ~FTS_NOSTAT; | |
571 | ||
572 | new = palloc(option); | |
573 | ||
574 | if (*flags_str == '-') { | |
575 | new->flags |= F_ATLEAST; | |
576 | flags_str++; | |
577 | } else if (*flags_str == '+') { | |
578 | new->flags |= F_ANY; | |
579 | flags_str++; | |
580 | } | |
581 | if (strtofflags(&flags_str, &flags, ¬flags) == 1) | |
582 | errx(1, "%s: %s: illegal flags string", option->name, flags_str); | |
583 | ||
584 | new->fl_flags = flags; | |
585 | new->fl_notflags = notflags; | |
586 | return new; | |
44bd5ea7 | 587 | } |
c0fcf4e1 | 588 | |
44bd5ea7 A |
589 | /* |
590 | * -follow functions -- | |
591 | * | |
592 | * Always true, causes symbolic links to be followed on a global | |
593 | * basis. | |
594 | */ | |
595 | PLAN * | |
c0fcf4e1 A |
596 | c_follow(option, argvp) |
597 | OPTION *option; | |
44bd5ea7 | 598 | char ***argvp; |
44bd5ea7 A |
599 | { |
600 | ftsoptions &= ~FTS_PHYSICAL; | |
601 | ftsoptions |= FTS_LOGICAL; | |
602 | ||
c0fcf4e1 | 603 | return palloc(option); |
44bd5ea7 | 604 | } |
c0fcf4e1 | 605 | |
44bd5ea7 A |
606 | /* |
607 | * -fstype functions -- | |
608 | * | |
609 | * True if the file is of a certain type. | |
610 | */ | |
611 | int | |
612 | f_fstype(plan, entry) | |
613 | PLAN *plan; | |
614 | FTSENT *entry; | |
615 | { | |
616 | static dev_t curdev; /* need a guaranteed illegal dev value */ | |
617 | static int first = 1; | |
618 | struct statfs sb; | |
c0fcf4e1 | 619 | static int val_type, val_flags; |
44bd5ea7 A |
620 | char *p, save[2]; |
621 | ||
622 | /* Only check when we cross mount point. */ | |
623 | if (first || curdev != entry->fts_statp->st_dev) { | |
624 | curdev = entry->fts_statp->st_dev; | |
625 | ||
626 | /* | |
627 | * Statfs follows symlinks; find wants the link's file system, | |
628 | * not where it points. | |
629 | */ | |
630 | if (entry->fts_info == FTS_SL || | |
631 | entry->fts_info == FTS_SLNONE) { | |
632 | if ((p = strrchr(entry->fts_accpath, '/')) != NULL) | |
633 | ++p; | |
634 | else | |
635 | p = entry->fts_accpath; | |
636 | save[0] = p[0]; | |
637 | p[0] = '.'; | |
638 | save[1] = p[1]; | |
639 | p[1] = '\0'; | |
c0fcf4e1 | 640 | } else |
44bd5ea7 A |
641 | p = NULL; |
642 | ||
643 | if (statfs(entry->fts_accpath, &sb)) | |
644 | err(1, "%s", entry->fts_accpath); | |
645 | ||
646 | if (p) { | |
647 | p[0] = save[0]; | |
648 | p[1] = save[1]; | |
649 | } | |
650 | ||
651 | first = 0; | |
652 | ||
653 | /* | |
654 | * Further tests may need both of these values, so | |
655 | * always copy both of them. | |
656 | */ | |
c0fcf4e1 A |
657 | val_flags = sb.f_flags; |
658 | val_type = sb.f_type; | |
44bd5ea7 | 659 | } |
c0fcf4e1 | 660 | switch (plan->flags & F_MTMASK) { |
44bd5ea7 | 661 | case F_MTFLAG: |
c0fcf4e1 | 662 | return (val_flags & plan->mt_data) != 0; |
44bd5ea7 | 663 | case F_MTTYPE: |
c0fcf4e1 | 664 | return (val_type == plan->mt_data); |
44bd5ea7 A |
665 | default: |
666 | abort(); | |
667 | } | |
668 | } | |
c0fcf4e1 A |
669 | |
670 | #if !defined(__NetBSD__) | |
44bd5ea7 | 671 | PLAN * |
c0fcf4e1 A |
672 | c_fstype(option, argvp) |
673 | OPTION *option; | |
44bd5ea7 | 674 | char ***argvp; |
44bd5ea7 | 675 | { |
c0fcf4e1 A |
676 | char *fsname; |
677 | register PLAN *new; | |
678 | struct vfsconf vfc; | |
679 | ||
680 | fsname = nextarg(option, argvp); | |
44bd5ea7 | 681 | ftsoptions &= ~FTS_NOSTAT; |
44bd5ea7 | 682 | |
c0fcf4e1 A |
683 | new = palloc(option); |
684 | ||
685 | /* | |
686 | * Check first for a filesystem name. | |
687 | */ | |
688 | if (getvfsbyname(fsname, &vfc) == 0) { | |
689 | new->flags |= F_MTTYPE; | |
690 | new->mt_data = vfc.vfc_typenum; | |
691 | return new; | |
692 | } | |
693 | ||
694 | switch (*fsname) { | |
44bd5ea7 | 695 | case 'l': |
c0fcf4e1 A |
696 | if (!strcmp(fsname, "local")) { |
697 | new->flags |= F_MTFLAG; | |
44bd5ea7 | 698 | new->mt_data = MNT_LOCAL; |
c0fcf4e1 | 699 | return new; |
44bd5ea7 A |
700 | } |
701 | break; | |
702 | case 'r': | |
c0fcf4e1 A |
703 | if (!strcmp(fsname, "rdonly")) { |
704 | new->flags |= F_MTFLAG; | |
44bd5ea7 | 705 | new->mt_data = MNT_RDONLY; |
c0fcf4e1 | 706 | return new; |
44bd5ea7 A |
707 | } |
708 | break; | |
709 | } | |
710 | ||
c0fcf4e1 A |
711 | errx(1, "%s: unknown file type", fsname); |
712 | /* NOTREACHED */ | |
44bd5ea7 | 713 | } |
c0fcf4e1 A |
714 | #endif /* __NetBSD__ */ |
715 | ||
44bd5ea7 A |
716 | /* |
717 | * -group gname functions -- | |
718 | * | |
719 | * True if the file belongs to the group gname. If gname is numeric and | |
720 | * an equivalent of the getgrnam() function does not return a valid group | |
721 | * name, gname is taken as a group ID. | |
722 | */ | |
723 | int | |
724 | f_group(plan, entry) | |
725 | PLAN *plan; | |
726 | FTSENT *entry; | |
727 | { | |
c0fcf4e1 | 728 | return entry->fts_statp->st_gid == plan->g_data; |
44bd5ea7 | 729 | } |
c0fcf4e1 | 730 | |
44bd5ea7 | 731 | PLAN * |
c0fcf4e1 A |
732 | c_group(option, argvp) |
733 | OPTION *option; | |
44bd5ea7 | 734 | char ***argvp; |
44bd5ea7 | 735 | { |
c0fcf4e1 | 736 | char *gname; |
44bd5ea7 A |
737 | PLAN *new; |
738 | struct group *g; | |
739 | gid_t gid; | |
c0fcf4e1 A |
740 | |
741 | gname = nextarg(option, argvp); | |
44bd5ea7 A |
742 | ftsoptions &= ~FTS_NOSTAT; |
743 | ||
744 | g = getgrnam(gname); | |
745 | if (g == NULL) { | |
746 | gid = atoi(gname); | |
747 | if (gid == 0 && gname[0] != '0') | |
c0fcf4e1 | 748 | errx(1, "%s: %s: no such group", option->name, gname); |
44bd5ea7 A |
749 | } else |
750 | gid = g->gr_gid; | |
c0fcf4e1 A |
751 | |
752 | new = palloc(option); | |
44bd5ea7 | 753 | new->g_data = gid; |
c0fcf4e1 | 754 | return new; |
44bd5ea7 A |
755 | } |
756 | ||
757 | /* | |
758 | * -inum n functions -- | |
759 | * | |
760 | * True if the file has inode # n. | |
761 | */ | |
762 | int | |
763 | f_inum(plan, entry) | |
764 | PLAN *plan; | |
765 | FTSENT *entry; | |
766 | { | |
767 | COMPARE(entry->fts_statp->st_ino, plan->i_data); | |
768 | } | |
c0fcf4e1 | 769 | |
44bd5ea7 | 770 | PLAN * |
c0fcf4e1 A |
771 | c_inum(option, argvp) |
772 | OPTION *option; | |
44bd5ea7 | 773 | char ***argvp; |
44bd5ea7 | 774 | { |
c0fcf4e1 | 775 | char *inum_str; |
44bd5ea7 | 776 | PLAN *new; |
c0fcf4e1 A |
777 | |
778 | inum_str = nextarg(option, argvp); | |
44bd5ea7 | 779 | ftsoptions &= ~FTS_NOSTAT; |
c0fcf4e1 A |
780 | |
781 | new = palloc(option); | |
782 | new->i_data = find_parsenum(new, option->name, inum_str, NULL); | |
783 | return new; | |
44bd5ea7 | 784 | } |
c0fcf4e1 | 785 | |
44bd5ea7 A |
786 | /* |
787 | * -links n functions -- | |
788 | * | |
789 | * True if the file has n links. | |
790 | */ | |
791 | int | |
792 | f_links(plan, entry) | |
793 | PLAN *plan; | |
794 | FTSENT *entry; | |
795 | { | |
796 | COMPARE(entry->fts_statp->st_nlink, plan->l_data); | |
797 | } | |
c0fcf4e1 | 798 | |
44bd5ea7 | 799 | PLAN * |
c0fcf4e1 A |
800 | c_links(option, argvp) |
801 | OPTION *option; | |
44bd5ea7 | 802 | char ***argvp; |
44bd5ea7 | 803 | { |
c0fcf4e1 | 804 | char *nlinks; |
44bd5ea7 | 805 | PLAN *new; |
c0fcf4e1 A |
806 | |
807 | nlinks = nextarg(option, argvp); | |
44bd5ea7 | 808 | ftsoptions &= ~FTS_NOSTAT; |
c0fcf4e1 A |
809 | |
810 | new = palloc(option); | |
811 | new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL); | |
812 | return new; | |
44bd5ea7 | 813 | } |
c0fcf4e1 | 814 | |
44bd5ea7 A |
815 | /* |
816 | * -ls functions -- | |
817 | * | |
818 | * Always true - prints the current entry to stdout in "ls" format. | |
819 | */ | |
820 | int | |
821 | f_ls(plan, entry) | |
822 | PLAN *plan; | |
823 | FTSENT *entry; | |
824 | { | |
825 | printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); | |
c0fcf4e1 | 826 | return 1; |
44bd5ea7 A |
827 | } |
828 | ||
44bd5ea7 | 829 | PLAN * |
c0fcf4e1 A |
830 | c_ls(option, argvp) |
831 | OPTION *option; | |
44bd5ea7 | 832 | char ***argvp; |
44bd5ea7 | 833 | { |
44bd5ea7 | 834 | ftsoptions &= ~FTS_NOSTAT; |
c0fcf4e1 | 835 | isoutput = 1; |
44bd5ea7 | 836 | |
c0fcf4e1 | 837 | return palloc(option); |
44bd5ea7 A |
838 | } |
839 | ||
840 | /* | |
841 | * -name functions -- | |
842 | * | |
843 | * True if the basename of the filename being examined | |
844 | * matches pattern using Pattern Matching Notation S3.14 | |
845 | */ | |
846 | int | |
847 | f_name(plan, entry) | |
848 | PLAN *plan; | |
849 | FTSENT *entry; | |
850 | { | |
c0fcf4e1 A |
851 | return !fnmatch(plan->c_data, entry->fts_name, |
852 | plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); | |
44bd5ea7 | 853 | } |
c0fcf4e1 | 854 | |
44bd5ea7 | 855 | PLAN * |
c0fcf4e1 A |
856 | c_name(option, argvp) |
857 | OPTION *option; | |
44bd5ea7 | 858 | char ***argvp; |
44bd5ea7 | 859 | { |
c0fcf4e1 | 860 | char *pattern; |
44bd5ea7 A |
861 | PLAN *new; |
862 | ||
c0fcf4e1 A |
863 | pattern = nextarg(option, argvp); |
864 | new = palloc(option); | |
44bd5ea7 | 865 | new->c_data = pattern; |
c0fcf4e1 | 866 | return new; |
44bd5ea7 | 867 | } |
c0fcf4e1 | 868 | |
44bd5ea7 A |
869 | /* |
870 | * -newer file functions -- | |
871 | * | |
872 | * True if the current file has been modified more recently | |
873 | * then the modification time of the file named by the pathname | |
874 | * file. | |
875 | */ | |
876 | int | |
877 | f_newer(plan, entry) | |
878 | PLAN *plan; | |
879 | FTSENT *entry; | |
880 | { | |
c0fcf4e1 A |
881 | if (plan->flags & F_TIME_C) |
882 | return entry->fts_statp->st_ctime > plan->t_data; | |
883 | else if (plan->flags & F_TIME_A) | |
884 | return entry->fts_statp->st_atime > plan->t_data; | |
885 | else | |
886 | return entry->fts_statp->st_mtime > plan->t_data; | |
44bd5ea7 | 887 | } |
c0fcf4e1 | 888 | |
44bd5ea7 | 889 | PLAN * |
c0fcf4e1 A |
890 | c_newer(option, argvp) |
891 | OPTION *option; | |
44bd5ea7 | 892 | char ***argvp; |
44bd5ea7 | 893 | { |
c0fcf4e1 | 894 | char *fn_or_tspec; |
44bd5ea7 A |
895 | PLAN *new; |
896 | struct stat sb; | |
c0fcf4e1 A |
897 | |
898 | fn_or_tspec = nextarg(option, argvp); | |
44bd5ea7 A |
899 | ftsoptions &= ~FTS_NOSTAT; |
900 | ||
c0fcf4e1 A |
901 | new = palloc(option); |
902 | /* compare against what */ | |
903 | if (option->flags & F_TIME2_T) { | |
904 | new->t_data = get_date(fn_or_tspec, (struct timeb *) 0); | |
905 | if (new->t_data == (time_t) -1) | |
906 | errx(1, "Can't parse date/time: %s", fn_or_tspec); | |
907 | } else { | |
908 | if (stat(fn_or_tspec, &sb)) | |
909 | err(1, "%s", fn_or_tspec); | |
910 | if (option->flags & F_TIME2_C) | |
911 | new->t_data = sb.st_ctime; | |
912 | else if (option->flags & F_TIME2_A) | |
913 | new->t_data = sb.st_atime; | |
914 | else | |
915 | new->t_data = sb.st_mtime; | |
916 | } | |
917 | return new; | |
44bd5ea7 | 918 | } |
c0fcf4e1 | 919 | |
44bd5ea7 A |
920 | /* |
921 | * -nogroup functions -- | |
922 | * | |
923 | * True if file belongs to a user ID for which the equivalent | |
924 | * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. | |
925 | */ | |
926 | int | |
927 | f_nogroup(plan, entry) | |
928 | PLAN *plan; | |
929 | FTSENT *entry; | |
930 | { | |
c0fcf4e1 | 931 | return group_from_gid(entry->fts_statp->st_gid, 1) == NULL; |
44bd5ea7 | 932 | } |
c0fcf4e1 | 933 | |
44bd5ea7 | 934 | PLAN * |
c0fcf4e1 A |
935 | c_nogroup(option, argvp) |
936 | OPTION *option; | |
44bd5ea7 | 937 | char ***argvp; |
44bd5ea7 A |
938 | { |
939 | ftsoptions &= ~FTS_NOSTAT; | |
940 | ||
c0fcf4e1 | 941 | return palloc(option); |
44bd5ea7 | 942 | } |
c0fcf4e1 | 943 | |
44bd5ea7 A |
944 | /* |
945 | * -nouser functions -- | |
946 | * | |
947 | * True if file belongs to a user ID for which the equivalent | |
948 | * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. | |
949 | */ | |
950 | int | |
951 | f_nouser(plan, entry) | |
952 | PLAN *plan; | |
953 | FTSENT *entry; | |
954 | { | |
c0fcf4e1 | 955 | return user_from_uid(entry->fts_statp->st_uid, 1) == NULL; |
44bd5ea7 | 956 | } |
c0fcf4e1 | 957 | |
44bd5ea7 | 958 | PLAN * |
c0fcf4e1 A |
959 | c_nouser(option, argvp) |
960 | OPTION *option; | |
44bd5ea7 | 961 | char ***argvp; |
44bd5ea7 A |
962 | { |
963 | ftsoptions &= ~FTS_NOSTAT; | |
964 | ||
c0fcf4e1 | 965 | return palloc(option); |
44bd5ea7 | 966 | } |
c0fcf4e1 | 967 | |
44bd5ea7 A |
968 | /* |
969 | * -path functions -- | |
970 | * | |
971 | * True if the path of the filename being examined | |
972 | * matches pattern using Pattern Matching Notation S3.14 | |
973 | */ | |
974 | int | |
975 | f_path(plan, entry) | |
976 | PLAN *plan; | |
977 | FTSENT *entry; | |
978 | { | |
c0fcf4e1 A |
979 | return !fnmatch(plan->c_data, entry->fts_path, |
980 | plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); | |
44bd5ea7 | 981 | } |
44bd5ea7 | 982 | |
c0fcf4e1 A |
983 | /* c_path is the same as c_name */ |
984 | ||
44bd5ea7 A |
985 | /* |
986 | * -perm functions -- | |
987 | * | |
988 | * The mode argument is used to represent file mode bits. If it starts | |
989 | * with a leading digit, it's treated as an octal mode, otherwise as a | |
990 | * symbolic mode. | |
991 | */ | |
992 | int | |
993 | f_perm(plan, entry) | |
994 | PLAN *plan; | |
995 | FTSENT *entry; | |
996 | { | |
997 | mode_t mode; | |
998 | ||
999 | mode = entry->fts_statp->st_mode & | |
1000 | (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); | |
c0fcf4e1 A |
1001 | if (plan->flags & F_ATLEAST) |
1002 | return (plan->m_data | mode) == mode; | |
1003 | else if (plan->flags & F_ANY) | |
1004 | return (mode & plan->m_data); | |
44bd5ea7 | 1005 | else |
c0fcf4e1 | 1006 | return mode == plan->m_data; |
44bd5ea7 A |
1007 | /* NOTREACHED */ |
1008 | } | |
c0fcf4e1 | 1009 | |
44bd5ea7 | 1010 | PLAN * |
c0fcf4e1 A |
1011 | c_perm(option, argvp) |
1012 | OPTION *option; | |
44bd5ea7 | 1013 | char ***argvp; |
44bd5ea7 | 1014 | { |
c0fcf4e1 | 1015 | char *perm; |
44bd5ea7 A |
1016 | PLAN *new; |
1017 | mode_t *set; | |
1018 | ||
c0fcf4e1 | 1019 | perm = nextarg(option, argvp); |
44bd5ea7 A |
1020 | ftsoptions &= ~FTS_NOSTAT; |
1021 | ||
c0fcf4e1 | 1022 | new = palloc(option); |
44bd5ea7 A |
1023 | |
1024 | if (*perm == '-') { | |
c0fcf4e1 A |
1025 | new->flags |= F_ATLEAST; |
1026 | ++perm; | |
1027 | } else if (*perm == '+') { | |
1028 | new->flags |= F_ANY; | |
44bd5ea7 A |
1029 | ++perm; |
1030 | } | |
1031 | ||
1032 | if ((set = setmode(perm)) == NULL) | |
c0fcf4e1 | 1033 | errx(1, "%s: %s: illegal mode string", option->name, perm); |
44bd5ea7 A |
1034 | |
1035 | new->m_data = getmode(set, 0); | |
c0fcf4e1 A |
1036 | free(set); |
1037 | return new; | |
44bd5ea7 | 1038 | } |
c0fcf4e1 | 1039 | |
44bd5ea7 A |
1040 | /* |
1041 | * -print functions -- | |
1042 | * | |
1043 | * Always true, causes the current pathame to be written to | |
1044 | * standard output. | |
1045 | */ | |
1046 | int | |
1047 | f_print(plan, entry) | |
1048 | PLAN *plan; | |
1049 | FTSENT *entry; | |
1050 | { | |
c0fcf4e1 A |
1051 | (void)puts(entry->fts_path); |
1052 | return 1; | |
44bd5ea7 A |
1053 | } |
1054 | ||
44bd5ea7 | 1055 | PLAN * |
c0fcf4e1 A |
1056 | c_print(option, argvp) |
1057 | OPTION *option; | |
44bd5ea7 | 1058 | char ***argvp; |
44bd5ea7 A |
1059 | { |
1060 | isoutput = 1; | |
1061 | ||
c0fcf4e1 | 1062 | return palloc(option); |
44bd5ea7 A |
1063 | } |
1064 | ||
c0fcf4e1 A |
1065 | /* |
1066 | * -print0 functions -- | |
1067 | * | |
1068 | * Always true, causes the current pathame to be written to | |
1069 | * standard output followed by a NUL character | |
1070 | */ | |
1071 | int | |
1072 | f_print0(plan, entry) | |
1073 | PLAN *plan; | |
1074 | FTSENT *entry; | |
44bd5ea7 | 1075 | { |
c0fcf4e1 A |
1076 | fputs(entry->fts_path, stdout); |
1077 | fputc('\0', stdout); | |
1078 | return 1; | |
44bd5ea7 | 1079 | } |
c0fcf4e1 A |
1080 | |
1081 | /* c_print0 is the same as c_print */ | |
1082 | ||
44bd5ea7 A |
1083 | /* |
1084 | * -prune functions -- | |
1085 | * | |
1086 | * Prune a portion of the hierarchy. | |
1087 | */ | |
1088 | int | |
1089 | f_prune(plan, entry) | |
1090 | PLAN *plan; | |
1091 | FTSENT *entry; | |
1092 | { | |
1093 | extern FTS *tree; | |
1094 | ||
1095 | if (fts_set(tree, entry, FTS_SKIP)) | |
1096 | err(1, "%s", entry->fts_path); | |
c0fcf4e1 | 1097 | return 1; |
44bd5ea7 | 1098 | } |
c0fcf4e1 A |
1099 | |
1100 | /* c_prune == c_simple */ | |
1101 | ||
1102 | /* | |
1103 | * -regex functions -- | |
1104 | * | |
1105 | * True if the whole path of the file matches pattern using | |
1106 | * regular expression. | |
1107 | */ | |
1108 | int | |
1109 | f_regex(plan, entry) | |
1110 | PLAN *plan; | |
1111 | FTSENT *entry; | |
1112 | { | |
1113 | char *str; | |
1114 | size_t len; | |
1115 | regex_t *pre; | |
1116 | regmatch_t pmatch; | |
1117 | int errcode; | |
1118 | char errbuf[LINE_MAX]; | |
1119 | int matched; | |
1120 | ||
1121 | pre = plan->re_data; | |
1122 | str = entry->fts_path; | |
1123 | len = strlen(str); | |
1124 | matched = 0; | |
1125 | ||
1126 | pmatch.rm_so = 0; | |
1127 | pmatch.rm_eo = len; | |
1128 | ||
1129 | errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); | |
1130 | ||
1131 | if (errcode != 0 && errcode != REG_NOMATCH) { | |
1132 | regerror(errcode, pre, errbuf, sizeof errbuf); | |
1133 | errx(1, "%s: %s", | |
1134 | plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf); | |
1135 | } | |
1136 | ||
1137 | if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) | |
1138 | matched = 1; | |
1139 | ||
1140 | return matched; | |
1141 | } | |
1142 | ||
44bd5ea7 | 1143 | PLAN * |
c0fcf4e1 A |
1144 | c_regex(option, argvp) |
1145 | OPTION *option; | |
44bd5ea7 | 1146 | char ***argvp; |
44bd5ea7 | 1147 | { |
c0fcf4e1 A |
1148 | PLAN *new; |
1149 | char *pattern; | |
1150 | regex_t *pre; | |
1151 | int errcode; | |
1152 | char errbuf[LINE_MAX]; | |
1153 | ||
1154 | if ((pre = malloc(sizeof(regex_t))) == NULL) | |
1155 | err(1, NULL); | |
1156 | ||
1157 | pattern = nextarg(option, argvp); | |
1158 | ||
1159 | if ((errcode = regcomp(pre, pattern, | |
1160 | regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) { | |
1161 | regerror(errcode, pre, errbuf, sizeof errbuf); | |
1162 | errx(1, "%s: %s: %s", | |
1163 | option->flags & F_IGNCASE ? "-iregex" : "-regex", | |
1164 | pattern, errbuf); | |
1165 | } | |
1166 | ||
1167 | new = palloc(option); | |
1168 | new->re_data = pre; | |
1169 | ||
1170 | return new; | |
1171 | } | |
1172 | ||
1173 | /* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or */ | |
1174 | ||
1175 | PLAN * | |
1176 | c_simple(option, argvp) | |
1177 | OPTION *option; | |
1178 | char ***argvp; | |
1179 | { | |
1180 | return palloc(option); | |
44bd5ea7 | 1181 | } |
c0fcf4e1 | 1182 | |
44bd5ea7 A |
1183 | /* |
1184 | * -size n[c] functions -- | |
1185 | * | |
1186 | * True if the file size in bytes, divided by an implementation defined | |
1187 | * value and rounded up to the next integer, is n. If n is followed by | |
1188 | * a c, the size is in bytes. | |
1189 | */ | |
1190 | #define FIND_SIZE 512 | |
1191 | static int divsize = 1; | |
1192 | ||
1193 | int | |
1194 | f_size(plan, entry) | |
1195 | PLAN *plan; | |
1196 | FTSENT *entry; | |
1197 | { | |
1198 | off_t size; | |
1199 | ||
1200 | size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / | |
1201 | FIND_SIZE : entry->fts_statp->st_size; | |
1202 | COMPARE(size, plan->o_data); | |
1203 | } | |
c0fcf4e1 | 1204 | |
44bd5ea7 | 1205 | PLAN * |
c0fcf4e1 A |
1206 | c_size(option, argvp) |
1207 | OPTION *option; | |
44bd5ea7 | 1208 | char ***argvp; |
44bd5ea7 | 1209 | { |
c0fcf4e1 | 1210 | char *size_str; |
44bd5ea7 A |
1211 | PLAN *new; |
1212 | char endch; | |
c0fcf4e1 A |
1213 | |
1214 | size_str = nextarg(option, argvp); | |
44bd5ea7 A |
1215 | ftsoptions &= ~FTS_NOSTAT; |
1216 | ||
c0fcf4e1 | 1217 | new = palloc(option); |
44bd5ea7 | 1218 | endch = 'c'; |
c0fcf4e1 | 1219 | new->o_data = find_parsenum(new, option->name, size_str, &endch); |
44bd5ea7 A |
1220 | if (endch == 'c') |
1221 | divsize = 0; | |
c0fcf4e1 | 1222 | return new; |
44bd5ea7 | 1223 | } |
c0fcf4e1 | 1224 | |
44bd5ea7 A |
1225 | /* |
1226 | * -type c functions -- | |
1227 | * | |
1228 | * True if the type of the file is c, where c is b, c, d, p, f or w | |
1229 | * for block special file, character special file, directory, FIFO, | |
1230 | * regular file or whiteout respectively. | |
1231 | */ | |
1232 | int | |
1233 | f_type(plan, entry) | |
1234 | PLAN *plan; | |
1235 | FTSENT *entry; | |
1236 | { | |
c0fcf4e1 | 1237 | return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data; |
44bd5ea7 | 1238 | } |
c0fcf4e1 | 1239 | |
44bd5ea7 | 1240 | PLAN * |
c0fcf4e1 A |
1241 | c_type(option, argvp) |
1242 | OPTION *option; | |
44bd5ea7 | 1243 | char ***argvp; |
44bd5ea7 | 1244 | { |
c0fcf4e1 | 1245 | char *typestring; |
44bd5ea7 | 1246 | PLAN *new; |
c0fcf4e1 A |
1247 | mode_t mask; |
1248 | ||
1249 | typestring = nextarg(option, argvp); | |
44bd5ea7 A |
1250 | ftsoptions &= ~FTS_NOSTAT; |
1251 | ||
1252 | switch (typestring[0]) { | |
44bd5ea7 A |
1253 | case 'b': |
1254 | mask = S_IFBLK; | |
1255 | break; | |
1256 | case 'c': | |
1257 | mask = S_IFCHR; | |
1258 | break; | |
1259 | case 'd': | |
1260 | mask = S_IFDIR; | |
1261 | break; | |
1262 | case 'f': | |
1263 | mask = S_IFREG; | |
1264 | break; | |
1265 | case 'l': | |
1266 | mask = S_IFLNK; | |
1267 | break; | |
1268 | case 'p': | |
1269 | mask = S_IFIFO; | |
1270 | break; | |
1271 | case 's': | |
1272 | mask = S_IFSOCK; | |
1273 | break; | |
1274 | #ifdef FTS_WHITEOUT | |
1275 | case 'w': | |
1276 | mask = S_IFWHT; | |
1277 | ftsoptions |= FTS_WHITEOUT; | |
1278 | break; | |
1279 | #endif /* FTS_WHITEOUT */ | |
1280 | default: | |
c0fcf4e1 | 1281 | errx(1, "%s: %s: unknown type", option->name, typestring); |
44bd5ea7 | 1282 | } |
c0fcf4e1 A |
1283 | |
1284 | new = palloc(option); | |
44bd5ea7 | 1285 | new->m_data = mask; |
c0fcf4e1 | 1286 | return new; |
44bd5ea7 | 1287 | } |
c0fcf4e1 | 1288 | |
44bd5ea7 A |
1289 | /* |
1290 | * -user uname functions -- | |
1291 | * | |
1292 | * True if the file belongs to the user uname. If uname is numeric and | |
1293 | * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not | |
1294 | * return a valid user name, uname is taken as a user ID. | |
1295 | */ | |
1296 | int | |
1297 | f_user(plan, entry) | |
1298 | PLAN *plan; | |
1299 | FTSENT *entry; | |
1300 | { | |
c0fcf4e1 | 1301 | return entry->fts_statp->st_uid == plan->u_data; |
44bd5ea7 | 1302 | } |
c0fcf4e1 | 1303 | |
44bd5ea7 | 1304 | PLAN * |
c0fcf4e1 A |
1305 | c_user(option, argvp) |
1306 | OPTION *option; | |
44bd5ea7 | 1307 | char ***argvp; |
44bd5ea7 | 1308 | { |
c0fcf4e1 | 1309 | char *username; |
44bd5ea7 A |
1310 | PLAN *new; |
1311 | struct passwd *p; | |
1312 | uid_t uid; | |
c0fcf4e1 A |
1313 | |
1314 | username = nextarg(option, argvp); | |
44bd5ea7 A |
1315 | ftsoptions &= ~FTS_NOSTAT; |
1316 | ||
1317 | p = getpwnam(username); | |
1318 | if (p == NULL) { | |
1319 | uid = atoi(username); | |
1320 | if (uid == 0 && username[0] != '0') | |
c0fcf4e1 | 1321 | errx(1, "%s: %s: no such user", option->name, username); |
44bd5ea7 A |
1322 | } else |
1323 | uid = p->pw_uid; | |
1324 | ||
c0fcf4e1 | 1325 | new = palloc(option); |
44bd5ea7 | 1326 | new->u_data = uid; |
c0fcf4e1 | 1327 | return new; |
44bd5ea7 | 1328 | } |
c0fcf4e1 | 1329 | |
44bd5ea7 A |
1330 | /* |
1331 | * -xdev functions -- | |
1332 | * | |
1333 | * Always true, causes find not to decend past directories that have a | |
1334 | * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) | |
1335 | */ | |
1336 | PLAN * | |
c0fcf4e1 A |
1337 | c_xdev(option, argvp) |
1338 | OPTION *option; | |
44bd5ea7 | 1339 | char ***argvp; |
44bd5ea7 A |
1340 | { |
1341 | ftsoptions |= FTS_XDEV; | |
1342 | ||
c0fcf4e1 | 1343 | return palloc(option); |
44bd5ea7 A |
1344 | } |
1345 | ||
1346 | /* | |
1347 | * ( expression ) functions -- | |
1348 | * | |
1349 | * True if expression is true. | |
1350 | */ | |
1351 | int | |
1352 | f_expr(plan, entry) | |
1353 | PLAN *plan; | |
1354 | FTSENT *entry; | |
1355 | { | |
c0fcf4e1 A |
1356 | register PLAN *p; |
1357 | register int state = 0; | |
44bd5ea7 | 1358 | |
44bd5ea7 | 1359 | for (p = plan->p_data[0]; |
c0fcf4e1 A |
1360 | p && (state = (p->execute)(p, entry)); p = p->next); |
1361 | return state; | |
44bd5ea7 | 1362 | } |
c0fcf4e1 | 1363 | |
44bd5ea7 | 1364 | /* |
c0fcf4e1 | 1365 | * f_openparen and f_closeparen nodes are temporary place markers. They are |
44bd5ea7 | 1366 | * eliminated during phase 2 of find_formplan() --- the '(' node is converted |
c0fcf4e1 A |
1367 | * to a f_expr node containing the expression and the ')' node is discarded. |
1368 | * The functions themselves are only used as constants. | |
44bd5ea7 | 1369 | */ |
c0fcf4e1 A |
1370 | |
1371 | int | |
1372 | f_openparen(plan, entry) | |
1373 | PLAN *plan; | |
1374 | FTSENT *entry; | |
1375 | { | |
1376 | abort(); | |
1377 | } | |
1378 | ||
1379 | int | |
1380 | f_closeparen(plan, entry) | |
1381 | PLAN *plan; | |
1382 | FTSENT *entry; | |
44bd5ea7 | 1383 | { |
c0fcf4e1 | 1384 | abort(); |
44bd5ea7 | 1385 | } |
c0fcf4e1 A |
1386 | |
1387 | /* c_openparen == c_simple */ | |
1388 | /* c_closeparen == c_simple */ | |
1389 | ||
1390 | /* | |
1391 | * AND operator. Since AND is implicit, no node is allocated. | |
1392 | */ | |
44bd5ea7 | 1393 | PLAN * |
c0fcf4e1 A |
1394 | c_and(option, argvp) |
1395 | OPTION *option; | |
44bd5ea7 | 1396 | char ***argvp; |
44bd5ea7 | 1397 | { |
c0fcf4e1 | 1398 | return NULL; |
44bd5ea7 | 1399 | } |
c0fcf4e1 | 1400 | |
44bd5ea7 A |
1401 | /* |
1402 | * ! expression functions -- | |
1403 | * | |
1404 | * Negation of a primary; the unary NOT operator. | |
1405 | */ | |
1406 | int | |
1407 | f_not(plan, entry) | |
1408 | PLAN *plan; | |
1409 | FTSENT *entry; | |
1410 | { | |
c0fcf4e1 A |
1411 | register PLAN *p; |
1412 | register int state = 0; | |
44bd5ea7 | 1413 | |
44bd5ea7 | 1414 | for (p = plan->p_data[0]; |
c0fcf4e1 A |
1415 | p && (state = (p->execute)(p, entry)); p = p->next); |
1416 | return !state; | |
44bd5ea7 | 1417 | } |
c0fcf4e1 A |
1418 | |
1419 | /* c_not == c_simple */ | |
1420 | ||
44bd5ea7 A |
1421 | /* |
1422 | * expression -o expression functions -- | |
1423 | * | |
1424 | * Alternation of primaries; the OR operator. The second expression is | |
1425 | * not evaluated if the first expression is true. | |
1426 | */ | |
1427 | int | |
1428 | f_or(plan, entry) | |
1429 | PLAN *plan; | |
1430 | FTSENT *entry; | |
1431 | { | |
c0fcf4e1 A |
1432 | register PLAN *p; |
1433 | register int state = 0; | |
44bd5ea7 | 1434 | |
44bd5ea7 | 1435 | for (p = plan->p_data[0]; |
c0fcf4e1 | 1436 | p && (state = (p->execute)(p, entry)); p = p->next); |
44bd5ea7 A |
1437 | |
1438 | if (state) | |
c0fcf4e1 | 1439 | return 1; |
44bd5ea7 A |
1440 | |
1441 | for (p = plan->p_data[1]; | |
c0fcf4e1 A |
1442 | p && (state = (p->execute)(p, entry)); p = p->next); |
1443 | return state; | |
44bd5ea7 A |
1444 | } |
1445 | ||
c0fcf4e1 | 1446 | /* c_or == c_simple */ |