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