source: trunk/essentials/sys-apps/findutils/find/parser.c

Last change on this file was 3170, checked in by bird, 18 years ago

findutils 4.3.2

File size: 82.7 KB
Line 
1/* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2001, 2003,
3 2004, 2005, 2006 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 USA.
19*/
20
21
22#include "defs.h"
23#include <ctype.h>
24#include <assert.h>
25#include <pwd.h>
26#include <grp.h>
27#include <fnmatch.h>
28#include "modechange.h"
29#include "modetype.h"
30#include "xstrtol.h"
31#include "xalloc.h"
32#include "quote.h"
33#include "quotearg.h"
34#include "buildcmd.h"
35#include "nextelem.h"
36#include "stdio-safer.h"
37#include "regextype.h"
38
39#ifdef HAVE_FCNTL_H
40#include <fcntl.h>
41#else
42#include <sys/file.h>
43#endif
44
45/* The presence of unistd.h is assumed by gnulib these days, so we
46 * might as well assume it too.
47 */
48/* We need <unistd.h> for isatty(). */
49#include <unistd.h>
50
51#if ENABLE_NLS
52# include <libintl.h>
53# define _(Text) gettext (Text)
54#else
55# define _(Text) Text
56#endif
57#ifdef gettext_noop
58# define N_(String) gettext_noop (String)
59#else
60/* See locate.c for explanation as to why not use (String) */
61# define N_(String) String
62#endif
63
64#if !defined (isascii) || defined (STDC_HEADERS)
65#ifdef isascii
66#undef isascii
67#endif
68#define isascii(c) 1
69#endif
70
71#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
72#define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
73
74#ifndef HAVE_ENDGRENT
75#define endgrent()
76#endif
77#ifndef HAVE_ENDPWENT
78#define endpwent()
79#endif
80
81
82static boolean parse_accesscheck PARAMS((const struct parser_table* entry, char **argv, int *arg_ptr));
83static boolean parse_amin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
84static boolean parse_and PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
85static boolean parse_anewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
86static boolean parse_cmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
87static boolean parse_cnewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
88static boolean parse_comma PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
89static boolean parse_daystart PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
90static boolean parse_delete PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
91static boolean parse_d PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
92static boolean parse_depth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
93static boolean parse_empty PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
94static boolean parse_exec PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
95static boolean parse_execdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
96static boolean parse_false PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
97static boolean parse_fls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
98static boolean parse_fprintf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
99static boolean parse_follow PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
100static boolean parse_fprint PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
101static boolean parse_fprint0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
102static boolean parse_fstype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
103static boolean parse_gid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
104static boolean parse_group PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
105static boolean parse_help PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
106static boolean parse_ilname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
107static boolean parse_iname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
108static boolean parse_inum PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
109static boolean parse_ipath PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
110static boolean parse_iregex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
111static boolean parse_iwholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
112static boolean parse_links PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
113static boolean parse_lname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
114static boolean parse_ls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
115static boolean parse_maxdepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
116static boolean parse_mindepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
117static boolean parse_mmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
118static boolean parse_name PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
119static boolean parse_negate PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
120static boolean parse_newer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
121static boolean parse_noleaf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
122static boolean parse_nogroup PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
123static boolean parse_nouser PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
124static boolean parse_nowarn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
125static boolean parse_ok PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
126static boolean parse_okdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
127static boolean parse_or PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
128static boolean parse_path PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
129static boolean parse_perm PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
130static boolean parse_print0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
131static boolean parse_printf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
132static boolean parse_prune PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
133static boolean parse_regex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
134static boolean parse_regextype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
135static boolean parse_samefile PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
136#if 0
137static boolean parse_show_control_chars PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
138#endif
139static boolean parse_size PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
140static boolean parse_time PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
141static boolean parse_true PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
142static boolean parse_type PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
143static boolean parse_uid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
144static boolean parse_used PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
145static boolean parse_user PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
146static boolean parse_version PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
147static boolean parse_wholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
148static boolean parse_xdev PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
149static boolean parse_ignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
150static boolean parse_noignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
151static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
152static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
153static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
154
155boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
156
157
158static boolean insert_type PARAMS((char **argv, int *arg_ptr, const struct parser_table *entry, PRED_FUNC which_pred));
159static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, const struct parser_table *entry, int regex_options));
160static boolean insert_fprintf PARAMS((FILE *fp, const struct parser_table *entry, PRED_FUNC func, char *argv[], int *arg_ptr));
161
162static struct segment **make_segment PARAMS((struct segment **segment, char *format, int len, int kind, struct predicate *pred));
163static boolean insert_exec_ok PARAMS((const char *action, const struct parser_table *entry, char *argv[], int *arg_ptr));
164static boolean get_num_days PARAMS((char *str, uintmax_t *num_days, enum comparison_type *comp_type));
165static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type));
166static struct predicate* insert_num PARAMS((char *argv[], int *arg_ptr, const struct parser_table *entry));
167static FILE *open_output_file PARAMS((char *path));
168static boolean stream_is_tty(FILE *fp);
169static boolean parse_noop PARAMS((const struct parser_table* entry, char **argv, int *arg_ptr));
170
171#define PASTE(x,y) x##y
172#define STRINGIFY(s) #s
173
174#define PARSE_OPTION(what,suffix) \
175 { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL }
176
177#define PARSE_POSOPT(what,suffix) \
178 { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL }
179
180#define PARSE_TEST(what,suffix) \
181 { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
182
183#define PARSE_TEST_NP(what,suffix) \
184 { (ARG_TEST), (what), PASTE(parse_,suffix), NULL }
185
186#define PARSE_ACTION(what,suffix) \
187 { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
188
189#define PARSE_ACTION_NP(what,suffix) \
190 { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL }
191
192#define PARSE_PUNCTUATION(what,suffix) \
193 { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
194
195
196/* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
197 If they are in some Unix versions of find, they are marked `Unix'. */
198
199static struct parser_table const parse_table[] =
200{
201 PARSE_PUNCTUATION("!", negate),
202 PARSE_PUNCTUATION("not", negate), /* GNU */
203 PARSE_PUNCTUATION("(", open),
204 PARSE_PUNCTUATION(")", close),
205 PARSE_PUNCTUATION(",", comma), /* GNU */
206 PARSE_PUNCTUATION("a", and),
207 PARSE_TEST ("amin", amin), /* GNU */
208 PARSE_PUNCTUATION("and", and), /* GNU */
209 PARSE_TEST ("anewer", anewer), /* GNU */
210 {ARG_TEST, "atime", parse_time, pred_atime},
211 PARSE_TEST ("cmin", cmin), /* GNU */
212 PARSE_TEST ("cnewer", cnewer), /* GNU */
213 {ARG_TEST, "ctime", parse_time, pred_ctime},
214 PARSE_POSOPT ("daystart", daystart), /* GNU */
215 PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
216 PARSE_OPTION ("d", d), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
217 PARSE_OPTION ("depth", depth),
218 PARSE_TEST ("empty", empty), /* GNU */
219 {ARG_ACTION, "exec", parse_exec, pred_exec}, /* POSIX */
220 {ARG_TEST, "executable", parse_accesscheck, pred_executable}, /* GNU, 4.3.0+ */
221 PARSE_ACTION ("execdir", execdir), /* *BSD, GNU */
222 PARSE_ACTION ("fls", fls), /* GNU */
223 PARSE_POSOPT ("follow", follow), /* GNU, Unix */
224 PARSE_ACTION ("fprint", fprint), /* GNU */
225 PARSE_ACTION ("fprint0", fprint0), /* GNU */
226 {ARG_ACTION, "fprintf", parse_fprintf, pred_fprintf}, /* GNU */
227 PARSE_TEST ("fstype", fstype), /* GNU, Unix */
228 PARSE_TEST ("gid", gid), /* GNU */
229 PARSE_TEST ("group", group),
230 PARSE_OPTION ("ignore_readdir_race", ignore_race), /* GNU */
231 PARSE_TEST ("ilname", ilname), /* GNU */
232 PARSE_TEST ("iname", iname), /* GNU */
233 PARSE_TEST ("inum", inum), /* GNU, Unix */
234 PARSE_TEST ("ipath", ipath), /* GNU, deprecated in favour of iwholename */
235 PARSE_TEST_NP ("iregex", iregex), /* GNU */
236 PARSE_TEST_NP ("iwholename", iwholename), /* GNU */
237 PARSE_TEST ("links", links),
238 PARSE_TEST ("lname", lname), /* GNU */
239 PARSE_ACTION ("ls", ls), /* GNU, Unix */
240 PARSE_OPTION ("maxdepth", maxdepth), /* GNU */
241 PARSE_OPTION ("mindepth", mindepth), /* GNU */
242 PARSE_TEST ("mmin", mmin), /* GNU */
243 PARSE_OPTION ("mount", xdev), /* Unix */
244 {ARG_TEST, "mtime", parse_time, pred_mtime},
245 PARSE_TEST ("name", name),
246#ifdef UNIMPLEMENTED_UNIX
247 PARSE(ARG_UNIMPLEMENTED, "ncpio", ncpio), /* Unix */
248#endif
249 PARSE_TEST ("newer", newer),
250 PARSE_OPTION ("noleaf", noleaf), /* GNU */
251 PARSE_TEST ("nogroup", nogroup),
252 PARSE_TEST ("nouser", nouser),
253 PARSE_OPTION ("noignore_readdir_race", noignore_race), /* GNU */
254 PARSE_POSOPT ("nowarn", nowarn), /* GNU */
255 PARSE_PUNCTUATION("o", or),
256 PARSE_PUNCTUATION("or", or), /* GNU */
257 PARSE_ACTION ("ok", ok),
258 PARSE_ACTION ("okdir", okdir), /* GNU (-execdir is BSD) */
259 PARSE_TEST ("path", path), /* GNU, HP-UX, GNU prefers wholename */
260 PARSE_TEST ("perm", perm),
261 PARSE_ACTION ("print", print),
262 PARSE_ACTION ("print0", print0), /* GNU */
263 {ARG_ACTION, "printf", parse_printf, NULL}, /* GNU */
264 PARSE_ACTION ("prune", prune),
265 PARSE_ACTION ("quit", quit), /* GNU */
266 {ARG_TEST, "readable", parse_accesscheck, pred_readable}, /* GNU, 4.3.0+ */
267 PARSE_TEST ("regex", regex), /* GNU */
268 PARSE_OPTION ("regextype", regextype), /* GNU */
269 PARSE_TEST ("samefile", samefile), /* GNU */
270#if 0
271 PARSE_OPTION ("show-control-chars", show_control_chars), /* GNU, 4.3.0+ */
272#endif
273 PARSE_TEST ("size", size),
274 PARSE_TEST ("type", type),
275 PARSE_TEST ("uid", uid), /* GNU */
276 PARSE_TEST ("used", used), /* GNU */
277 PARSE_TEST ("user", user),
278 PARSE_OPTION ("warn", warn), /* GNU */
279 PARSE_TEST_NP ("wholename", wholename), /* GNU, replaces -path */
280 {ARG_TEST, "writable", parse_accesscheck, pred_writable}, /* GNU, 4.3.0+ */
281 PARSE_OPTION ("xdev", xdev),
282 PARSE_TEST ("xtype", xtype), /* GNU */
283#ifdef UNIMPLEMENTED_UNIX
284 /* It's pretty ugly for find to know about archive formats.
285 Plus what it could do with cpio archives is very limited.
286 Better to leave it out. */
287 PARSE(ARG_UNIMPLEMENTED, "cpio", cpio), /* Unix */
288#endif
289 /* gnulib's stdbool.h might have made true and false into macros,
290 * so we can't leave named 'true' and 'false' tokens, so we have
291 * to expeant the relevant entries longhand.
292 */
293 {ARG_TEST, "false", parse_false, pred_false}, /* GNU */
294 {ARG_TEST, "true", parse_true, pred_true }, /* GNU */
295 {ARG_NOOP, "noop", NULL, pred_true }, /* GNU, internal use only */
296
297 /* Various other cases that don't fit neatly into our macro scheme. */
298 {ARG_TEST, "help", parse_help, NULL}, /* GNU */
299 {ARG_TEST, "-help", parse_help, NULL}, /* GNU */
300 {ARG_TEST, "version", parse_version, NULL}, /* GNU */
301 {ARG_TEST, "-version", parse_version, NULL}, /* GNU */
302 {0, 0, 0, 0}
303};
304
305
306
307static const char *first_nonoption_arg = NULL;
308static const struct parser_table *noop = NULL;
309
310
311
312
313static const struct parser_table*
314get_noop(void)
315{
316 int i;
317 if (NULL == noop)
318 {
319 for (i = 0; parse_table[i].parser_name != 0; i++)
320 {
321 if (ARG_NOOP ==parse_table[i].type)
322 {
323 noop = &(parse_table[i]);
324 break;
325 }
326 }
327 }
328 return noop;
329}
330
331
332
333
334void
335set_follow_state(enum SymlinkOption opt)
336{
337 if (options.debug_options & DebugStat)
338 {
339 /* For DebugStat, the choice is made at runtime within debug_stat()
340 * by checking the contents of the symlink_handling variable.
341 */
342 options.xstat = debug_stat;
343 }
344 else
345 {
346 switch (opt)
347 {
348 case SYMLINK_ALWAYS_DEREF: /* -L */
349 options.xstat = optionl_stat;
350 options.no_leaf_check = true;
351 break;
352
353 case SYMLINK_NEVER_DEREF: /* -P (default) */
354 options.xstat = optionp_stat;
355 /* Can't turn no_leaf_check off because the user might have specified
356 * -noleaf anyway
357 */
358 break;
359
360 case SYMLINK_DEREF_ARGSONLY: /* -H */
361 options.xstat = optionh_stat;
362 options.no_leaf_check = true;
363 }
364 }
365 options.symlink_handling = opt;
366}
367
368
369void
370parse_begin_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates)
371{
372 (void) args;
373 (void) argno;
374 (void) last;
375 (void) predicates;
376 first_nonoption_arg = NULL;
377}
378
379void
380parse_end_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates)
381{
382 /* does nothing */
383 (void) args;
384 (void) argno;
385 (void) last;
386 (void) predicates;
387}
388
389
390
391
392/* Return a pointer to the parser function to invoke for predicate
393 SEARCH_NAME.
394 Return NULL if SEARCH_NAME is not a valid predicate name. */
395
396const struct parser_table*
397find_parser (char *search_name)
398{
399 int i;
400 const char *original_arg = search_name;
401
402 if (*search_name == '-')
403 search_name++;
404 for (i = 0; parse_table[i].parser_name != 0; i++)
405 {
406 if (strcmp (parse_table[i].parser_name, search_name) == 0)
407 {
408 /* If this is an option, but we have already had a
409 * non-option argument, the user may be under the
410 * impression that the behaviour of the option
411 * argument is conditional on some preceding
412 * tests. This might typically be the case with,
413 * for example, -maxdepth.
414 *
415 * The options -daystart and -follow are exempt
416 * from this treatment, since their positioning
417 * in the command line does have an effect on
418 * subsequent tests but not previous ones. That
419 * might be intentional on the part of the user.
420 */
421 if (parse_table[i].type != ARG_POSITIONAL_OPTION)
422 {
423 /* Something other than -follow/-daystart.
424 * If this is an option, check if it followed
425 * a non-option and if so, issue a warning.
426 */
427 if (parse_table[i].type == ARG_OPTION)
428 {
429 if ((first_nonoption_arg != NULL)
430 && options.warnings )
431 {
432 /* option which follows a non-option */
433 error (0, 0,
434 _("warning: you have specified the %s "
435 "option after a non-option argument %s, "
436 "but options are not positional (%s affects "
437 "tests specified before it as well as those "
438 "specified after it). Please specify options "
439 "before other arguments.\n"),
440 original_arg,
441 first_nonoption_arg,
442 original_arg);
443 }
444 }
445 else
446 {
447 /* Not an option or a positional option,
448 * so remember we've seen it in order to
449 * use it in a possible future warning message.
450 */
451 if (first_nonoption_arg == NULL)
452 {
453 first_nonoption_arg = original_arg;
454 }
455 }
456 }
457
458 return &parse_table[i];
459 }
460 }
461 return NULL;
462}
463
464
465static float
466estimate_file_age_success_rate(float num_days)
467{
468 if (num_days < 0.1)
469 {
470 /* Assume 1% of files have timestamps in the future */
471 return 0.01f;
472 }
473 else if (num_days < 1)
474 {
475 /* Assume 30% of files have timestamps today */
476 return 0.3f;
477 }
478 else if (num_days > 100)
479 {
480 /* Assume 30% of files are very old */
481 return 0.3f;
482 }
483 else
484 {
485 /* Assume 39% of files are between 1 and 100 days old. */
486 return 0.39f;
487 }
488}
489
490
491static float
492estimate_timestamp_success_rate(time_t when)
493{
494 int num_days = (when - options.cur_day_start) / 86400;
495 return estimate_file_age_success_rate(num_days);
496}
497
498
499
500/* The parsers are responsible to continue scanning ARGV for
501 their arguments. Each parser knows what is and isn't
502 allowed for itself.
503
504 ARGV is the argument array.
505 *ARG_PTR is the index to start at in ARGV,
506 updated to point beyond the last element consumed.
507
508 The predicate structure is updated with the new information. */
509
510static boolean
511parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr)
512{
513 struct predicate *our_pred;
514 uintmax_t num;
515 enum comparison_type c_type;
516 time_t t;
517
518 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
519 return false;
520 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
521 return false;
522 t = options.cur_day_start + DAYSECS - num * 60;
523 our_pred = insert_primary (entry);
524 our_pred->args.info.kind = c_type;
525 our_pred->args.info.negative = t < 0;
526 our_pred->args.info.l_val = t;
527 our_pred->est_success_rate = estimate_file_age_success_rate(num);
528 (*arg_ptr)++;
529 return true;
530}
531
532static boolean
533parse_and (const struct parser_table* entry, char **argv, int *arg_ptr)
534{
535 struct predicate *our_pred;
536
537 (void) argv;
538 (void) arg_ptr;
539
540 our_pred = get_new_pred (entry);
541 our_pred->pred_func = pred_and;
542 our_pred->p_type = BI_OP;
543 our_pred->p_prec = AND_PREC;
544 our_pred->need_stat = our_pred->need_type = false;
545 return true;
546}
547
548static boolean
549parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr)
550{
551 struct predicate *our_pred;
552 struct stat stat_newer;
553
554 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
555 return false;
556 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
557 error (1, errno, "%s", argv[*arg_ptr]);
558 our_pred = insert_primary (entry);
559 our_pred->args.time = stat_newer.st_mtime;
560 our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
561 (*arg_ptr)++;
562 return true;
563}
564
565boolean
566parse_close (const struct parser_table* entry, char **argv, int *arg_ptr)
567{
568 struct predicate *our_pred;
569
570 (void) argv;
571 (void) arg_ptr;
572
573 our_pred = get_new_pred (entry);
574 our_pred->pred_func = pred_close;
575 our_pred->p_type = CLOSE_PAREN;
576 our_pred->p_prec = NO_PREC;
577 our_pred->need_stat = our_pred->need_type = false;
578 return true;
579}
580
581static boolean
582parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr)
583{
584 struct predicate *our_pred;
585 uintmax_t num;
586 enum comparison_type c_type;
587 time_t t;
588
589 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
590 return false;
591 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
592 return false;
593 t = options.cur_day_start + DAYSECS - num * 60;
594 our_pred = insert_primary (entry);
595 our_pred->args.info.kind = c_type;
596 our_pred->args.info.negative = t < 0;
597 our_pred->args.info.l_val = t;
598 our_pred->est_success_rate = estimate_file_age_success_rate(num);
599 (*arg_ptr)++;
600 return true;
601}
602
603static boolean
604parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr)
605{
606 struct predicate *our_pred;
607 struct stat stat_newer;
608
609 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
610 return false;
611 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
612 error (1, errno, "%s", argv[*arg_ptr]);
613 our_pred = insert_primary (entry);
614 our_pred->args.time = stat_newer.st_mtime;
615 our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
616 (*arg_ptr)++;
617 return true;
618}
619
620static boolean
621parse_comma (const struct parser_table* entry, char **argv, int *arg_ptr)
622{
623 struct predicate *our_pred;
624
625 (void) argv;
626 (void) arg_ptr;
627
628 our_pred = get_new_pred (entry);
629 our_pred->pred_func = pred_comma;
630 our_pred->p_type = BI_OP;
631 our_pred->p_prec = COMMA_PREC;
632 our_pred->need_stat = our_pred->need_type = false;
633 our_pred->est_success_rate = 1.0f;
634 return true;
635}
636
637static boolean
638parse_daystart (const struct parser_table* entry, char **argv, int *arg_ptr)
639{
640 struct tm *local;
641
642 (void) entry;
643 (void) argv;
644 (void) arg_ptr;
645
646 if (options.full_days == false)
647 {
648 options.cur_day_start += DAYSECS;
649 local = localtime (&options.cur_day_start);
650 options.cur_day_start -= (local
651 ? (local->tm_sec + local->tm_min * 60
652 + local->tm_hour * 3600)
653 : options.cur_day_start % DAYSECS);
654 options.full_days = true;
655 }
656 return true;
657}
658
659static boolean
660parse_delete (const struct parser_table* entry, char *argv[], int *arg_ptr)
661{
662 struct predicate *our_pred;
663 (void) argv;
664 (void) arg_ptr;
665
666 our_pred = insert_primary (entry);
667 our_pred->side_effects = our_pred->no_default_print = true;
668 /* -delete implies -depth */
669 options.do_dir_first = false;
670 our_pred->est_success_rate = 1.0f;
671 return true;
672}
673
674static boolean
675parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr)
676{
677 (void) entry;
678 (void) argv;
679 (void) arg_ptr;
680
681 options.do_dir_first = false;
682 return parse_noop(entry, argv, arg_ptr);
683}
684
685static boolean
686parse_d (const struct parser_table* entry, char **argv, int *arg_ptr)
687{
688 (void) argv;
689 (void) arg_ptr;
690
691 if (options.warnings)
692 {
693 error (0, 0,
694 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
695 }
696 return parse_depth(entry, argv, arg_ptr);
697}
698
699static boolean
700parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr)
701{
702 struct predicate *our_pred;
703 (void) argv;
704 (void) arg_ptr;
705
706 our_pred = insert_primary (entry);
707 our_pred->est_success_rate = 0.01f; /* assume 1% of files are empty. */
708 return true;
709}
710
711static boolean
712parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr)
713{
714 return insert_exec_ok ("-exec", entry, argv, arg_ptr);
715}
716
717static boolean
718parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr)
719{
720 return insert_exec_ok ("-execdir", entry, argv, arg_ptr);
721}
722
723static boolean
724parse_false (const struct parser_table* entry, char **argv, int *arg_ptr)
725{
726 struct predicate *our_pred;
727
728 (void) argv;
729 (void) arg_ptr;
730
731 our_pred = insert_primary (entry);
732 our_pred->need_stat = our_pred->need_type = false;
733 our_pred->side_effects = our_pred->no_default_print = false;
734 our_pred->est_success_rate = 0.0f;
735 return true;
736}
737
738static boolean
739parse_fls (const struct parser_table* entry, char **argv, int *arg_ptr)
740{
741 struct predicate *our_pred;
742
743 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
744 return false;
745 our_pred = insert_primary (entry);
746 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
747 our_pred->side_effects = our_pred->no_default_print = true;
748 our_pred->est_success_rate = 1.0f;
749 (*arg_ptr)++;
750 return true;
751}
752
753static boolean
754parse_fprintf (const struct parser_table* entry, char **argv, int *arg_ptr)
755{
756 FILE *fp;
757
758 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
759 return false;
760 if (argv[*arg_ptr + 1] == NULL)
761 {
762 /* Ensure we get "missing arg" message, not "invalid arg". */
763 (*arg_ptr)++;
764 return false;
765 }
766 fp = open_output_file (argv[*arg_ptr]);
767 (*arg_ptr)++;
768 return insert_fprintf (fp, entry, pred_fprintf, argv, arg_ptr);
769}
770
771static boolean
772parse_follow (const struct parser_table* entry, char **argv, int *arg_ptr)
773{
774 (void) entry;
775 (void) argv;
776 (void) arg_ptr;
777
778 set_follow_state(SYMLINK_ALWAYS_DEREF);
779 return parse_noop(entry, argv, arg_ptr);
780}
781
782static boolean
783parse_fprint (const struct parser_table* entry, char **argv, int *arg_ptr)
784{
785 struct predicate *our_pred;
786
787 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
788 return false;
789 our_pred = insert_primary (entry);
790 our_pred->args.printf_vec.segment = NULL;
791 our_pred->args.printf_vec.stream = open_output_file (argv[*arg_ptr]);
792 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(our_pred->args.printf_vec.stream);
793 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
794 our_pred->side_effects = our_pred->no_default_print = true;
795 our_pred->need_stat = our_pred->need_type = false;
796 our_pred->est_success_rate = 1.0f;
797 (*arg_ptr)++;
798 return true;
799}
800
801static boolean
802parse_fprint0 (const struct parser_table* entry, char **argv, int *arg_ptr)
803{
804 struct predicate *our_pred;
805
806 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
807 return false;
808 our_pred = insert_primary (entry);
809 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
810 our_pred->side_effects = our_pred->no_default_print = true;
811 our_pred->need_stat = our_pred->need_type = false;
812 our_pred->est_success_rate = 1.0f;
813 (*arg_ptr)++;
814 return true;
815}
816
817static float estimate_fstype_success_rate(const char *fsname)
818{
819 struct stat dir_stat;
820 const char *dir = "/";
821 if (0 == stat(dir, &dir_stat))
822 {
823 const char *fstype = filesystem_type(&dir_stat, dir);
824 /* Assume most files are on the same filesystem type as the root fs. */
825 if (0 == strcmp(fsname, fstype))
826 return 0.7f;
827 else
828 return 0.3f;
829 }
830 return 1.0f;
831}
832
833
834static boolean
835parse_fstype (const struct parser_table* entry, char **argv, int *arg_ptr)
836{
837 struct predicate *our_pred;
838
839 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
840 return false;
841 our_pred = insert_primary (entry);
842 our_pred->args.str = argv[*arg_ptr];
843
844 /* This is an expensive operation, so although there are
845 * circumstances where it is selective, we ignore this fact because
846 * we probably don't want to promote this test to the front anyway.
847 */
848 our_pred->est_success_rate = estimate_fstype_success_rate(argv[*arg_ptr]);
849 (*arg_ptr)++;
850 return true;
851}
852
853static boolean
854parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr)
855{
856 struct predicate *p = insert_num (argv, arg_ptr, entry);
857 p->est_success_rate = (p->args.info.l_val < 100) ? 0.99 : 0.2;
858 return p;
859}
860
861static boolean
862parse_group (const struct parser_table* entry, char **argv, int *arg_ptr)
863{
864 struct group *cur_gr;
865 struct predicate *our_pred;
866 gid_t gid;
867 int gid_len;
868
869 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
870 return false;
871 cur_gr = getgrnam (argv[*arg_ptr]);
872 endgrent ();
873 if (cur_gr != NULL)
874 gid = cur_gr->gr_gid;
875 else
876 {
877 gid_len = strspn (argv[*arg_ptr], "0123456789");
878 if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0'))
879 return false;
880 gid = atoi (argv[*arg_ptr]);
881 }
882 our_pred = insert_primary (entry);
883 our_pred->args.gid = gid;
884 our_pred->est_success_rate = (our_pred->args.info.l_val < 100) ? 0.99 : 0.2;
885 (*arg_ptr)++;
886 return true;
887}
888
889static boolean
890parse_help (const struct parser_table* entry, char **argv, int *arg_ptr)
891{
892 (void) entry;
893 (void) argv;
894 (void) arg_ptr;
895
896 usage(stdout, 0, NULL);
897 puts (_("\n\
898default path is the current directory; default expression is -print\n\
899expression may consist of: operators, options, tests, and actions:\n"));
900 puts (_("\
901operators (decreasing precedence; -and is implicit where no others are given):\n\
902 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
903 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
904 puts (_("\
905positional options (always true): -daystart -follow -regextype\n\n\
906normal options (always true, specified before other expressions):\n\
907 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
908 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
909 puts (_("\
910tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
911 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
912 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
913 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
914 puts (_("\
915 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
916 -readable -writable -executable\n\
917 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
918 -used N -user NAME -xtype [bcdpfls]\n"));
919 puts (_("\
920actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
921 -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
922 -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
923 -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
924"));
925 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
926page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
927email to <bug-findutils@gnu.org>."));
928 exit (0);
929}
930
931static float estimate_pattern_match_rate(const char *pattern, int is_regex)
932{
933 if (strpbrk(pattern, "*?[") || (is_regex && strpbrk(pattern, ".")))
934 {
935 /* A wildcard; assume the pattern matches most files. */
936 return 0.8f;
937 }
938 else
939 {
940 return 0.1f;
941 }
942}
943
944static boolean
945parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr)
946{
947 struct predicate *our_pred;
948
949 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
950 return false;
951 our_pred = insert_primary (entry);
952 our_pred->args.str = argv[*arg_ptr];
953 /* Use the generic glob pattern estimator to figure out how many
954 * links will match, but bear in mind that most files won't be links.
955 */
956 our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(our_pred->args.str, 0);
957 (*arg_ptr)++;
958 return true;
959}
960
961
962/* sanity check the fnmatch() function to make sure
963 * it really is the GNU version.
964 */
965static boolean
966fnmatch_sanitycheck(void)
967{
968 /* fprintf(stderr, "Performing find sanity check..."); */
969 if (0 != fnmatch("foo", "foo", 0)
970 || 0 == fnmatch("Foo", "foo", 0)
971 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
972 {
973 error (1, 0, _("sanity check of the fnmatch() library function failed."));
974 /* fprintf(stderr, "FAILED\n"); */
975 return false;
976 }
977
978 /* fprintf(stderr, "OK\n"); */
979 return true;
980}
981
982
983static boolean
984check_name_arg(const char *pred, const char *arg)
985{
986 if (strchr(arg, '/'))
987 {
988 error(0, 0,_("warning: Unix filenames usually don't contain slashes (though pathnames do). That means that '%s %s' will probably evaluate to false all the time on this system. You might find the '-wholename' test more useful, or perhaps '-samefile'. Alternatively, if you are using GNU grep, you could use 'find ... -print0 | grep -FzZ %s'."),
989 pred, arg, arg);
990 }
991 return true; /* allow it anyway */
992}
993
994
995
996static boolean
997parse_iname (const struct parser_table* entry, char **argv, int *arg_ptr)
998{
999 struct predicate *our_pred;
1000
1001 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1002 return false;
1003 if (!check_name_arg("-iname", argv[*arg_ptr]))
1004 return false;
1005
1006 fnmatch_sanitycheck();
1007
1008 our_pred = insert_primary (entry);
1009 our_pred->need_stat = our_pred->need_type = false;
1010 our_pred->args.str = argv[*arg_ptr];
1011 our_pred->est_success_rate = estimate_pattern_match_rate(our_pred->args.str, 0);
1012 (*arg_ptr)++;
1013 return true;
1014}
1015
1016static boolean
1017parse_inum (const struct parser_table* entry, char **argv, int *arg_ptr)
1018{
1019 struct predicate *p = insert_num (argv, arg_ptr, entry);
1020 /* inode number is exact match only, so very low proportions of files match */
1021 p->est_success_rate = 1e-6;
1022 return p;
1023}
1024
1025/* -ipath is deprecated (at RMS's request) in favour of
1026 * -iwholename. See the node "GNU Manuals" in standards.texi
1027 * for the rationale for this (basically, GNU prefers the use
1028 * of the phrase "file name" to "path name"
1029 */
1030static boolean
1031parse_ipath (const struct parser_table* entry, char **argv, int *arg_ptr)
1032{
1033 error (0, 0,
1034 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
1035
1036 return parse_iwholename(entry, argv, arg_ptr);
1037}
1038
1039static boolean
1040parse_iwholename (const struct parser_table* entry, char **argv, int *arg_ptr)
1041{
1042 struct predicate *our_pred;
1043
1044 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1045 return false;
1046
1047 fnmatch_sanitycheck();
1048
1049 our_pred = insert_primary_withpred (entry, pred_ipath);
1050 our_pred->need_stat = our_pred->need_type = false;
1051 our_pred->args.str = argv[*arg_ptr];
1052 our_pred->est_success_rate = estimate_pattern_match_rate(our_pred->args.str, 0);
1053 (*arg_ptr)++;
1054 return true;
1055}
1056
1057static boolean
1058parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr)
1059{
1060 return insert_regex (argv, arg_ptr, entry, RE_ICASE|options.regex_options);
1061}
1062
1063static boolean
1064parse_links (const struct parser_table* entry, char **argv, int *arg_ptr)
1065{
1066 struct predicate *p = insert_num (argv, arg_ptr, entry);
1067 if (p->args.info.l_val == 1)
1068 p->est_success_rate = 0.99;
1069 else if (p->args.info.l_val == 2)
1070 p->est_success_rate = 0.01;
1071 else
1072 p->est_success_rate = 1e-3;
1073 return p;
1074}
1075
1076static boolean
1077parse_lname (const struct parser_table* entry, char **argv, int *arg_ptr)
1078{
1079 struct predicate *our_pred;
1080
1081 (void) argv;
1082 (void) arg_ptr;
1083
1084 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1085 return false;
1086
1087 fnmatch_sanitycheck();
1088
1089 our_pred = insert_primary (entry);
1090 our_pred->args.str = argv[*arg_ptr];
1091 our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(our_pred->args.str, 0);
1092 (*arg_ptr)++;
1093 return true;
1094}
1095
1096static boolean
1097parse_ls (const struct parser_table* entry, char **argv, int *arg_ptr)
1098{
1099 struct predicate *our_pred;
1100
1101 (void) &argv;
1102 (void) &arg_ptr;
1103
1104 our_pred = insert_primary (entry);
1105 our_pred->side_effects = our_pred->no_default_print = true;
1106 return true;
1107}
1108
1109static boolean
1110parse_maxdepth (const struct parser_table* entry, char **argv, int *arg_ptr)
1111{
1112 int depth_len;
1113 (void) entry;
1114
1115 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1116 return false;
1117 depth_len = strspn (argv[*arg_ptr], "0123456789");
1118 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
1119 return false;
1120 options.maxdepth = atoi (argv[*arg_ptr]);
1121 if (options.maxdepth < 0)
1122 return false;
1123 (*arg_ptr)++;
1124 return parse_noop(entry, argv, arg_ptr);
1125}
1126
1127static boolean
1128parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr)
1129{
1130 int depth_len;
1131 (void) entry;
1132
1133 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1134 return false;
1135 depth_len = strspn (argv[*arg_ptr], "0123456789");
1136 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
1137 return false;
1138 options.mindepth = atoi (argv[*arg_ptr]);
1139 if (options.mindepth < 0)
1140 return false;
1141 (*arg_ptr)++;
1142 return parse_noop(entry, argv, arg_ptr);
1143}
1144
1145static boolean
1146parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr)
1147{
1148 struct predicate *our_pred;
1149 uintmax_t num;
1150 enum comparison_type c_type;
1151 time_t t;
1152
1153 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1154 return false;
1155 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
1156 return false;
1157 t = options.cur_day_start + DAYSECS - num * 60;
1158 our_pred = insert_primary (entry);
1159 our_pred->args.info.kind = c_type;
1160 our_pred->args.info.negative = t < 0;
1161 our_pred->args.info.l_val = t;
1162 our_pred->est_success_rate = estimate_file_age_success_rate(num);
1163 (*arg_ptr)++;
1164 return true;
1165}
1166
1167static boolean
1168parse_name (const struct parser_table* entry, char **argv, int *arg_ptr)
1169{
1170 struct predicate *our_pred;
1171
1172 (void) argv;
1173 (void) arg_ptr;
1174
1175 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1176 return false;
1177 if (!check_name_arg("-name", argv[*arg_ptr]))
1178 return false;
1179 fnmatch_sanitycheck();
1180
1181 our_pred = insert_primary (entry);
1182 our_pred->need_stat = our_pred->need_type = false;
1183 our_pred->args.str = argv[*arg_ptr];
1184 our_pred->est_success_rate = estimate_pattern_match_rate(our_pred->args.str, 0);
1185 (*arg_ptr)++;
1186 return true;
1187}
1188
1189static boolean
1190parse_negate (const struct parser_table* entry, char **argv, int *arg_ptr)
1191{
1192 struct predicate *our_pred;
1193
1194 (void) &argv;
1195 (void) &arg_ptr;
1196
1197 our_pred = get_new_pred_chk_op (entry);
1198 our_pred->pred_func = pred_negate;
1199 our_pred->p_type = UNI_OP;
1200 our_pred->p_prec = NEGATE_PREC;
1201 our_pred->need_stat = our_pred->need_type = false;
1202 return true;
1203}
1204
1205static boolean
1206parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr)
1207{
1208 struct predicate *our_pred;
1209 struct stat stat_newer;
1210
1211 (void) argv;
1212 (void) arg_ptr;
1213
1214 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1215 return false;
1216 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
1217 error (1, errno, "%s", argv[*arg_ptr]);
1218 our_pred = insert_primary (entry);
1219 our_pred->args.time = stat_newer.st_mtime;
1220 our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
1221 (*arg_ptr)++;
1222 return true;
1223}
1224
1225static boolean
1226parse_noleaf (const struct parser_table* entry, char **argv, int *arg_ptr)
1227{
1228 (void) &argv;
1229 (void) &arg_ptr;
1230 (void) entry;
1231
1232 options.no_leaf_check = true;
1233 return parse_noop(entry, argv, arg_ptr);
1234}
1235
1236#ifdef CACHE_IDS
1237/* Arbitrary amount by which to increase size
1238 of `uid_unused' and `gid_unused'. */
1239#define ALLOC_STEP 2048
1240
1241/* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1242char *uid_unused = NULL;
1243
1244/* Number of elements in `uid_unused'. */
1245unsigned uid_allocated;
1246
1247/* Similar for GIDs and group entries. */
1248char *gid_unused = NULL;
1249unsigned gid_allocated;
1250#endif
1251
1252static boolean
1253parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr)
1254{
1255 struct predicate *our_pred;
1256
1257 (void) &argv;
1258 (void) &arg_ptr;
1259
1260 our_pred = insert_primary (entry);
1261 our_pred->est_success_rate = 1e-4;
1262#ifdef CACHE_IDS
1263 if (gid_unused == NULL)
1264 {
1265 struct group *gr;
1266
1267 gid_allocated = ALLOC_STEP;
1268 gid_unused = xmalloc (gid_allocated);
1269 memset (gid_unused, 1, gid_allocated);
1270 setgrent ();
1271 while ((gr = getgrent ()) != NULL)
1272 {
1273 if ((unsigned) gr->gr_gid >= gid_allocated)
1274 {
1275 unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
1276 gid_unused = xrealloc (gid_unused, new_allocated);
1277 memset (gid_unused + gid_allocated, 1,
1278 new_allocated - gid_allocated);
1279 gid_allocated = new_allocated;
1280 }
1281 gid_unused[(unsigned) gr->gr_gid] = 0;
1282 }
1283 endgrent ();
1284 }
1285#endif
1286 return true;
1287}
1288
1289static boolean
1290parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr)
1291{
1292 struct predicate *our_pred;
1293 (void) argv;
1294 (void) arg_ptr;
1295
1296
1297 our_pred = insert_primary (entry);
1298 our_pred->est_success_rate = 1e-3;
1299#ifdef CACHE_IDS
1300 if (uid_unused == NULL)
1301 {
1302 struct passwd *pw;
1303
1304 uid_allocated = ALLOC_STEP;
1305 uid_unused = xmalloc (uid_allocated);
1306 memset (uid_unused, 1, uid_allocated);
1307 setpwent ();
1308 while ((pw = getpwent ()) != NULL)
1309 {
1310 if ((unsigned) pw->pw_uid >= uid_allocated)
1311 {
1312 unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
1313 uid_unused = xrealloc (uid_unused, new_allocated);
1314 memset (uid_unused + uid_allocated, 1,
1315 new_allocated - uid_allocated);
1316 uid_allocated = new_allocated;
1317 }
1318 uid_unused[(unsigned) pw->pw_uid] = 0;
1319 }
1320 endpwent ();
1321 }
1322#endif
1323 return true;
1324}
1325
1326static boolean
1327parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr)
1328{
1329 (void) argv;
1330 (void) arg_ptr;
1331 (void) entry;
1332
1333 options.warnings = false;
1334 return parse_noop(entry, argv, arg_ptr);
1335}
1336
1337static boolean
1338parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr)
1339{
1340 return insert_exec_ok ("-ok", entry, argv, arg_ptr);
1341}
1342
1343static boolean
1344parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr)
1345{
1346 return insert_exec_ok ("-okdir", entry, argv, arg_ptr);
1347}
1348
1349boolean
1350parse_open (const struct parser_table* entry, char **argv, int *arg_ptr)
1351{
1352 struct predicate *our_pred;
1353
1354 (void) argv;
1355 (void) arg_ptr;
1356
1357 our_pred = get_new_pred_chk_op (entry);
1358 our_pred->pred_func = pred_open;
1359 our_pred->p_type = OPEN_PAREN;
1360 our_pred->p_prec = NO_PREC;
1361 our_pred->need_stat = our_pred->need_type = false;
1362 return true;
1363}
1364
1365static boolean
1366parse_or (const struct parser_table* entry, char **argv, int *arg_ptr)
1367{
1368 struct predicate *our_pred;
1369
1370 (void) argv;
1371 (void) arg_ptr;
1372
1373 our_pred = get_new_pred (entry);
1374 our_pred->pred_func = pred_or;
1375 our_pred->p_type = BI_OP;
1376 our_pred->p_prec = OR_PREC;
1377 our_pred->need_stat = our_pred->need_type = false;
1378 return true;
1379}
1380
1381/* -path is deprecated (at RMS's request) in favour of
1382 * -iwholename. See the node "GNU Manuals" in standards.texi
1383 * for the rationale for this (basically, GNU prefers the use
1384 * of the phrase "file name" to "path name".
1385 *
1386 * We do not issue a warning that this usage is deprecated
1387 * since HPUX find supports this predicate also.
1388 */
1389static boolean
1390parse_path (const struct parser_table* entry, char **argv, int *arg_ptr)
1391{
1392 return parse_wholename(entry, argv, arg_ptr);
1393}
1394
1395static boolean
1396parse_wholename (const struct parser_table* entry, char **argv, int *arg_ptr)
1397{
1398 struct predicate *our_pred;
1399
1400 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1401 return false;
1402 our_pred = insert_primary_withpred (entry, pred_path);
1403 our_pred->need_stat = our_pred->need_type = false;
1404 our_pred->args.str = argv[*arg_ptr];
1405 our_pred->est_success_rate = estimate_pattern_match_rate(our_pred->args.str, 0);
1406 (*arg_ptr)++;
1407 return true;
1408}
1409
1410static boolean
1411parse_perm (const struct parser_table* entry, char **argv, int *arg_ptr)
1412{
1413 mode_t perm_val[2];
1414 float rate;
1415 int mode_start = 0;
1416 boolean havekind = false;
1417 enum permissions_type kind = PERM_EXACT;
1418 struct mode_change *change = NULL;
1419 struct predicate *our_pred;
1420
1421 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1422 return false;
1423
1424 switch (argv[*arg_ptr][0])
1425 {
1426 case '-':
1427 mode_start = 1;
1428 kind = PERM_AT_LEAST;
1429 havekind = true;
1430 rate = 0.2;
1431 break;
1432
1433 case '+':
1434 change = mode_compile (argv[*arg_ptr]);
1435 if (NULL == change)
1436 {
1437 /* Most likely the caller is an old script that is still
1438 * using the obsolete GNU syntax '-perm +MODE'. This old
1439 * syntax was withdrawn in favor of '-perm /MODE' because
1440 * it is incompatible with POSIX in some cases, but we
1441 * still support uses of it that are not incompatible with
1442 * POSIX.
1443 */
1444 mode_start = 1;
1445 kind = PERM_ANY;
1446 rate = 0.3;
1447 }
1448 else
1449 {
1450 /* This is a POSIX-compatible usage */
1451 mode_start = 0;
1452 kind = PERM_EXACT;
1453 rate = 0.1;
1454 }
1455 havekind = true;
1456 break;
1457
1458 case '/': /* GNU extension */
1459 mode_start = 1;
1460 kind = PERM_ANY;
1461 havekind = true;
1462 rate = 0.3;
1463 break;
1464
1465 default:
1466 /* For example, '-perm 0644', which is valid and matches
1467 * only files whose mode is exactly 0644.
1468 *
1469 * We do nothing here, because mode_start and kind are already
1470 * correctly set.
1471 */
1472 rate = 0.01;
1473 break;
1474 }
1475
1476 if (NULL == change)
1477 {
1478 change = mode_compile (argv[*arg_ptr] + mode_start);
1479 if (NULL == change)
1480 error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
1481 }
1482 perm_val[0] = mode_adjust (0, false, 0, change, NULL);
1483 perm_val[1] = mode_adjust (0, true, 0, change, NULL);
1484 free (change);
1485
1486 our_pred = insert_primary (entry);
1487 our_pred->est_success_rate = rate;
1488 if (havekind)
1489 {
1490 our_pred->args.perm.kind = kind;
1491 }
1492 else
1493 {
1494
1495 switch (argv[*arg_ptr][0])
1496 {
1497 case '-':
1498 our_pred->args.perm.kind = PERM_AT_LEAST;
1499 break;
1500 case '+':
1501 our_pred->args.perm.kind = PERM_ANY;
1502 break;
1503 default:
1504 our_pred->args.perm.kind = PERM_EXACT;
1505 break;
1506 }
1507 }
1508 if (('/' == argv[*arg_ptr][0]) && (0 == perm_val[0]) && (0 == perm_val[1]))
1509 {
1510 /* The meaning of -perm /000 will change in the future.
1511 * It currently matches no files, but like -perm -000 it
1512 * should match all files.
1513 */
1514 error (0, 0,
1515 _("warning: you have specified a mode pattern %s which is "
1516 "equivalent to 000. The meaning of -perm /000 will soon be "
1517 "changed to be consistent with -perm -000; that is, at the "
1518 "moment it matches no files but it will soon be changed to "
1519 "match all files."),
1520 argv[*arg_ptr]);
1521 }
1522
1523 memcpy (our_pred->args.perm.val, perm_val, sizeof perm_val);
1524 (*arg_ptr)++;
1525 return true;
1526}
1527
1528boolean
1529parse_print (const struct parser_table* entry, char **argv, int *arg_ptr)
1530{
1531 struct predicate *our_pred;
1532
1533 (void) argv;
1534 (void) arg_ptr;
1535
1536 our_pred = insert_primary (entry);
1537 /* -print has the side effect of printing. This prevents us
1538 from doing undesired multiple printing when the user has
1539 already specified -print. */
1540 our_pred->side_effects = our_pred->no_default_print = true;
1541 our_pred->need_stat = our_pred->need_type = false;
1542 our_pred->args.printf_vec.segment = NULL;
1543 our_pred->args.printf_vec.stream = stdout;
1544 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(stdout);
1545 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
1546
1547 return true;
1548}
1549
1550static boolean
1551parse_print0 (const struct parser_table* entry, char **argv, int *arg_ptr)
1552{
1553 struct predicate *our_pred;
1554
1555 (void) argv;
1556 (void) arg_ptr;
1557
1558 our_pred = insert_primary (entry);
1559 /* -print0 has the side effect of printing. This prevents us
1560 from doing undesired multiple printing when the user has
1561 already specified -print0. */
1562 our_pred->side_effects = our_pred->no_default_print = true;
1563 our_pred->need_stat = our_pred->need_type = false;
1564 return true;
1565}
1566
1567static boolean
1568parse_printf (const struct parser_table* entry, char **argv, int *arg_ptr)
1569{
1570 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1571 return false;
1572 return insert_fprintf (stdout, entry, pred_fprintf, argv, arg_ptr);
1573}
1574
1575static boolean
1576parse_prune (const struct parser_table* entry, char **argv, int *arg_ptr)
1577{
1578 struct predicate *our_pred;
1579
1580 (void) argv;
1581 (void) arg_ptr;
1582
1583 our_pred = insert_primary (entry);
1584 our_pred->need_stat = our_pred->need_type = false;
1585 /* -prune has a side effect that it does not descend into
1586 the current directory. */
1587 our_pred->side_effects = true;
1588 our_pred->no_default_print = false;
1589 return true;
1590}
1591
1592static boolean
1593parse_quit (const struct parser_table* entry, char **argv, int *arg_ptr)
1594{
1595 struct predicate *our_pred = insert_primary (entry);
1596 (void) argv;
1597 (void) arg_ptr;
1598 our_pred->need_stat = our_pred->need_type = false;
1599 our_pred->side_effects = true; /* Exiting is a side effect... */
1600 our_pred->no_default_print = false; /* Don't inhibit the default print, though. */
1601 our_pred->est_success_rate = 1e-6;
1602 return true;
1603}
1604
1605
1606static boolean
1607parse_regextype (const struct parser_table* entry, char **argv, int *arg_ptr)
1608{
1609 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1610 return false;
1611
1612 /* collect the regex type name */
1613 options.regex_options = get_regex_type(argv[*arg_ptr]);
1614 (*arg_ptr)++;
1615
1616 return parse_noop(entry, argv, arg_ptr);
1617}
1618
1619
1620static boolean
1621parse_regex (const struct parser_table* entry, char **argv, int *arg_ptr)
1622{
1623 return insert_regex (argv, arg_ptr, entry, options.regex_options);
1624}
1625
1626static boolean
1627insert_regex (char **argv, int *arg_ptr, const struct parser_table *entry, int regex_options)
1628{
1629 struct predicate *our_pred;
1630 struct re_pattern_buffer *re;
1631 const char *error_message;
1632
1633 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1634 return false;
1635 our_pred = insert_primary_withpred (entry, pred_regex);
1636 our_pred->need_stat = our_pred->need_type = false;
1637 re = (struct re_pattern_buffer *)
1638 xmalloc (sizeof (struct re_pattern_buffer));
1639 our_pred->args.regex = re;
1640 re->allocated = 100;
1641 re->buffer = (unsigned char *) xmalloc (re->allocated);
1642 re->fastmap = NULL;
1643
1644 re_set_syntax(regex_options);
1645 re->syntax = regex_options;
1646 re->translate = NULL;
1647
1648 error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
1649 re);
1650 if (error_message)
1651 error (1, 0, "%s", error_message);
1652 our_pred->est_success_rate = estimate_pattern_match_rate(argv[*arg_ptr], 1);
1653 (*arg_ptr)++;
1654 return true;
1655}
1656
1657static boolean
1658parse_size (const struct parser_table* entry, char **argv, int *arg_ptr)
1659{
1660 struct predicate *our_pred;
1661 uintmax_t num;
1662 enum comparison_type c_type;
1663 int blksize = 512;
1664 int len;
1665 float rate = 1.0;
1666
1667 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1668 return false;
1669 len = strlen (argv[*arg_ptr]);
1670 if (len == 0)
1671 error (1, 0, _("invalid null argument to -size"));
1672 switch (argv[*arg_ptr][len - 1])
1673 {
1674 case 'b':
1675 blksize = 512;
1676 argv[*arg_ptr][len - 1] = '\0';
1677 break;
1678
1679 case 'c':
1680 blksize = 1;
1681 argv[*arg_ptr][len - 1] = '\0';
1682 break;
1683
1684 case 'k':
1685 blksize = 1024;
1686 argv[*arg_ptr][len - 1] = '\0';
1687 break;
1688
1689 case 'M': /* Megabytes */
1690 blksize = 1024*1024;
1691 argv[*arg_ptr][len - 1] = '\0';
1692 break;
1693
1694 case 'G': /* Gigabytes */
1695 blksize = 1024*1024*1024;
1696 argv[*arg_ptr][len - 1] = '\0';
1697 break;
1698
1699 case 'w':
1700 blksize = 2;
1701 argv[*arg_ptr][len - 1] = '\0';
1702 break;
1703
1704 case '0':
1705 case '1':
1706 case '2':
1707 case '3':
1708 case '4':
1709 case '5':
1710 case '6':
1711 case '7':
1712 case '8':
1713 case '9':
1714 break;
1715
1716 default:
1717 error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
1718 }
1719 if (!get_num (argv[*arg_ptr], &num, &c_type))
1720 return false;
1721 our_pred = insert_primary (entry);
1722 our_pred->args.size.kind = c_type;
1723 our_pred->args.size.blocksize = blksize;
1724 our_pred->args.size.size = num;
1725 our_pred->need_stat = true;
1726 our_pred->need_type = false;
1727
1728 if (COMP_GT == c_type)
1729 our_pred->est_success_rate = (num*blksize > 20480) ? 0.1 : 0.9;
1730 else if (COMP_LT == c_type)
1731 our_pred->est_success_rate = (num*blksize > 20480) ? 0.9 : 0.1;
1732 else
1733 our_pred->est_success_rate = 0.01;
1734
1735 (*arg_ptr)++;
1736 return true;
1737}
1738
1739
1740static boolean
1741parse_samefile (const struct parser_table* entry, char **argv, int *arg_ptr)
1742{
1743 struct predicate *our_pred;
1744 struct stat st;
1745
1746 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1747 return false;
1748 if ((*options.xstat) (argv[*arg_ptr], &st))
1749 error (1, errno, "%s", argv[*arg_ptr]);
1750
1751 our_pred = insert_primary (entry);
1752 our_pred->args.fileid.ino = st.st_ino;
1753 our_pred->args.fileid.dev = st.st_dev;
1754 our_pred->need_type = false;
1755 our_pred->need_stat = true;
1756 our_pred->est_success_rate = 0.01f;
1757 (*arg_ptr)++;
1758 return true;
1759}
1760
1761#if 0
1762static boolean
1763parse_show_control_chars (const struct parser_table* entry, char **argv, int *arg_ptr)
1764{
1765 const char *arg;
1766 const char *errmsg = _("The -show-control-chars option takes a single argument which "
1767 "must be 'literal' or 'safe'");
1768
1769 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1770 {
1771 error (1, errno, "%s", errmsg);
1772 return false;
1773 }
1774 else
1775 {
1776 arg = argv[*arg_ptr];
1777
1778 if (0 == strcmp("literal", arg))
1779 {
1780 options.literal_control_chars = true;
1781 }
1782 else if (0 == strcmp("safe", arg))
1783 {
1784 options.literal_control_chars = false;
1785 }
1786 else
1787 {
1788 error (1, errno, "%s", errmsg);
1789 return false;
1790 }
1791 (*arg_ptr)++; /* consume the argument. */
1792 return true;
1793 }
1794}
1795#endif
1796
1797
1798static boolean
1799parse_true (const struct parser_table* entry, char **argv, int *arg_ptr)
1800{
1801 struct predicate *our_pred;
1802
1803 (void) argv;
1804 (void) arg_ptr;
1805
1806 our_pred = insert_primary (entry);
1807 our_pred->need_stat = our_pred->need_type = false;
1808 our_pred->est_success_rate = 1.0f;
1809 return true;
1810}
1811
1812static boolean
1813parse_noop (const struct parser_table* entry, char **argv, int *arg_ptr)
1814{
1815 (void) entry;
1816 return parse_true(get_noop(), argv, arg_ptr);
1817}
1818
1819static boolean
1820parse_accesscheck (const struct parser_table* entry, char **argv, int *arg_ptr)
1821{
1822 struct predicate *our_pred;
1823 (void) argv;
1824 (void) arg_ptr;
1825 our_pred = insert_primary (entry);
1826 our_pred->need_stat = our_pred->need_type = false;
1827 our_pred->side_effects = our_pred->no_default_print = false;
1828 if (our_pred->pred_func == pred_executable)
1829 our_pred->est_success_rate = 0.2;
1830 else
1831 our_pred->est_success_rate = 0.9;
1832 return true;
1833}
1834
1835static boolean
1836parse_type (const struct parser_table* entry, char **argv, int *arg_ptr)
1837{
1838 return insert_type (argv, arg_ptr, entry, pred_type);
1839}
1840
1841static boolean
1842parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr)
1843{
1844 struct predicate *p = insert_num (argv, arg_ptr, entry);
1845 p->est_success_rate = (p->args.info.l_val < 100) ? 0.99 : 0.2;
1846 return p;
1847}
1848
1849static boolean
1850parse_used (const struct parser_table* entry, char **argv, int *arg_ptr)
1851{
1852 struct predicate *our_pred;
1853 uintmax_t num_days;
1854 enum comparison_type c_type;
1855 time_t t;
1856
1857 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1858 return false;
1859 if (!get_num (argv[*arg_ptr], &num_days, &c_type))
1860 return false;
1861 t = num_days * DAYSECS;
1862 our_pred = insert_primary (entry);
1863 our_pred->args.info.kind = c_type;
1864 our_pred->args.info.negative = t < 0;
1865 our_pred->args.info.l_val = t;
1866 our_pred->est_success_rate = estimate_file_age_success_rate(num_days);
1867 (*arg_ptr)++;
1868 return true;
1869}
1870
1871static boolean
1872parse_user (const struct parser_table* entry, char **argv, int *arg_ptr)
1873{
1874 struct passwd *cur_pwd;
1875 struct predicate *our_pred;
1876 uid_t uid;
1877 int uid_len;
1878
1879 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1880 return false;
1881 cur_pwd = getpwnam (argv[*arg_ptr]);
1882 endpwent ();
1883 if (cur_pwd != NULL)
1884 uid = cur_pwd->pw_uid;
1885 else
1886 {
1887 uid_len = strspn (argv[*arg_ptr], "0123456789");
1888 if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
1889 return false;
1890 uid = atoi (argv[*arg_ptr]);
1891 }
1892 our_pred = insert_primary (entry);
1893 our_pred->args.uid = uid;
1894 our_pred->est_success_rate = (our_pred->args.uid < 100) ? 0.99 : 0.2;
1895 (*arg_ptr)++;
1896 return true;
1897}
1898
1899static boolean
1900parse_version (const struct parser_table* entry, char **argv, int *arg_ptr)
1901{
1902 extern char *version_string;
1903 int features = 0;
1904
1905 (void) argv;
1906 (void) arg_ptr;
1907 (void) entry;
1908
1909 fflush (stderr);
1910 printf (_("GNU find version %s\n"), version_string);
1911 printf (_("Features enabled: "));
1912
1913#if CACHE_IDS
1914 printf("CACHE_IDS ");
1915 ++features;
1916#endif
1917#if DEBUG
1918 printf("DEBUG ");
1919 ++features;
1920#endif
1921#if DEBUG_STAT
1922 printf("DEBUG_STAT ");
1923 ++features;
1924#endif
1925#if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1926 printf("D_TYPE ");
1927 ++features;
1928#endif
1929#if defined(O_NOFOLLOW)
1930 printf("O_NOFOLLOW(%s) ",
1931 (options.open_nofollow_available ? "enabled" : "disabled"));
1932 ++features;
1933#endif
1934#if defined(LEAF_OPTIMISATION)
1935 printf("LEAF_OPTIMISATION ");
1936 ++features;
1937#endif
1938
1939 if (is_fts_enabled())
1940 {
1941 printf("FTS ");
1942 ++features;
1943 }
1944
1945 printf("CBO(level=%d) ", (int)(options.optimisation_level));
1946 ++features;
1947
1948 if (0 == features)
1949 {
1950 /* For the moment, leave this as English in case someone wants
1951 to parse these strings. */
1952 printf("none");
1953 }
1954 printf("\n");
1955
1956 exit (0);
1957}
1958
1959static boolean
1960parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr)
1961{
1962 (void) argv;
1963 (void) arg_ptr;
1964 (void) entry;
1965 options.stay_on_filesystem = true;
1966 return parse_noop(entry, argv, arg_ptr);
1967}
1968
1969static boolean
1970parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
1971{
1972 (void) argv;
1973 (void) arg_ptr;
1974 (void) entry;
1975 options.ignore_readdir_race = true;
1976 return parse_noop(entry, argv, arg_ptr);
1977}
1978
1979static boolean
1980parse_noignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
1981{
1982 (void) argv;
1983 (void) arg_ptr;
1984 (void) entry;
1985 options.ignore_readdir_race = false;
1986 return parse_noop(entry, argv, arg_ptr);
1987}
1988
1989static boolean
1990parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr)
1991{
1992 (void) argv;
1993 (void) arg_ptr;
1994 (void) entry;
1995 options.warnings = true;
1996 return parse_noop(entry, argv, arg_ptr);
1997}
1998
1999static boolean
2000parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr)
2001{
2002 (void) argv;
2003 (void) arg_ptr;
2004 return insert_type (argv, arg_ptr, entry, pred_xtype);
2005}
2006
2007
2008static boolean
2009insert_type (char **argv, int *arg_ptr, const struct parser_table *entry, PRED_FUNC which_pred)
2010{
2011 mode_t type_cell;
2012 struct predicate *our_pred;
2013 float rate = 0.5;
2014
2015 if ((argv == NULL) || (argv[*arg_ptr] == NULL)
2016 || (strlen (argv[*arg_ptr]) != 1))
2017 return false;
2018 switch (argv[*arg_ptr][0])
2019 {
2020 case 'b': /* block special */
2021 type_cell = S_IFBLK;
2022 rate = 0.01f;
2023 break;
2024 case 'c': /* character special */
2025 type_cell = S_IFCHR;
2026 rate = 0.01f;
2027 break;
2028 case 'd': /* directory */
2029 type_cell = S_IFDIR;
2030 rate = 0.4f;
2031 break;
2032 case 'f': /* regular file */
2033 type_cell = S_IFREG;
2034 rate = 0.95f;
2035 break;
2036#ifdef S_IFLNK
2037 case 'l': /* symbolic link */
2038 type_cell = S_IFLNK;
2039 rate = 0.1f;
2040 break;
2041#endif
2042#ifdef S_IFIFO
2043 case 'p': /* pipe */
2044 type_cell = S_IFIFO;
2045 rate = 0.01f;
2046 break;
2047#endif
2048#ifdef S_IFSOCK
2049 case 's': /* socket */
2050 type_cell = S_IFSOCK;
2051 rate = 0.01f;
2052 break;
2053#endif
2054#ifdef S_IFDOOR
2055 case 'D': /* Solaris door */
2056 type_cell = S_IFDOOR;
2057 rate = 0.01f;
2058 break;
2059#endif
2060 default: /* None of the above ... nuke 'em. */
2061 return false;
2062 }
2063 our_pred = insert_primary_withpred (entry, which_pred);
2064 our_pred->est_success_rate = rate;
2065
2066 /* Figure out if we will need to stat the file, because if we don't
2067 * need to follow symlinks, we can avoid a stat call by using
2068 * struct dirent.d_type.
2069 */
2070 if (which_pred == pred_xtype)
2071 {
2072 our_pred->need_stat = true;
2073 our_pred->need_type = false;
2074 }
2075 else
2076 {
2077 our_pred->need_stat = false; /* struct dirent is enough */
2078 our_pred->need_type = true;
2079 }
2080 our_pred->args.type = type_cell;
2081 (*arg_ptr)++; /* Move on to next argument. */
2082 return true;
2083}
2084
2085
2086/* Return true if the file accessed via FP is a terminal.
2087 */
2088static boolean
2089stream_is_tty(FILE *fp)
2090{
2091 int fd = fileno(fp);
2092 if (-1 == fd)
2093 {
2094 return false; /* not a valid stream */
2095 }
2096 else
2097 {
2098 return isatty(fd) ? true : false;
2099 }
2100
2101}
2102
2103
2104
2105
2106/* XXX: do we need to pass FUNC to this function? */
2107static boolean
2108insert_fprintf (FILE *fp, const struct parser_table *entry, PRED_FUNC func, char **argv, int *arg_ptr)
2109{
2110 char *format; /* Beginning of unprocessed format string. */
2111 register char *scan; /* Current address in scanning `format'. */
2112 register char *scan2; /* Address inside of element being scanned. */
2113 struct segment **segmentp; /* Address of current segment. */
2114 struct predicate *our_pred;
2115
2116 format = argv[(*arg_ptr)++];
2117
2118 our_pred = insert_primary_withpred (entry, func);
2119 our_pred->side_effects = our_pred->no_default_print = true;
2120 our_pred->args.printf_vec.stream = fp;
2121 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(fp);
2122 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
2123 our_pred->need_type = false;
2124 our_pred->need_stat = false;
2125
2126 segmentp = &our_pred->args.printf_vec.segment;
2127 *segmentp = NULL;
2128
2129 for (scan = format; *scan; scan++)
2130 {
2131 if (*scan == '\\')
2132 {
2133 scan2 = scan + 1;
2134 if (*scan2 >= '0' && *scan2 <= '7')
2135 {
2136 register int n, i;
2137
2138 for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
2139 i++, scan2++)
2140 n = 8 * n + *scan2 - '0';
2141 scan2--;
2142 *scan = n;
2143 }
2144 else
2145 {
2146 switch (*scan2)
2147 {
2148 case 'a':
2149 *scan = 7;
2150 break;
2151 case 'b':
2152 *scan = '\b';
2153 break;
2154 case 'c':
2155 make_segment (segmentp, format, scan - format, KIND_STOP,
2156 our_pred);
2157 if (our_pred->need_stat && (our_pred->p_cost < NeedsStatInfo))
2158 our_pred->p_cost = NeedsStatInfo;
2159 return true;
2160 case 'f':
2161 *scan = '\f';
2162 break;
2163 case 'n':
2164 *scan = '\n';
2165 break;
2166 case 'r':
2167 *scan = '\r';
2168 break;
2169 case 't':
2170 *scan = '\t';
2171 break;
2172 case 'v':
2173 *scan = '\v';
2174 break;
2175 case '\\':
2176 /* *scan = '\\'; * it already is */
2177 break;
2178 default:
2179 error (0, 0,
2180 _("warning: unrecognized escape `\\%c'"), *scan2);
2181 scan++;
2182 continue;
2183 }
2184 }
2185 segmentp = make_segment (segmentp, format, scan - format + 1,
2186 KIND_PLAIN, our_pred);
2187 format = scan2 + 1; /* Move past the escape. */
2188 scan = scan2; /* Incremented immediately by `for'. */
2189 }
2190 else if (*scan == '%')
2191 {
2192 if (scan[1] == 0)
2193 {
2194 /* Trailing %. We don't like those. */
2195 error (1, 0, _("error: %s at end of format string"), scan);
2196 }
2197 else if (scan[1] == '%')
2198 {
2199 segmentp = make_segment (segmentp, format, scan - format + 1,
2200 KIND_PLAIN, our_pred);
2201 scan++;
2202 format = scan + 1;
2203 continue;
2204 }
2205 /* Scan past flags, width and precision, to verify kind. */
2206 for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
2207 /* Do nothing. */ ;
2208 while (ISDIGIT (*scan2))
2209 scan2++;
2210 if (*scan2 == '.')
2211 for (scan2++; ISDIGIT (*scan2); scan2++)
2212 /* Do nothing. */ ;
2213 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2))
2214 {
2215 segmentp = make_segment (segmentp, format, scan2 - format,
2216 (int) *scan2, our_pred);
2217 scan = scan2;
2218 format = scan + 1;
2219 }
2220 else if (strchr ("ACT", *scan2) && scan2[1])
2221 {
2222 segmentp = make_segment (segmentp, format, scan2 - format,
2223 *scan2 | (scan2[1] << 8),
2224 our_pred);
2225 scan = scan2 + 1;
2226 format = scan + 1;
2227 continue;
2228 }
2229 else
2230 {
2231 /* An unrecognized % escape. Print the char after the %. */
2232 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
2233 *scan2);
2234 segmentp = make_segment (segmentp, format, scan - format,
2235 KIND_PLAIN, our_pred);
2236 format = scan + 1;
2237 continue;
2238 }
2239 }
2240 }
2241
2242 if (scan > format)
2243 make_segment (segmentp, format, scan - format, KIND_PLAIN,
2244 our_pred);
2245 return true;
2246}
2247
2248/* Create a new fprintf segment in *SEGMENT, with type KIND,
2249 from the text in FORMAT, which has length LEN.
2250 Return the address of the `next' pointer of the new segment. */
2251
2252static struct segment **
2253make_segment (struct segment **segment, char *format, int len, int kind,
2254 struct predicate *pred)
2255{
2256 enum EvaluationCost mycost = NeedsNothing;
2257 char *fmt;
2258
2259 *segment = (struct segment *) xmalloc (sizeof (struct segment));
2260
2261 (*segment)->kind = kind;
2262 (*segment)->next = NULL;
2263 (*segment)->text_len = len;
2264
2265 fmt = (*segment)->text = xmalloc (len + sizeof "d");
2266 strncpy (fmt, format, len);
2267 fmt += len;
2268
2269 switch (kind & 0xff)
2270 {
2271 case KIND_PLAIN: /* Plain text string, no % conversion. */
2272 case KIND_STOP: /* Terminate argument, no newline. */
2273 break;
2274
2275 case 'l': /* object of symlink */
2276 pred->need_stat = true;
2277 mycost = NeedsLinkName;
2278 *fmt++ = 's';
2279 break;
2280
2281 case 'y': /* file type */
2282 pred->need_type = true;
2283 mycost = NeedsType;
2284 *fmt++ = 's';
2285 break;
2286
2287 case 'a': /* atime in `ctime' format */
2288 case 'A': /* atime in user-specified strftime format */
2289 case 'c': /* ctime in `ctime' format */
2290 case 'C': /* ctime in user-specified strftime format */
2291 case 'F': /* filesystem type */
2292 case 'g': /* group name */
2293 case 'i': /* inode number */
2294 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
2295 case 's': /* size in bytes */
2296 case 't': /* mtime in `ctime' format */
2297 case 'T': /* mtime in user-specified strftime format */
2298 case 'u': /* user name */
2299 pred->need_stat = true;
2300 mycost = NeedsStatInfo;
2301 *fmt++ = 's';
2302 break;
2303
2304 case 'Y': /* symlink pointed file type */
2305 pred->need_stat = true;
2306 mycost = NeedsType; /* true for amortised effect */
2307 *fmt++ = 's';
2308 break;
2309
2310 case 'f': /* basename of path */
2311 case 'h': /* leading directories part of path */
2312 case 'H': /* ARGV element file was found under */
2313 case 'p': /* pathname */
2314 case 'P': /* pathname with ARGV element stripped */
2315 *fmt++ = 's';
2316 break;
2317
2318 /* Numeric items that one might expect to honour
2319 * #, 0, + flags but which do not.
2320 */
2321 case 'G': /* GID number */
2322 case 'U': /* UID number */
2323 case 'b': /* size in 512-byte blocks */
2324 case 'D': /* Filesystem device on which the file exits */
2325 case 'k': /* size in 1K blocks */
2326 case 'n': /* number of links */
2327 pred->need_stat = true;
2328 mycost = NeedsStatInfo;
2329 *fmt++ = 's';
2330 break;
2331
2332 /* Numeric items that DO honour #, 0, + flags.
2333 */
2334 case 'd': /* depth in search tree (0 = ARGV element) */
2335 *fmt++ = 'd';
2336 break;
2337
2338 case 'm': /* mode as octal number (perms only) */
2339 *fmt++ = 'o';
2340 pred->need_stat = true;
2341 mycost = NeedsStatInfo;
2342 break;
2343 }
2344 *fmt = '\0';
2345
2346 if (mycost > pred->p_cost)
2347 pred->p_cost = mycost;
2348 return &(*segment)->next;
2349}
2350
2351
2352static void
2353check_path_safety(const char *action)
2354{
2355 const char *path = getenv("PATH");
2356 char *s;
2357 s = next_element(path, 1);
2358 while ((s = next_element ((char *) NULL, 1)) != NULL)
2359 {
2360 if (0 == strcmp(s, "."))
2361 {
2362 error(1, 0, _("The current directory is included in the PATH environment variable, which is insecure in combination with the %s action of find. Please remove the current directory from your $PATH (that is, remove \".\" or leading or trailing colons)"),
2363 action);
2364 }
2365 }
2366}
2367
2368
2369
2370/* handles both exec and ok predicate */
2371#if defined(NEW_EXEC)
2372/* handles both exec and ok predicate */
2373static boolean
2374new_insert_exec_ok (const char *action,
2375 const struct parser_table *entry,
2376 char **argv,
2377 int *arg_ptr)
2378{
2379 int start, end; /* Indexes in ARGV of start & end of cmd. */
2380 int i; /* Index into cmd args */
2381 int saw_braces; /* True if previous arg was '{}'. */
2382 boolean allow_plus; /* True if + is a valid terminator */
2383 int brace_count; /* Number of instances of {}. */
2384 PRED_FUNC func = entry->pred_func;
2385 enum BC_INIT_STATUS bcstatus;
2386
2387 struct predicate *our_pred;
2388 struct exec_val *execp; /* Pointer for efficiency. */
2389
2390 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2391 return false;
2392
2393 our_pred = insert_primary_withpred (entry, func);
2394 our_pred->side_effects = our_pred->no_default_print = true;
2395 our_pred->need_type = our_pred->need_stat = false;
2396
2397 execp = &our_pred->args.exec_vec;
2398
2399 if ((func != pred_okdir) && (func != pred_ok))
2400 {
2401 allow_plus = true;
2402 execp->close_stdin = false;
2403 }
2404 else
2405 {
2406 allow_plus = false;
2407 /* If find reads stdin (i.e. for -ok and similar), close stdin
2408 * in the child to prevent some script from consiming the output
2409 * intended for find.
2410 */
2411 execp->close_stdin = true;
2412 }
2413
2414
2415 if ((func == pred_execdir) || (func == pred_okdir))
2416 {
2417 options.ignore_readdir_race = false;
2418 check_path_safety(action);
2419 execp->use_current_dir = true;
2420 }
2421 else
2422 {
2423 execp->use_current_dir = false;
2424 }
2425
2426 our_pred->args.exec_vec.multiple = 0;
2427
2428 /* Count the number of args with path replacements, up until the ';'.
2429 * Also figure out if the command is terminated by ";" or by "+".
2430 */
2431 start = *arg_ptr;
2432 for (end = start, saw_braces=0, brace_count=0;
2433 (argv[end] != NULL)
2434 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2435 end++)
2436 {
2437 /* For -exec and -execdir, "{} +" can terminate the command. */
2438 if ( allow_plus
2439 && argv[end][0] == '+' && argv[end][1] == 0
2440 && saw_braces)
2441 {
2442 our_pred->args.exec_vec.multiple = 1;
2443 break;
2444 }
2445
2446 saw_braces = 0;
2447 if (strstr (argv[end], "{}"))
2448 {
2449 saw_braces = 1;
2450 ++brace_count;
2451
2452 if (0 == end && (func == pred_execdir || func == pred_okdir))
2453 {
2454 /* The POSIX standard says that {} replacement should
2455 * occur even in the utility name. This is insecure
2456 * since it means we will be executing a command whose
2457 * name is chosen according to whatever find finds in
2458 * the filesystem. That can be influenced by an
2459 * attacker. Hence for -execdir and -okdir this is not
2460 * allowed. We can specify this as those options are
2461 * not defined by POSIX.
2462 */
2463 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2464 }
2465 }
2466 }
2467
2468 /* Fail if no command given or no semicolon found. */
2469 if ((end == start) || (argv[end] == NULL))
2470 {
2471 *arg_ptr = end;
2472 free(our_pred);
2473 return false;
2474 }
2475
2476 if (our_pred->args.exec_vec.multiple && brace_count > 1)
2477 {
2478
2479 const char *suffix;
2480 if (func == pred_execdir)
2481 suffix = "dir";
2482 else
2483 suffix = "";
2484
2485 error(1, 0,
2486 _("Only one instance of {} is supported with -exec%s ... +"),
2487 suffix);
2488 }
2489
2490 /* We use a switch statement here so that
2491 * the compiler warns us when we forget to handle a
2492 * newly invented enum value.
2493 */
2494 bcstatus = bc_init_controlinfo(&execp->ctl);
2495 switch (bcstatus)
2496 {
2497 case BC_INIT_ENV_TOO_BIG:
2498 error(1, 0,
2499 _("The environment is too large for exec()."));
2500 break;
2501 case BC_INIT_OK:
2502 /* Good news. Carry on. */
2503 break;
2504 }
2505 bc_use_sensible_arg_max(&execp->ctl);
2506
2507
2508 execp->ctl.exec_callback = launch;
2509
2510 if (our_pred->args.exec_vec.multiple)
2511 {
2512 /* "+" terminator, so we can just append our arguments after the
2513 * command and initial arguments.
2514 */
2515 execp->replace_vec = NULL;
2516 execp->ctl.replace_pat = NULL;
2517 execp->ctl.rplen = 0;
2518 execp->ctl.lines_per_exec = 0; /* no limit */
2519 execp->ctl.args_per_exec = 0; /* no limit */
2520
2521 /* remember how many arguments there are */
2522 execp->ctl.initial_argc = (end-start) - 1;
2523
2524 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2525 bc_init_state(&execp->ctl, &execp->state, execp);
2526
2527 /* Gather the initial arguments. Skip the {}. */
2528 for (i=start; i<end-1; ++i)
2529 {
2530 bc_push_arg(&execp->ctl, &execp->state,
2531 argv[i], strlen(argv[i])+1,
2532 NULL, 0,
2533 1);
2534 }
2535 }
2536 else
2537 {
2538 /* Semicolon terminator - more than one {} is supported, so we
2539 * have to do brace-replacement.
2540 */
2541 execp->num_args = end - start;
2542
2543 execp->ctl.replace_pat = "{}";
2544 execp->ctl.rplen = strlen(execp->ctl.replace_pat);
2545 execp->ctl.lines_per_exec = 0; /* no limit */
2546 execp->ctl.args_per_exec = 0; /* no limit */
2547 execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
2548
2549
2550 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2551 bc_init_state(&execp->ctl, &execp->state, execp);
2552
2553 /* Remember the (pre-replacement) arguments for later. */
2554 for (i=0; i<execp->num_args; ++i)
2555 {
2556 execp->replace_vec[i] = argv[i+start];
2557 }
2558 }
2559
2560 if (argv[end] == NULL)
2561 *arg_ptr = end;
2562 else
2563 *arg_ptr = end + 1;
2564
2565 return true;
2566}
2567#else
2568/* handles both exec and ok predicate */
2569static boolean
2570old_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
2571{
2572 int start, end; /* Indexes in ARGV of start & end of cmd. */
2573 int num_paths; /* Number of args with path replacements. */
2574 int path_pos; /* Index in array of path replacements. */
2575 int vec_pos; /* Index in array of args. */
2576 struct predicate *our_pred;
2577 struct exec_val *execp; /* Pointer for efficiency. */
2578
2579 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2580 return false;
2581
2582 /* Count the number of args with path replacements, up until the ';'. */
2583 start = *arg_ptr;
2584 for (end = start, num_paths = 0;
2585 (argv[end] != NULL)
2586 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2587 end++)
2588 if (strstr (argv[end], "{}"))
2589 num_paths++;
2590 /* Fail if no command given or no semicolon found. */
2591 if ((end == start) || (argv[end] == NULL))
2592 {
2593 *arg_ptr = end;
2594 return false;
2595 }
2596
2597 our_pred = insert_primary (func);
2598 our_pred->side_effects = our_pred->no_default_print = true;
2599 execp = &our_pred->args.exec_vec;
2600 execp->usercontext = our_pred;
2601 execp->use_current_dir = false;
2602 execp->paths =
2603 (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1));
2604 execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1));
2605 /* Record the positions of all args, and the args with path replacements. */
2606 for (end = start, path_pos = vec_pos = 0;
2607 (argv[end] != NULL)
2608 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2609 end++)
2610 {
2611 register char *p;
2612
2613 execp->paths[path_pos].count = 0;
2614 for (p = argv[end]; *p; ++p)
2615 if (p[0] == '{' && p[1] == '}')
2616 {
2617 execp->paths[path_pos].count++;
2618 ++p;
2619 }
2620 if (execp->paths[path_pos].count)
2621 {
2622 execp->paths[path_pos].offset = vec_pos;
2623 execp->paths[path_pos].origarg = argv[end];
2624 path_pos++;
2625 }
2626 execp->vec[vec_pos++] = argv[end];
2627 }
2628 execp->paths[path_pos].offset = -1;
2629 execp->vec[vec_pos] = NULL;
2630
2631 if (argv[end] == NULL)
2632 *arg_ptr = end;
2633 else
2634 *arg_ptr = end + 1;
2635 return true;
2636}
2637#endif
2638
2639
2640
2641static boolean
2642insert_exec_ok (const char *action, const struct parser_table *entry, char **argv, int *arg_ptr)
2643{
2644#if defined(NEW_EXEC)
2645 return new_insert_exec_ok(action, entry, argv, arg_ptr);
2646#else
2647 return old_insert_exec_ok(func, argv, arg_ptr);
2648#endif
2649}
2650
2651
2652
2653
2654/* Get a number of days and comparison type.
2655 STR is the ASCII representation.
2656 Set *NUM_DAYS to the number of days, taken as being from
2657 the current moment (or possibly midnight). Thus the sense of the
2658 comparison type appears to be reversed.
2659 Set *COMP_TYPE to the kind of comparison that is requested.
2660
2661 Return true if all okay, false if input error.
2662
2663 Used by -atime, -ctime and -mtime (parsers) to
2664 get the appropriate information for a time predicate processor. */
2665
2666static boolean
2667get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
2668{
2669 boolean r = get_num (str, num_days, comp_type);
2670 if (r)
2671 switch (*comp_type)
2672 {
2673 case COMP_LT: *comp_type = COMP_GT; break;
2674 case COMP_GT: *comp_type = COMP_LT; break;
2675 default: break;
2676 }
2677 return r;
2678}
2679
2680
2681/* Insert a time predicate PRED.
2682 ARGV is a pointer to the argument array.
2683 ARG_PTR is a pointer to an index into the array, incremented if
2684 all went well.
2685
2686 Return true if input is valid, false if not.
2687
2688 A new predicate node is assigned, along with an argument node
2689 obtained with malloc.
2690
2691 Used by -atime, -ctime, and -mtime parsers. */
2692
2693static boolean
2694parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr)
2695{
2696 struct predicate *our_pred;
2697 uintmax_t num_days;
2698 enum comparison_type c_type;
2699 time_t t;
2700
2701 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2702 return false;
2703 if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
2704 return false;
2705
2706 /* Figure out the timestamp value we are looking for. */
2707 t = ( options.cur_day_start - num_days * DAYSECS
2708 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2709
2710 if (1)
2711 {
2712 /* We introduce a scope in which 'val' can be declared, for the
2713 * benefit of compilers that are really C89 compilers
2714 * which support intmax_t because config.h #defines it
2715 */
2716 intmax_t val = ( (intmax_t)options.cur_day_start - num_days * DAYSECS
2717 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2718 t = val;
2719
2720 /* Check for possibility of an overflow */
2721 if ( (intmax_t)t != val )
2722 {
2723 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv[*arg_ptr]);
2724 }
2725 }
2726
2727 our_pred = insert_primary (entry);
2728 our_pred->args.info.kind = c_type;
2729 our_pred->args.info.negative = t < 0;
2730 our_pred->args.info.l_val = t;
2731 our_pred->est_success_rate = estimate_file_age_success_rate(num_days);
2732 (*arg_ptr)++;
2733
2734 if (options.debug_options & DebugExpressionTree)
2735 {
2736 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2737 fprintf (stderr, " type: %s %s ",
2738 (c_type == COMP_GT) ? "gt" :
2739 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2740 (c_type == COMP_GT) ? " >" :
2741 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
2742 t = our_pred->args.info.l_val;
2743 fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2744 if (c_type == COMP_EQ)
2745 {
2746 t = our_pred->args.info.l_val += DAYSECS;
2747 fprintf (stderr, " < %ju %s",
2748 (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2749 our_pred->args.info.l_val -= DAYSECS;
2750 }
2751 }
2752
2753 return true;
2754}
2755
2756
2757/* Get a number with comparison information.
2758 The sense of the comparison information is 'normal'; that is,
2759 '+' looks for a count > than the number and '-' less than.
2760
2761 STR is the ASCII representation of the number.
2762 Set *NUM to the number.
2763 Set *COMP_TYPE to the kind of comparison that is requested.
2764
2765 Return true if all okay, false if input error. */
2766
2767static boolean
2768get_num (char *str, uintmax_t *num, enum comparison_type *comp_type)
2769{
2770 if (str == NULL)
2771 return false;
2772 switch (str[0])
2773 {
2774 case '+':
2775 *comp_type = COMP_GT;
2776 str++;
2777 break;
2778 case '-':
2779 *comp_type = COMP_LT;
2780 str++;
2781 break;
2782 default:
2783 *comp_type = COMP_EQ;
2784 break;
2785 }
2786
2787 return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK;
2788}
2789
2790
2791/* Insert a number predicate.
2792 ARGV is a pointer to the argument array.
2793 *ARG_PTR is an index into ARGV, incremented if all went well.
2794 *PRED is the predicate processor to insert.
2795
2796 Return true if input is valid, false if error.
2797
2798 A new predicate node is assigned, along with an argument node
2799 obtained with malloc.
2800
2801 Used by -inum and -links parsers. */
2802
2803static struct predicate *
2804insert_num (char **argv, int *arg_ptr, const struct parser_table *entry)
2805{
2806 struct predicate *our_pred;
2807 uintmax_t num;
2808 enum comparison_type c_type;
2809
2810 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2811 return NULL;
2812 if (!get_num (argv[*arg_ptr], &num, &c_type))
2813 return NULL;
2814 our_pred = insert_primary (entry);
2815 our_pred->args.info.kind = c_type;
2816 our_pred->args.info.l_val = num;
2817 (*arg_ptr)++;
2818
2819 if (options.debug_options & DebugExpressionTree)
2820 {
2821 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2822 fprintf (stderr, " type: %s %s ",
2823 (c_type == COMP_GT) ? "gt" :
2824 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2825 (c_type == COMP_GT) ? " >" :
2826 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
2827 fprintf (stderr, "%ju\n", our_pred->args.info.l_val);
2828 }
2829 return our_pred;
2830}
2831
2832static FILE *
2833open_output_file (char *path)
2834{
2835 FILE *f;
2836
2837 if (!strcmp (path, "/dev/stderr"))
2838 return stderr;
2839 else if (!strcmp (path, "/dev/stdout"))
2840 return stdout;
2841 f = fopen_safer (path, "w");
2842 if (f == NULL)
2843 error (1, errno, "%s", path);
2844 return f;
2845}
Note: See TracBrowser for help on using the repository browser.