Support fts() in FTS_CWDFD mode. Also work around Savannah bug #18466 (with a perfor...
[findutils.git] / find / parser.c
blob19b95ed1100bc62ef1d8688157e7bd06056f36b6
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.
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.
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.
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.
22 #include "defs.h"
23 #include <ctype.h>
24 #include <math.h>
25 #include <assert.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <fnmatch.h>
29 #include "modechange.h"
30 #include "modetype.h"
31 #include "xstrtol.h"
32 #include "xalloc.h"
33 #include "quote.h"
34 #include "quotearg.h"
35 #include "buildcmd.h"
36 #include "nextelem.h"
37 #include "stdio-safer.h"
38 #include "regextype.h"
39 #include "stat-time.h"
40 #include "xstrtod.h"
41 #include "fts_.h"
42 #include "gnulib-version.h"
44 #ifdef HAVE_FCNTL_H
45 #include <fcntl.h>
46 #else
47 #include <sys/file.h>
48 #endif
50 /* The presence of unistd.h is assumed by gnulib these days, so we
51 * might as well assume it too.
53 /* We need <unistd.h> for isatty(). */
54 #include <unistd.h>
56 #if ENABLE_NLS
57 # include <libintl.h>
58 # define _(Text) gettext (Text)
59 #else
60 # define _(Text) Text
61 #endif
62 #ifdef gettext_noop
63 # define N_(String) gettext_noop (String)
64 #else
65 /* See locate.c for explanation as to why not use (String) */
66 # define N_(String) String
67 #endif
69 #if !defined (isascii) || defined (STDC_HEADERS)
70 #ifdef isascii
71 #undef isascii
72 #endif
73 #define isascii(c) 1
74 #endif
76 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
77 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
79 #ifndef HAVE_ENDGRENT
80 #define endgrent()
81 #endif
82 #ifndef HAVE_ENDPWENT
83 #define endpwent()
84 #endif
86 static boolean parse_accesscheck PARAMS((const struct parser_table* entry, char **argv, int *arg_ptr));
87 static boolean parse_amin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
88 static boolean parse_and PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
89 static boolean parse_anewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
90 static boolean parse_cmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
91 static boolean parse_cnewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
92 static boolean parse_comma PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
93 static boolean parse_daystart PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
94 static boolean parse_delete PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
95 static boolean parse_d PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
96 static boolean parse_depth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
97 static boolean parse_empty PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
98 static boolean parse_exec PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
99 static boolean parse_execdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
100 static boolean parse_false PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
101 static boolean parse_fls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
102 static boolean parse_fprintf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
103 static boolean parse_follow PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
104 static boolean parse_fprint PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
105 static boolean parse_fprint0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
106 static boolean parse_fstype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
107 static boolean parse_gid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
108 static boolean parse_group PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
109 static boolean parse_help PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
110 static boolean parse_ilname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
111 static boolean parse_iname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
112 static boolean parse_inum PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
113 static boolean parse_ipath PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
114 static boolean parse_iregex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
115 static boolean parse_iwholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
116 static boolean parse_links PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
117 static boolean parse_lname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
118 static boolean parse_ls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
119 static boolean parse_maxdepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
120 static boolean parse_mindepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
121 static boolean parse_mmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
122 static boolean parse_name PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
123 static boolean parse_negate PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
124 static boolean parse_newer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
125 static boolean parse_newerXY PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
126 static boolean parse_noleaf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
127 static boolean parse_nogroup PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
128 static boolean parse_nouser PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
129 static boolean parse_nowarn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
130 static boolean parse_ok PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
131 static boolean parse_okdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
132 static boolean parse_or PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
133 static boolean parse_path PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
134 static boolean parse_perm PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
135 static boolean parse_print0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
136 static boolean parse_printf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
137 static boolean parse_prune PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
138 static boolean parse_regex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
139 static boolean parse_regextype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
140 static boolean parse_samefile PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
141 #if 0
142 static boolean parse_show_control_chars PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
143 #endif
144 static boolean parse_size PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
145 static boolean parse_time PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
146 static boolean parse_true PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
147 static boolean parse_type PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
148 static boolean parse_uid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
149 static boolean parse_used PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
150 static boolean parse_user PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
151 static boolean parse_version PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
152 static boolean parse_wholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
153 static boolean parse_xdev PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
154 static boolean parse_ignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
155 static boolean parse_noignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
156 static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
157 static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
158 static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
160 boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
163 static boolean insert_type PARAMS((char **argv, int *arg_ptr, const struct parser_table *entry, PRED_FUNC which_pred));
164 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, const struct parser_table *entry, int regex_options));
165 static boolean insert_fprintf PARAMS((FILE *fp, const struct parser_table *entry, PRED_FUNC func, char *argv[], int *arg_ptr));
167 static struct segment **make_segment PARAMS((struct segment **segment, char *format, int len,
168 int kind, char format_char, char aux_format_char,
169 struct predicate *pred));
170 static boolean insert_exec_ok PARAMS((const char *action, const struct parser_table *entry, int dirfd, char *argv[], int *arg_ptr));
171 static boolean get_comp_type PARAMS((char **str, enum comparison_type *comp_type));
172 static boolean get_relative_timestamp PARAMS((char *str, struct time_val *tval, time_t origin, double sec_per_unit, const char *overflowmessage));
173 static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type));
174 static struct predicate* insert_num PARAMS((char *argv[], int *arg_ptr, const struct parser_table *entry));
175 static FILE *open_output_file PARAMS((char *path));
176 static boolean stream_is_tty(FILE *fp);
177 static boolean parse_noop PARAMS((const struct parser_table* entry, char **argv, int *arg_ptr));
179 #define PASTE(x,y) x##y
180 #define STRINGIFY(s) #s
182 #define PARSE_OPTION(what,suffix) \
183 { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL }
185 #define PARSE_POSOPT(what,suffix) \
186 { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL }
188 #define PARSE_TEST(what,suffix) \
189 { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
191 #define PARSE_TEST_NP(what,suffix) \
192 { (ARG_TEST), (what), PASTE(parse_,suffix), NULL }
194 #define PARSE_ACTION(what,suffix) \
195 { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
197 #define PARSE_ACTION_NP(what,suffix) \
198 { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL }
200 #define PARSE_PUNCTUATION(what,suffix) \
201 { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
204 /* Predicates we cannot handle in the usual way */
205 static struct parser_table const parse_entry_newerXY =
207 ARG_SPECIAL_PARSE, "newerXY", parse_newerXY, pred_newerXY /* BSD */
210 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
211 If they are in some Unix versions of find, they are marked `Unix'. */
213 static struct parser_table const parse_table[] =
215 PARSE_PUNCTUATION("!", negate),
216 PARSE_PUNCTUATION("not", negate), /* GNU */
217 PARSE_PUNCTUATION("(", open),
218 PARSE_PUNCTUATION(")", close),
219 PARSE_PUNCTUATION(",", comma), /* GNU */
220 PARSE_PUNCTUATION("a", and),
221 PARSE_TEST ("amin", amin), /* GNU */
222 PARSE_PUNCTUATION("and", and), /* GNU */
223 PARSE_TEST ("anewer", anewer), /* GNU */
224 {ARG_TEST, "atime", parse_time, pred_atime},
225 PARSE_TEST ("cmin", cmin), /* GNU */
226 PARSE_TEST ("cnewer", cnewer), /* GNU */
227 {ARG_TEST, "ctime", parse_time, pred_ctime},
228 PARSE_POSOPT ("daystart", daystart), /* GNU */
229 PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
230 PARSE_OPTION ("d", d), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
231 PARSE_OPTION ("depth", depth),
232 PARSE_TEST ("empty", empty), /* GNU */
233 {ARG_ACTION, "exec", parse_exec, pred_exec}, /* POSIX */
234 {ARG_TEST, "executable", parse_accesscheck, pred_executable}, /* GNU, 4.3.0+ */
235 PARSE_ACTION ("execdir", execdir), /* *BSD, GNU */
236 PARSE_ACTION ("fls", fls), /* GNU */
237 PARSE_POSOPT ("follow", follow), /* GNU, Unix */
238 PARSE_ACTION ("fprint", fprint), /* GNU */
239 PARSE_ACTION ("fprint0", fprint0), /* GNU */
240 {ARG_ACTION, "fprintf", parse_fprintf, pred_fprintf}, /* GNU */
241 PARSE_TEST ("fstype", fstype), /* GNU, Unix */
242 PARSE_TEST ("gid", gid), /* GNU */
243 PARSE_TEST ("group", group),
244 PARSE_OPTION ("ignore_readdir_race", ignore_race), /* GNU */
245 PARSE_TEST ("ilname", ilname), /* GNU */
246 PARSE_TEST ("iname", iname), /* GNU */
247 PARSE_TEST ("inum", inum), /* GNU, Unix */
248 PARSE_TEST ("ipath", ipath), /* GNU, deprecated in favour of iwholename */
249 PARSE_TEST_NP ("iregex", iregex), /* GNU */
250 PARSE_TEST_NP ("iwholename", iwholename), /* GNU */
251 PARSE_TEST ("links", links),
252 PARSE_TEST ("lname", lname), /* GNU */
253 PARSE_ACTION ("ls", ls), /* GNU, Unix */
254 PARSE_OPTION ("maxdepth", maxdepth), /* GNU */
255 PARSE_OPTION ("mindepth", mindepth), /* GNU */
256 PARSE_TEST ("mmin", mmin), /* GNU */
257 PARSE_OPTION ("mount", xdev), /* Unix */
258 {ARG_TEST, "mtime", parse_time, pred_mtime},
259 PARSE_TEST ("name", name),
260 #ifdef UNIMPLEMENTED_UNIX
261 PARSE(ARG_UNIMPLEMENTED, "ncpio", ncpio), /* Unix */
262 #endif
263 PARSE_TEST ("newer", newer),
264 {ARG_TEST, "atime", parse_time, pred_atime},
265 PARSE_OPTION ("noleaf", noleaf), /* GNU */
266 PARSE_TEST ("nogroup", nogroup),
267 PARSE_TEST ("nouser", nouser),
268 PARSE_OPTION ("noignore_readdir_race", noignore_race), /* GNU */
269 PARSE_POSOPT ("nowarn", nowarn), /* GNU */
270 PARSE_PUNCTUATION("o", or),
271 PARSE_PUNCTUATION("or", or), /* GNU */
272 PARSE_ACTION ("ok", ok),
273 PARSE_ACTION ("okdir", okdir), /* GNU (-execdir is BSD) */
274 PARSE_TEST ("path", path), /* GNU, HP-UX, GNU prefers wholename */
275 PARSE_TEST ("perm", perm),
276 PARSE_ACTION ("print", print),
277 PARSE_ACTION ("print0", print0), /* GNU */
278 {ARG_ACTION, "printf", parse_printf, NULL}, /* GNU */
279 PARSE_ACTION ("prune", prune),
280 PARSE_ACTION ("quit", quit), /* GNU */
281 {ARG_TEST, "readable", parse_accesscheck, pred_readable}, /* GNU, 4.3.0+ */
282 PARSE_TEST ("regex", regex), /* GNU */
283 PARSE_OPTION ("regextype", regextype), /* GNU */
284 PARSE_TEST ("samefile", samefile), /* GNU */
285 #if 0
286 PARSE_OPTION ("show-control-chars", show_control_chars), /* GNU, 4.3.0+ */
287 #endif
288 PARSE_TEST ("size", size),
289 PARSE_TEST ("type", type),
290 PARSE_TEST ("uid", uid), /* GNU */
291 PARSE_TEST ("used", used), /* GNU */
292 PARSE_TEST ("user", user),
293 PARSE_OPTION ("warn", warn), /* GNU */
294 PARSE_TEST_NP ("wholename", wholename), /* GNU, replaces -path */
295 {ARG_TEST, "writable", parse_accesscheck, pred_writable}, /* GNU, 4.3.0+ */
296 PARSE_OPTION ("xdev", xdev),
297 PARSE_TEST ("xtype", xtype), /* GNU */
298 #ifdef UNIMPLEMENTED_UNIX
299 /* It's pretty ugly for find to know about archive formats.
300 Plus what it could do with cpio archives is very limited.
301 Better to leave it out. */
302 PARSE(ARG_UNIMPLEMENTED, "cpio", cpio), /* Unix */
303 #endif
304 /* gnulib's stdbool.h might have made true and false into macros,
305 * so we can't leave named 'true' and 'false' tokens, so we have
306 * to expeant the relevant entries longhand.
308 {ARG_TEST, "false", parse_false, pred_false}, /* GNU */
309 {ARG_TEST, "true", parse_true, pred_true }, /* GNU */
310 {ARG_NOOP, "noop", NULL, pred_true }, /* GNU, internal use only */
312 /* Various other cases that don't fit neatly into our macro scheme. */
313 {ARG_TEST, "help", parse_help, NULL}, /* GNU */
314 {ARG_TEST, "-help", parse_help, NULL}, /* GNU */
315 {ARG_TEST, "version", parse_version, NULL}, /* GNU */
316 {ARG_TEST, "-version", parse_version, NULL}, /* GNU */
317 {0, 0, 0, 0}
321 static const char *first_nonoption_arg = NULL;
322 static const struct parser_table *noop = NULL;
325 static const struct parser_table*
326 get_noop(void)
328 int i;
329 if (NULL == noop)
331 for (i = 0; parse_table[i].parser_name != 0; i++)
333 if (ARG_NOOP ==parse_table[i].type)
335 noop = &(parse_table[i]);
336 break;
340 return noop;
343 static int
344 get_stat_Ytime(const struct stat *p,
345 char what,
346 struct timespec *ret)
348 switch (what)
350 case 'a':
351 *ret = get_stat_atime(p);
352 return 1;
353 case 'B':
354 *ret = get_stat_birthtime(p);
355 return (ret->tv_nsec >= 0);
356 case 'c':
357 *ret = get_stat_ctime(p);
358 return 1;
359 case 'm':
360 *ret = get_stat_mtime(p);
361 return 1;
362 default:
363 assert(0);
364 abort();
365 abort();
369 void
370 set_follow_state(enum SymlinkOption opt)
372 if (options.debug_options & DebugStat)
374 /* For DebugStat, the choice is made at runtime within debug_stat()
375 * by checking the contents of the symlink_handling variable.
377 options.xstat = debug_stat;
379 else
381 switch (opt)
383 case SYMLINK_ALWAYS_DEREF: /* -L */
384 options.xstat = optionl_stat;
385 options.no_leaf_check = true;
386 break;
388 case SYMLINK_NEVER_DEREF: /* -P (default) */
389 options.xstat = optionp_stat;
390 /* Can't turn no_leaf_check off because the user might have specified
391 * -noleaf anyway
393 break;
395 case SYMLINK_DEREF_ARGSONLY: /* -H */
396 options.xstat = optionh_stat;
397 options.no_leaf_check = true;
400 options.symlink_handling = opt;
404 void
405 parse_begin_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates)
407 (void) args;
408 (void) argno;
409 (void) last;
410 (void) predicates;
411 first_nonoption_arg = NULL;
414 void
415 parse_end_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates)
417 /* does nothing */
418 (void) args;
419 (void) argno;
420 (void) last;
421 (void) predicates;
425 /* Check that it is legal to fid the given primary in its
426 * position and return it.
428 const struct parser_table*
429 found_parser(const char *original_arg, const struct parser_table *entry)
431 /* If this is an option, but we have already had a
432 * non-option argument, the user may be under the
433 * impression that the behaviour of the option
434 * argument is conditional on some preceding
435 * tests. This might typically be the case with,
436 * for example, -maxdepth.
438 * The options -daystart and -follow are exempt
439 * from this treatment, since their positioning
440 * in the command line does have an effect on
441 * subsequent tests but not previous ones. That
442 * might be intentional on the part of the user.
444 if (entry->type != ARG_POSITIONAL_OPTION)
446 /* Something other than -follow/-daystart.
447 * If this is an option, check if it followed
448 * a non-option and if so, issue a warning.
450 if (entry->type == ARG_OPTION)
452 if ((first_nonoption_arg != NULL)
453 && options.warnings )
455 /* option which follows a non-option */
456 error (0, 0,
457 _("warning: you have specified the %s "
458 "option after a non-option argument %s, "
459 "but options are not positional (%s affects "
460 "tests specified before it as well as those "
461 "specified after it). Please specify options "
462 "before other arguments.\n"),
463 original_arg,
464 first_nonoption_arg,
465 original_arg);
468 else
470 /* Not an option or a positional option,
471 * so remember we've seen it in order to
472 * use it in a possible future warning message.
474 if (first_nonoption_arg == NULL)
476 first_nonoption_arg = original_arg;
481 return entry;
485 /* Return a pointer to the parser function to invoke for predicate
486 SEARCH_NAME.
487 Return NULL if SEARCH_NAME is not a valid predicate name. */
489 const struct parser_table*
490 find_parser (char *search_name)
492 int i;
493 const struct parser_table *p;
494 const char *original_arg = search_name;
496 /* Ugh. Special case -newerXY. */
497 if (0 == strncmp("-newer", search_name, 6)
498 && (8 == strlen(search_name)))
500 return found_parser(original_arg, &parse_entry_newerXY);
503 if (*search_name == '-')
504 search_name++;
506 for (i = 0; parse_table[i].parser_name != 0; i++)
508 if (strcmp (parse_table[i].parser_name, search_name) == 0)
510 return found_parser(original_arg, &parse_table[i]);
513 return NULL;
516 static float
517 estimate_file_age_success_rate(float num_days)
519 if (num_days < 0.1)
521 /* Assume 1% of files have timestamps in the future */
522 return 0.01f;
524 else if (num_days < 1)
526 /* Assume 30% of files have timestamps today */
527 return 0.3f;
529 else if (num_days > 100)
531 /* Assume 30% of files are very old */
532 return 0.3f;
534 else
536 /* Assume 39% of files are between 1 and 100 days old. */
537 return 0.39f;
541 static float
542 estimate_timestamp_success_rate(time_t when)
544 int num_days = (options.cur_day_start - when) / 86400;
545 return estimate_file_age_success_rate(num_days);
548 /* The parsers are responsible to continue scanning ARGV for
549 their arguments. Each parser knows what is and isn't
550 allowed for itself.
552 ARGV is the argument array.
553 *ARG_PTR is the index to start at in ARGV,
554 updated to point beyond the last element consumed.
556 The predicate structure is updated with the new information. */
559 static boolean
560 parse_and (const struct parser_table* entry, char **argv, int *arg_ptr)
562 struct predicate *our_pred;
564 (void) argv;
565 (void) arg_ptr;
567 our_pred = get_new_pred (entry);
568 our_pred->pred_func = pred_and;
569 our_pred->p_type = BI_OP;
570 our_pred->p_prec = AND_PREC;
571 our_pred->need_stat = our_pred->need_type = false;
572 return true;
575 static boolean
576 parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr)
578 struct predicate *our_pred;
579 struct stat stat_newer;
581 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
582 return false;
583 set_stat_placeholders(&stat_newer);
584 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
585 error (1, errno, "%s", argv[*arg_ptr]);
586 our_pred = insert_primary (entry);
587 our_pred->args.reftime.xval = XVAL_ATIME;
588 our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
589 our_pred->args.reftime.kind = COMP_GT;
590 our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
591 (*arg_ptr)++;
592 return true;
595 boolean
596 parse_close (const struct parser_table* entry, char **argv, int *arg_ptr)
598 struct predicate *our_pred;
600 (void) argv;
601 (void) arg_ptr;
603 our_pred = get_new_pred (entry);
604 our_pred->pred_func = pred_close;
605 our_pred->p_type = CLOSE_PAREN;
606 our_pred->p_prec = NO_PREC;
607 our_pred->need_stat = our_pred->need_type = false;
608 return true;
611 static boolean
612 parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr)
614 struct predicate *our_pred;
615 struct stat stat_newer;
617 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
618 return false;
619 set_stat_placeholders(&stat_newer);
620 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
621 error (1, errno, "%s", argv[*arg_ptr]);
622 our_pred = insert_primary (entry);
623 our_pred->args.reftime.xval = XVAL_CTIME; /* like -newercm */
624 our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
625 our_pred->args.reftime.kind = COMP_GT;
626 our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
627 (*arg_ptr)++;
628 return true;
631 static boolean
632 parse_comma (const struct parser_table* entry, char **argv, int *arg_ptr)
634 struct predicate *our_pred;
636 (void) argv;
637 (void) arg_ptr;
639 our_pred = get_new_pred (entry);
640 our_pred->pred_func = pred_comma;
641 our_pred->p_type = BI_OP;
642 our_pred->p_prec = COMMA_PREC;
643 our_pred->need_stat = our_pred->need_type = false;
644 our_pred->est_success_rate = 1.0f;
645 return true;
648 static boolean
649 parse_daystart (const struct parser_table* entry, char **argv, int *arg_ptr)
651 struct tm *local;
653 (void) entry;
654 (void) argv;
655 (void) arg_ptr;
657 if (options.full_days == false)
659 options.cur_day_start += DAYSECS;
660 local = localtime (&options.cur_day_start);
661 options.cur_day_start -= (local
662 ? (local->tm_sec + local->tm_min * 60
663 + local->tm_hour * 3600)
664 : options.cur_day_start % DAYSECS);
665 options.full_days = true;
667 return true;
670 static boolean
671 parse_delete (const struct parser_table* entry, char *argv[], int *arg_ptr)
673 struct predicate *our_pred;
674 (void) argv;
675 (void) arg_ptr;
677 our_pred = insert_primary (entry);
678 our_pred->side_effects = our_pred->no_default_print = true;
679 /* -delete implies -depth */
680 options.do_dir_first = false;
681 our_pred->est_success_rate = 1.0f;
682 return true;
685 static boolean
686 parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr)
688 (void) entry;
689 (void) argv;
690 (void) arg_ptr;
692 options.do_dir_first = false;
693 return parse_noop(entry, argv, arg_ptr);
696 static boolean
697 parse_d (const struct parser_table* entry, char **argv, int *arg_ptr)
699 (void) argv;
700 (void) arg_ptr;
702 if (options.warnings)
704 error (0, 0,
705 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
707 return parse_depth(entry, argv, arg_ptr);
710 static boolean
711 parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr)
713 struct predicate *our_pred;
714 (void) argv;
715 (void) arg_ptr;
717 our_pred = insert_primary (entry);
718 our_pred->est_success_rate = 0.01f; /* assume 1% of files are empty. */
719 return true;
722 static boolean
723 parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr)
725 return insert_exec_ok ("-exec", entry, get_start_dirfd(), argv, arg_ptr);
728 static boolean
729 parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr)
731 return insert_exec_ok ("-execdir", entry, -1, argv, arg_ptr);
734 static boolean
735 parse_false (const struct parser_table* entry, char **argv, int *arg_ptr)
737 struct predicate *our_pred;
739 (void) argv;
740 (void) arg_ptr;
742 our_pred = insert_primary (entry);
743 our_pred->need_stat = our_pred->need_type = false;
744 our_pred->side_effects = our_pred->no_default_print = false;
745 our_pred->est_success_rate = 0.0f;
746 return true;
749 static boolean
750 parse_fls (const struct parser_table* entry, char **argv, int *arg_ptr)
752 struct predicate *our_pred;
754 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
755 return false;
756 our_pred = insert_primary (entry);
757 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
758 our_pred->side_effects = our_pred->no_default_print = true;
759 our_pred->est_success_rate = 1.0f;
760 (*arg_ptr)++;
761 return true;
764 static boolean
765 parse_fprintf (const struct parser_table* entry, char **argv, int *arg_ptr)
767 FILE *fp;
769 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
770 return false;
771 if (argv[*arg_ptr + 1] == NULL)
773 /* Ensure we get "missing arg" message, not "invalid arg". */
774 (*arg_ptr)++;
775 return false;
777 fp = open_output_file (argv[*arg_ptr]);
778 (*arg_ptr)++;
779 return insert_fprintf (fp, entry, pred_fprintf, argv, arg_ptr);
782 static boolean
783 parse_follow (const struct parser_table* entry, char **argv, int *arg_ptr)
785 (void) entry;
786 (void) argv;
787 (void) arg_ptr;
789 set_follow_state(SYMLINK_ALWAYS_DEREF);
790 return parse_noop(entry, argv, arg_ptr);
793 static boolean
794 parse_fprint (const struct parser_table* entry, char **argv, int *arg_ptr)
796 struct predicate *our_pred;
798 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
799 return false;
800 our_pred = insert_primary (entry);
801 our_pred->args.printf_vec.segment = NULL;
802 our_pred->args.printf_vec.stream = open_output_file (argv[*arg_ptr]);
803 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(our_pred->args.printf_vec.stream);
804 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
805 our_pred->side_effects = our_pred->no_default_print = true;
806 our_pred->need_stat = our_pred->need_type = false;
807 our_pred->est_success_rate = 1.0f;
808 (*arg_ptr)++;
809 return true;
812 static boolean
813 parse_fprint0 (const struct parser_table* entry, char **argv, int *arg_ptr)
815 struct predicate *our_pred;
817 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
818 return false;
819 our_pred = insert_primary (entry);
820 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
821 our_pred->side_effects = our_pred->no_default_print = true;
822 our_pred->need_stat = our_pred->need_type = false;
823 our_pred->est_success_rate = 1.0f;
824 (*arg_ptr)++;
825 return true;
828 static float estimate_fstype_success_rate(const char *fsname)
830 struct stat dir_stat;
831 const char *dir = "/";
832 if (0 == stat(dir, &dir_stat))
834 const char *fstype = filesystem_type(&dir_stat, dir);
835 /* Assume most files are on the same filesystem type as the root fs. */
836 if (0 == strcmp(fsname, fstype))
837 return 0.7f;
838 else
839 return 0.3f;
841 return 1.0f;
845 static boolean
846 parse_fstype (const struct parser_table* entry, char **argv, int *arg_ptr)
848 struct predicate *our_pred;
850 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
851 return false;
852 our_pred = insert_primary (entry);
853 our_pred->args.str = argv[*arg_ptr];
855 /* This is an expensive operation, so although there are
856 * circumstances where it is selective, we ignore this fact because
857 * we probably don't want to promote this test to the front anyway.
859 our_pred->est_success_rate = estimate_fstype_success_rate(argv[*arg_ptr]);
860 (*arg_ptr)++;
861 return true;
864 static boolean
865 parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr)
867 struct predicate *p = insert_num (argv, arg_ptr, entry);
868 p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2;
869 return p;
872 static boolean
873 parse_group (const struct parser_table* entry, char **argv, int *arg_ptr)
875 struct group *cur_gr;
876 struct predicate *our_pred;
877 gid_t gid;
878 int gid_len;
880 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
881 return false;
882 cur_gr = getgrnam (argv[*arg_ptr]);
883 endgrent ();
884 if (cur_gr != NULL)
885 gid = cur_gr->gr_gid;
886 else
888 gid_len = strspn (argv[*arg_ptr], "0123456789");
889 if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0'))
890 return false;
891 gid = atoi (argv[*arg_ptr]);
893 our_pred = insert_primary (entry);
894 our_pred->args.gid = gid;
895 our_pred->est_success_rate = (our_pred->args.numinfo.l_val < 100) ? 0.99 : 0.2;
896 (*arg_ptr)++;
897 return true;
900 static boolean
901 parse_help (const struct parser_table* entry, char **argv, int *arg_ptr)
903 (void) entry;
904 (void) argv;
905 (void) arg_ptr;
907 usage(stdout, 0, NULL);
908 puts (_("\n\
909 default path is the current directory; default expression is -print\n\
910 expression may consist of: operators, options, tests, and actions:\n"));
911 puts (_("\
912 operators (decreasing precedence; -and is implicit where no others are given):\n\
913 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
914 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
915 puts (_("\
916 positional options (always true): -daystart -follow -regextype\n\n\
917 normal options (always true, specified before other expressions):\n\
918 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
919 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
920 puts (_("\
921 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
922 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
923 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
924 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
925 puts (_("\
926 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
927 -readable -writable -executable\n\
928 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
929 -used N -user NAME -xtype [bcdpfls]\n"));
930 puts (_("\
931 actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
932 -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
933 -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
934 -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
935 "));
936 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
937 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
938 email to <bug-findutils@gnu.org>."));
939 exit (0);
942 static float
943 estimate_pattern_match_rate(const char *pattern, int is_regex)
945 if (strpbrk(pattern, "*?[") || (is_regex && strpbrk(pattern, ".")))
947 /* A wildcard; assume the pattern matches most files. */
948 return 0.8f;
950 else
952 return 0.1f;
956 static boolean
957 parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr)
959 struct predicate *our_pred;
961 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
962 return false;
963 our_pred = insert_primary (entry);
964 our_pred->args.str = argv[*arg_ptr];
965 /* Use the generic glob pattern estimator to figure out how many
966 * links will match, but bear in mind that most files won't be links.
968 our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(our_pred->args.str, 0);
969 (*arg_ptr)++;
970 return true;
974 /* sanity check the fnmatch() function to make sure
975 * it really is the GNU version.
977 static boolean
978 fnmatch_sanitycheck(void)
980 /* fprintf(stderr, "Performing find sanity check..."); */
981 if (0 != fnmatch("foo", "foo", 0)
982 || 0 == fnmatch("Foo", "foo", 0)
983 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
985 error (1, 0, _("sanity check of the fnmatch() library function failed."));
986 /* fprintf(stderr, "FAILED\n"); */
987 return false;
990 /* fprintf(stderr, "OK\n"); */
991 return true;
995 static boolean
996 check_name_arg(const char *pred, const char *arg)
998 if (strchr(arg, '/'))
1000 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'."),
1001 pred, arg, arg);
1003 return true; /* allow it anyway */
1008 static boolean
1009 parse_iname (const struct parser_table* entry, char **argv, int *arg_ptr)
1011 struct predicate *our_pred;
1013 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1014 return false;
1015 if (!check_name_arg("-iname", argv[*arg_ptr]))
1016 return false;
1018 fnmatch_sanitycheck();
1020 our_pred = insert_primary (entry);
1021 our_pred->need_stat = our_pred->need_type = false;
1022 our_pred->args.str = argv[*arg_ptr];
1023 our_pred->est_success_rate = estimate_pattern_match_rate(our_pred->args.str, 0);
1024 (*arg_ptr)++;
1025 return true;
1028 static boolean
1029 parse_inum (const struct parser_table* entry, char **argv, int *arg_ptr)
1031 struct predicate *p = insert_num (argv, arg_ptr, entry);
1032 /* inode number is exact match only, so very low proportions of files match */
1033 p->est_success_rate = 1e-6;
1034 return p;
1037 /* -ipath is deprecated (at RMS's request) in favour of
1038 * -iwholename. See the node "GNU Manuals" in standards.texi
1039 * for the rationale for this (basically, GNU prefers the use
1040 * of the phrase "file name" to "path name"
1042 static boolean
1043 parse_ipath (const struct parser_table* entry, char **argv, int *arg_ptr)
1045 error (0, 0,
1046 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
1048 return parse_iwholename(entry, argv, arg_ptr);
1051 static boolean
1052 parse_iwholename (const struct parser_table* entry, char **argv, int *arg_ptr)
1054 struct predicate *our_pred;
1056 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1057 return false;
1059 fnmatch_sanitycheck();
1061 our_pred = insert_primary_withpred (entry, pred_ipath);
1062 our_pred->need_stat = our_pred->need_type = false;
1063 our_pred->args.str = argv[*arg_ptr];
1064 our_pred->est_success_rate = estimate_pattern_match_rate(our_pred->args.str, 0);
1065 (*arg_ptr)++;
1066 return true;
1069 static boolean
1070 parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr)
1072 return insert_regex (argv, arg_ptr, entry, RE_ICASE|options.regex_options);
1075 static boolean
1076 parse_links (const struct parser_table* entry, char **argv, int *arg_ptr)
1078 struct predicate *p = insert_num (argv, arg_ptr, entry);
1079 if (p->args.numinfo.l_val == 1)
1080 p->est_success_rate = 0.99;
1081 else if (p->args.numinfo.l_val == 2)
1082 p->est_success_rate = 0.01;
1083 else
1084 p->est_success_rate = 1e-3;
1085 return p;
1088 static boolean
1089 parse_lname (const struct parser_table* entry, char **argv, int *arg_ptr)
1091 struct predicate *our_pred;
1093 (void) argv;
1094 (void) arg_ptr;
1096 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1097 return false;
1099 fnmatch_sanitycheck();
1101 our_pred = insert_primary (entry);
1102 our_pred->args.str = argv[*arg_ptr];
1103 our_pred->est_success_rate = 0.1 * estimate_pattern_match_rate(our_pred->args.str, 0);
1104 (*arg_ptr)++;
1105 return true;
1108 static boolean
1109 parse_ls (const struct parser_table* entry, char **argv, int *arg_ptr)
1111 struct predicate *our_pred;
1113 (void) &argv;
1114 (void) &arg_ptr;
1116 our_pred = insert_primary (entry);
1117 our_pred->side_effects = our_pred->no_default_print = true;
1118 return true;
1121 static boolean
1122 parse_maxdepth (const struct parser_table* entry, char **argv, int *arg_ptr)
1124 int depth_len;
1125 (void) entry;
1127 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1128 return false;
1129 depth_len = strspn (argv[*arg_ptr], "0123456789");
1130 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
1131 return false;
1132 options.maxdepth = atoi (argv[*arg_ptr]);
1133 if (options.maxdepth < 0)
1134 return false;
1135 (*arg_ptr)++;
1136 return parse_noop(entry, argv, arg_ptr);
1139 static boolean
1140 parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr)
1142 int depth_len;
1143 (void) entry;
1145 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1146 return false;
1147 depth_len = strspn (argv[*arg_ptr], "0123456789");
1148 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
1149 return false;
1150 options.mindepth = atoi (argv[*arg_ptr]);
1151 if (options.mindepth < 0)
1152 return false;
1153 (*arg_ptr)++;
1154 return parse_noop(entry, argv, arg_ptr);
1158 static boolean
1159 do_parse_xmin (const struct parser_table* entry, char **argv, int *arg_ptr, enum xval xv)
1161 struct predicate *our_pred;
1162 struct time_val tval;
1164 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1165 return false;
1167 tval.xval = xv;
1168 if (!get_relative_timestamp(argv[*arg_ptr], &tval,
1169 options.cur_day_start + DAYSECS, 60,
1170 "arithmetic overflow while converting %s minutes to a number of seconds"))
1171 return false;
1173 our_pred = insert_primary (entry);
1174 our_pred->args.reftime = tval;
1175 our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec);
1176 (*arg_ptr)++;
1177 return true;
1179 static boolean
1180 parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr)
1182 return do_parse_xmin(entry, argv, arg_ptr, XVAL_ATIME);
1185 static boolean
1186 parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr)
1188 return do_parse_xmin(entry, argv, arg_ptr, XVAL_CTIME);
1192 static boolean
1193 parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr)
1195 return do_parse_xmin(entry, argv, arg_ptr, XVAL_MTIME);
1198 static boolean
1199 parse_name (const struct parser_table* entry, char **argv, int *arg_ptr)
1201 struct predicate *our_pred;
1203 (void) argv;
1204 (void) arg_ptr;
1206 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1207 return false;
1208 if (!check_name_arg("-name", argv[*arg_ptr]))
1209 return false;
1210 fnmatch_sanitycheck();
1212 our_pred = insert_primary (entry);
1213 our_pred->need_stat = our_pred->need_type = false;
1214 our_pred->args.str = argv[*arg_ptr];
1215 our_pred->est_success_rate = estimate_pattern_match_rate(our_pred->args.str, 0);
1216 (*arg_ptr)++;
1217 return true;
1220 static boolean
1221 parse_negate (const struct parser_table* entry, char **argv, int *arg_ptr)
1223 struct predicate *our_pred;
1225 (void) &argv;
1226 (void) &arg_ptr;
1228 our_pred = get_new_pred_chk_op (entry);
1229 our_pred->pred_func = pred_negate;
1230 our_pred->p_type = UNI_OP;
1231 our_pred->p_prec = NEGATE_PREC;
1232 our_pred->need_stat = our_pred->need_type = false;
1233 return true;
1236 static boolean
1237 parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr)
1239 struct predicate *our_pred;
1240 struct stat stat_newer;
1242 (void) argv;
1243 (void) arg_ptr;
1245 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1246 return false;
1247 set_stat_placeholders(&stat_newer);
1248 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
1249 error (1, errno, "%s", argv[*arg_ptr]);
1250 our_pred = insert_primary (entry);
1251 our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
1252 our_pred->args.reftime.xval = XVAL_MTIME;
1253 our_pred->args.reftime.kind = COMP_GT;
1254 our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
1255 (*arg_ptr)++;
1256 return true;
1260 static boolean
1261 parse_newerXY (const struct parser_table* entry, char **argv, int *arg_ptr)
1263 (void) argv;
1264 (void) arg_ptr;
1266 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1268 return false;
1270 else if (8u != strlen(argv[*arg_ptr]))
1272 return false;
1274 else
1276 char x, y;
1277 const char validchars[] = "aBcmt";
1279 assert(0 == strncmp("-newer", argv[*arg_ptr], 6));
1280 x = argv[*arg_ptr][6];
1281 y = argv[*arg_ptr][7];
1284 #if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC)
1285 if ('B' == x || 'B' == y)
1287 error(0, 0,
1288 _("This system does not provide a way to find the birth time of a file."));
1289 return 0;
1291 #endif
1293 /* -newertY (for any Y) is invalid. */
1294 if (x == 't'
1295 || 0 == strchr(validchars, x)
1296 || 0 == strchr( validchars, y))
1298 return false;
1300 else
1302 struct predicate *our_pred;
1304 /* Because this item is ARG_SPECIAL_PARSE, we have to advance arg_ptr
1305 * past the test name (for most other tests, this is already done)
1307 (*arg_ptr)++;
1309 our_pred = insert_primary (entry);
1312 switch (x)
1314 case 'a':
1315 our_pred->args.reftime.xval = XVAL_ATIME;
1316 break;
1317 case 'B':
1318 our_pred->args.reftime.xval = XVAL_BIRTHTIME;
1319 break;
1320 case 'c':
1321 our_pred->args.reftime.xval = XVAL_CTIME;
1322 break;
1323 case 'm':
1324 our_pred->args.reftime.xval = XVAL_MTIME;
1325 break;
1326 default:
1327 assert(strchr(validchars, x));
1328 assert(0);
1331 if ('t' == y)
1333 if (!get_date(&our_pred->args.reftime.ts,
1334 argv[*arg_ptr],
1335 &options.start_time))
1337 error(1, 0,
1338 _("I cannot figure out how to interpret `%s' as a date or time"),
1339 argv[*arg_ptr]);
1342 else
1344 struct stat stat_newer;
1346 /* Stat the named file. */
1347 set_stat_placeholders(&stat_newer);
1348 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
1349 error (1, errno, "%s", argv[*arg_ptr]);
1351 if (!get_stat_Ytime(&stat_newer, y, &our_pred->args.reftime.ts))
1353 /* We cannot extract a timestamp from the struct stat. */
1354 error(1, 0, _("Cannot obtain birth time of file `%s'"),
1355 argv[*arg_ptr]);
1358 our_pred->args.reftime.kind = COMP_GT;
1359 our_pred->est_success_rate = estimate_timestamp_success_rate(our_pred->args.reftime.ts.tv_sec);
1360 (*arg_ptr)++;
1362 assert(our_pred->pred_func != NULL);
1363 assert(our_pred->pred_func == pred_newerXY);
1364 assert(our_pred->need_stat);
1365 return true;
1371 static boolean
1372 parse_noleaf (const struct parser_table* entry, char **argv, int *arg_ptr)
1374 (void) &argv;
1375 (void) &arg_ptr;
1376 (void) entry;
1378 options.no_leaf_check = true;
1379 return parse_noop(entry, argv, arg_ptr);
1382 #ifdef CACHE_IDS
1383 /* Arbitrary amount by which to increase size
1384 of `uid_unused' and `gid_unused'. */
1385 #define ALLOC_STEP 2048
1387 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1388 char *uid_unused = NULL;
1390 /* Number of elements in `uid_unused'. */
1391 unsigned uid_allocated;
1393 /* Similar for GIDs and group entries. */
1394 char *gid_unused = NULL;
1395 unsigned gid_allocated;
1396 #endif
1398 static boolean
1399 parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr)
1401 struct predicate *our_pred;
1403 (void) &argv;
1404 (void) &arg_ptr;
1406 our_pred = insert_primary (entry);
1407 our_pred->est_success_rate = 1e-4;
1408 #ifdef CACHE_IDS
1409 if (gid_unused == NULL)
1411 struct group *gr;
1413 gid_allocated = ALLOC_STEP;
1414 gid_unused = xmalloc (gid_allocated);
1415 memset (gid_unused, 1, gid_allocated);
1416 setgrent ();
1417 while ((gr = getgrent ()) != NULL)
1419 if ((unsigned) gr->gr_gid >= gid_allocated)
1421 unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
1422 gid_unused = xrealloc (gid_unused, new_allocated);
1423 memset (gid_unused + gid_allocated, 1,
1424 new_allocated - gid_allocated);
1425 gid_allocated = new_allocated;
1427 gid_unused[(unsigned) gr->gr_gid] = 0;
1429 endgrent ();
1431 #endif
1432 return true;
1435 static boolean
1436 parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr)
1438 struct predicate *our_pred;
1439 (void) argv;
1440 (void) arg_ptr;
1443 our_pred = insert_primary (entry);
1444 our_pred->est_success_rate = 1e-3;
1445 #ifdef CACHE_IDS
1446 if (uid_unused == NULL)
1448 struct passwd *pw;
1450 uid_allocated = ALLOC_STEP;
1451 uid_unused = xmalloc (uid_allocated);
1452 memset (uid_unused, 1, uid_allocated);
1453 setpwent ();
1454 while ((pw = getpwent ()) != NULL)
1456 if ((unsigned) pw->pw_uid >= uid_allocated)
1458 unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
1459 uid_unused = xrealloc (uid_unused, new_allocated);
1460 memset (uid_unused + uid_allocated, 1,
1461 new_allocated - uid_allocated);
1462 uid_allocated = new_allocated;
1464 uid_unused[(unsigned) pw->pw_uid] = 0;
1466 endpwent ();
1468 #endif
1469 return true;
1472 static boolean
1473 parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr)
1475 (void) argv;
1476 (void) arg_ptr;
1477 (void) entry;
1479 options.warnings = false;
1480 return parse_noop(entry, argv, arg_ptr);
1483 static boolean
1484 parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr)
1486 return insert_exec_ok ("-ok", entry, get_start_dirfd(), argv, arg_ptr);
1489 static boolean
1490 parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr)
1492 return insert_exec_ok ("-okdir", entry, -1, argv, arg_ptr);
1495 boolean
1496 parse_open (const struct parser_table* entry, char **argv, int *arg_ptr)
1498 struct predicate *our_pred;
1500 (void) argv;
1501 (void) arg_ptr;
1503 our_pred = get_new_pred_chk_op (entry);
1504 our_pred->pred_func = pred_open;
1505 our_pred->p_type = OPEN_PAREN;
1506 our_pred->p_prec = NO_PREC;
1507 our_pred->need_stat = our_pred->need_type = false;
1508 return true;
1511 static boolean
1512 parse_or (const struct parser_table* entry, char **argv, int *arg_ptr)
1514 struct predicate *our_pred;
1516 (void) argv;
1517 (void) arg_ptr;
1519 our_pred = get_new_pred (entry);
1520 our_pred->pred_func = pred_or;
1521 our_pred->p_type = BI_OP;
1522 our_pred->p_prec = OR_PREC;
1523 our_pred->need_stat = our_pred->need_type = false;
1524 return true;
1527 /* -path is deprecated (at RMS's request) in favour of
1528 * -iwholename. See the node "GNU Manuals" in standards.texi
1529 * for the rationale for this (basically, GNU prefers the use
1530 * of the phrase "file name" to "path name".
1532 * We do not issue a warning that this usage is deprecated
1533 * since HPUX find supports this predicate also.
1535 static boolean
1536 parse_path (const struct parser_table* entry, char **argv, int *arg_ptr)
1538 return parse_wholename(entry, argv, arg_ptr);
1541 static boolean
1542 parse_wholename (const struct parser_table* entry, char **argv, int *arg_ptr)
1544 struct predicate *our_pred;
1546 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1547 return false;
1548 our_pred = insert_primary_withpred (entry, pred_path);
1549 our_pred->need_stat = our_pred->need_type = false;
1550 our_pred->args.str = argv[*arg_ptr];
1551 our_pred->est_success_rate = estimate_pattern_match_rate(our_pred->args.str, 0);
1552 (*arg_ptr)++;
1553 return true;
1556 static boolean
1557 parse_perm (const struct parser_table* entry, char **argv, int *arg_ptr)
1559 mode_t perm_val[2];
1560 float rate;
1561 int mode_start = 0;
1562 boolean havekind = false;
1563 enum permissions_type kind = PERM_EXACT;
1564 struct mode_change *change = NULL;
1565 struct predicate *our_pred;
1567 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1568 return false;
1570 switch (argv[*arg_ptr][0])
1572 case '-':
1573 mode_start = 1;
1574 kind = PERM_AT_LEAST;
1575 havekind = true;
1576 rate = 0.2;
1577 break;
1579 case '+':
1580 change = mode_compile (argv[*arg_ptr]);
1581 if (NULL == change)
1583 /* Most likely the caller is an old script that is still
1584 * using the obsolete GNU syntax '-perm +MODE'. This old
1585 * syntax was withdrawn in favor of '-perm /MODE' because
1586 * it is incompatible with POSIX in some cases, but we
1587 * still support uses of it that are not incompatible with
1588 * POSIX.
1590 mode_start = 1;
1591 kind = PERM_ANY;
1592 rate = 0.3;
1594 else
1596 /* This is a POSIX-compatible usage */
1597 mode_start = 0;
1598 kind = PERM_EXACT;
1599 rate = 0.1;
1601 havekind = true;
1602 break;
1604 case '/': /* GNU extension */
1605 mode_start = 1;
1606 kind = PERM_ANY;
1607 havekind = true;
1608 rate = 0.3;
1609 break;
1611 default:
1612 /* For example, '-perm 0644', which is valid and matches
1613 * only files whose mode is exactly 0644.
1615 mode_start = 0;
1616 kind = PERM_EXACT;
1617 havekind = true;
1618 rate = 0.01;
1619 break;
1622 if (NULL == change)
1624 change = mode_compile (argv[*arg_ptr] + mode_start);
1625 if (NULL == change)
1626 error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
1628 perm_val[0] = mode_adjust (0, false, 0, change, NULL);
1629 perm_val[1] = mode_adjust (0, true, 0, change, NULL);
1630 free (change);
1632 if (('/' == argv[*arg_ptr][0]) && (0 == perm_val[0]) && (0 == perm_val[1]))
1634 /* The meaning of -perm /000 will change in the future. It
1635 * currently matches no files, but like -perm -000 it should
1636 * match all files.
1638 * Starting in 2005, we used to issue a warning message
1639 * informing the user that the behaviour would change in the
1640 * future. We have now changed the behaviour and issue a
1641 * warning message that the behaviour recently changed.
1643 error (0, 0,
1644 _("warning: you have specified a mode pattern %s (which is "
1645 "equivalent to /000). The meaning of -perm /000 has now been "
1646 "changed to be consistent with -perm -000; that is, while it "
1647 "used to match no files, it now matches all files."),
1648 argv[*arg_ptr]);
1650 kind = PERM_AT_LEAST;
1651 havekind = true;
1653 /* The "magic" number below is just the fraction of files on my
1654 * own system that "-type l -xtype l" fails for (i.e. unbroken symlinks).
1655 * Actual totals are 1472 and 1073833.
1657 rate = 0.9986; /* probably matches anything but a broken symlink */
1660 our_pred = insert_primary (entry);
1661 our_pred->est_success_rate = rate;
1662 if (havekind)
1664 our_pred->args.perm.kind = kind;
1666 else
1669 switch (argv[*arg_ptr][0])
1671 case '-':
1672 our_pred->args.perm.kind = PERM_AT_LEAST;
1673 break;
1674 case '+':
1675 our_pred->args.perm.kind = PERM_ANY;
1676 break;
1677 default:
1678 our_pred->args.perm.kind = PERM_EXACT;
1679 break;
1682 memcpy (our_pred->args.perm.val, perm_val, sizeof perm_val);
1683 (*arg_ptr)++;
1684 return true;
1687 boolean
1688 parse_print (const struct parser_table* entry, char **argv, int *arg_ptr)
1690 struct predicate *our_pred;
1692 (void) argv;
1693 (void) arg_ptr;
1695 our_pred = insert_primary (entry);
1696 /* -print has the side effect of printing. This prevents us
1697 from doing undesired multiple printing when the user has
1698 already specified -print. */
1699 our_pred->side_effects = our_pred->no_default_print = true;
1700 our_pred->need_stat = our_pred->need_type = false;
1701 our_pred->args.printf_vec.segment = NULL;
1702 our_pred->args.printf_vec.stream = stdout;
1703 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(stdout);
1704 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
1706 return true;
1709 static boolean
1710 parse_print0 (const struct parser_table* entry, char **argv, int *arg_ptr)
1712 struct predicate *our_pred;
1714 (void) argv;
1715 (void) arg_ptr;
1717 our_pred = insert_primary (entry);
1718 /* -print0 has the side effect of printing. This prevents us
1719 from doing undesired multiple printing when the user has
1720 already specified -print0. */
1721 our_pred->side_effects = our_pred->no_default_print = true;
1722 our_pred->need_stat = our_pred->need_type = false;
1723 return true;
1726 static boolean
1727 parse_printf (const struct parser_table* entry, char **argv, int *arg_ptr)
1729 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1730 return false;
1731 return insert_fprintf (stdout, entry, pred_fprintf, argv, arg_ptr);
1734 static boolean
1735 parse_prune (const struct parser_table* entry, char **argv, int *arg_ptr)
1737 struct predicate *our_pred;
1739 (void) argv;
1740 (void) arg_ptr;
1742 our_pred = insert_primary (entry);
1743 our_pred->need_stat = our_pred->need_type = false;
1744 /* -prune has a side effect that it does not descend into
1745 the current directory. */
1746 our_pred->side_effects = true;
1747 our_pred->no_default_print = false;
1748 return true;
1751 static boolean
1752 parse_quit (const struct parser_table* entry, char **argv, int *arg_ptr)
1754 struct predicate *our_pred = insert_primary (entry);
1755 (void) argv;
1756 (void) arg_ptr;
1757 our_pred->need_stat = our_pred->need_type = false;
1758 our_pred->side_effects = true; /* Exiting is a side effect... */
1759 our_pred->no_default_print = false; /* Don't inhibit the default print, though. */
1760 our_pred->est_success_rate = 1.0f;
1761 return true;
1765 static boolean
1766 parse_regextype (const struct parser_table* entry, char **argv, int *arg_ptr)
1768 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1769 return false;
1771 /* collect the regex type name */
1772 options.regex_options = get_regex_type(argv[*arg_ptr]);
1773 (*arg_ptr)++;
1775 return parse_noop(entry, argv, arg_ptr);
1779 static boolean
1780 parse_regex (const struct parser_table* entry, char **argv, int *arg_ptr)
1782 return insert_regex (argv, arg_ptr, entry, options.regex_options);
1785 static boolean
1786 insert_regex (char **argv, int *arg_ptr, const struct parser_table *entry, int regex_options)
1788 struct predicate *our_pred;
1789 struct re_pattern_buffer *re;
1790 const char *error_message;
1792 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1793 return false;
1794 our_pred = insert_primary_withpred (entry, pred_regex);
1795 our_pred->need_stat = our_pred->need_type = false;
1796 re = (struct re_pattern_buffer *)
1797 xmalloc (sizeof (struct re_pattern_buffer));
1798 our_pred->args.regex = re;
1799 re->allocated = 100;
1800 re->buffer = (unsigned char *) xmalloc (re->allocated);
1801 re->fastmap = NULL;
1803 re_set_syntax(regex_options);
1804 re->syntax = regex_options;
1805 re->translate = NULL;
1807 error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
1808 re);
1809 if (error_message)
1810 error (1, 0, "%s", error_message);
1811 our_pred->est_success_rate = estimate_pattern_match_rate(argv[*arg_ptr], 1);
1812 (*arg_ptr)++;
1813 return true;
1816 static boolean
1817 parse_size (const struct parser_table* entry, char **argv, int *arg_ptr)
1819 struct predicate *our_pred;
1820 uintmax_t num;
1821 enum comparison_type c_type;
1822 int blksize = 512;
1823 int len;
1825 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1826 return false;
1827 len = strlen (argv[*arg_ptr]);
1828 if (len == 0)
1829 error (1, 0, _("invalid null argument to -size"));
1830 switch (argv[*arg_ptr][len - 1])
1832 case 'b':
1833 blksize = 512;
1834 argv[*arg_ptr][len - 1] = '\0';
1835 break;
1837 case 'c':
1838 blksize = 1;
1839 argv[*arg_ptr][len - 1] = '\0';
1840 break;
1842 case 'k':
1843 blksize = 1024;
1844 argv[*arg_ptr][len - 1] = '\0';
1845 break;
1847 case 'M': /* Megabytes */
1848 blksize = 1024*1024;
1849 argv[*arg_ptr][len - 1] = '\0';
1850 break;
1852 case 'G': /* Gigabytes */
1853 blksize = 1024*1024*1024;
1854 argv[*arg_ptr][len - 1] = '\0';
1855 break;
1857 case 'w':
1858 blksize = 2;
1859 argv[*arg_ptr][len - 1] = '\0';
1860 break;
1862 case '0':
1863 case '1':
1864 case '2':
1865 case '3':
1866 case '4':
1867 case '5':
1868 case '6':
1869 case '7':
1870 case '8':
1871 case '9':
1872 break;
1874 default:
1875 error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
1877 /* TODO: accept fractional megabytes etc. ? */
1878 if (!get_num (argv[*arg_ptr], &num, &c_type))
1879 return false;
1880 our_pred = insert_primary (entry);
1881 our_pred->args.size.kind = c_type;
1882 our_pred->args.size.blocksize = blksize;
1883 our_pred->args.size.size = num;
1884 our_pred->need_stat = true;
1885 our_pred->need_type = false;
1887 if (COMP_GT == c_type)
1888 our_pred->est_success_rate = (num*blksize > 20480) ? 0.1 : 0.9;
1889 else if (COMP_LT == c_type)
1890 our_pred->est_success_rate = (num*blksize > 20480) ? 0.9 : 0.1;
1891 else
1892 our_pred->est_success_rate = 0.01;
1894 (*arg_ptr)++;
1895 return true;
1899 static boolean
1900 parse_samefile (const struct parser_table* entry, char **argv, int *arg_ptr)
1902 struct predicate *our_pred;
1903 struct stat st;
1905 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1906 return false;
1907 set_stat_placeholders(&st);
1908 if ((*options.xstat) (argv[*arg_ptr], &st))
1909 error (1, errno, "%s", argv[*arg_ptr]);
1911 our_pred = insert_primary (entry);
1912 our_pred->args.fileid.ino = st.st_ino;
1913 our_pred->args.fileid.dev = st.st_dev;
1914 our_pred->need_type = false;
1915 our_pred->need_stat = true;
1916 our_pred->est_success_rate = 0.01f;
1917 (*arg_ptr)++;
1918 return true;
1921 #if 0
1922 static boolean
1923 parse_show_control_chars (const struct parser_table* entry, char **argv, int *arg_ptr)
1925 const char *arg;
1926 const char *errmsg = _("The -show-control-chars option takes a single argument which "
1927 "must be 'literal' or 'safe'");
1929 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1931 error (1, errno, "%s", errmsg);
1932 return false;
1934 else
1936 arg = argv[*arg_ptr];
1938 if (0 == strcmp("literal", arg))
1940 options.literal_control_chars = true;
1942 else if (0 == strcmp("safe", arg))
1944 options.literal_control_chars = false;
1946 else
1948 error (1, errno, "%s", errmsg);
1949 return false;
1951 (*arg_ptr)++; /* consume the argument. */
1952 return true;
1955 #endif
1958 static boolean
1959 parse_true (const struct parser_table* entry, char **argv, int *arg_ptr)
1961 struct predicate *our_pred;
1963 (void) argv;
1964 (void) arg_ptr;
1966 our_pred = insert_primary (entry);
1967 our_pred->need_stat = our_pred->need_type = false;
1968 our_pred->est_success_rate = 1.0f;
1969 return true;
1972 static boolean
1973 parse_noop (const struct parser_table* entry, char **argv, int *arg_ptr)
1975 (void) entry;
1976 return parse_true(get_noop(), argv, arg_ptr);
1979 static boolean
1980 parse_accesscheck (const struct parser_table* entry, char **argv, int *arg_ptr)
1982 struct predicate *our_pred;
1983 (void) argv;
1984 (void) arg_ptr;
1985 our_pred = insert_primary (entry);
1986 our_pred->need_stat = our_pred->need_type = false;
1987 our_pred->side_effects = our_pred->no_default_print = false;
1988 if (our_pred->pred_func == pred_executable)
1989 our_pred->est_success_rate = 0.2;
1990 else
1991 our_pred->est_success_rate = 0.9;
1992 return true;
1995 static boolean
1996 parse_type (const struct parser_table* entry, char **argv, int *arg_ptr)
1998 return insert_type (argv, arg_ptr, entry, pred_type);
2001 static boolean
2002 parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr)
2004 struct predicate *p = insert_num (argv, arg_ptr, entry);
2005 p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2;
2006 return p;
2009 static boolean
2010 parse_used (const struct parser_table* entry, char **argv, int *arg_ptr)
2012 struct predicate *our_pred;
2013 struct time_val tval;
2014 const char *errmsg = "arithmetic overflow while converting %s days to a number of seconds";
2016 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2017 return false;
2019 /* The timespec is actually a delta value, so we use an origin of 0. */
2020 if (!get_relative_timestamp(argv[*arg_ptr], &tval, 0, DAYSECS, errmsg))
2021 return false;
2023 our_pred = insert_primary (entry);
2024 our_pred->args.reftime = tval;
2025 our_pred->est_success_rate = estimate_file_age_success_rate(tval.ts.tv_sec / DAYSECS);
2026 (*arg_ptr)++;
2027 return true;
2030 static boolean
2031 parse_user (const struct parser_table* entry, char **argv, int *arg_ptr)
2033 struct passwd *cur_pwd;
2034 struct predicate *our_pred;
2035 uid_t uid;
2036 int uid_len;
2038 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2039 return false;
2040 cur_pwd = getpwnam (argv[*arg_ptr]);
2041 endpwent ();
2042 if (cur_pwd != NULL)
2043 uid = cur_pwd->pw_uid;
2044 else
2046 uid_len = strspn (argv[*arg_ptr], "0123456789");
2047 if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
2048 return false;
2049 uid = atoi (argv[*arg_ptr]);
2051 our_pred = insert_primary (entry);
2052 our_pred->args.uid = uid;
2053 our_pred->est_success_rate = (our_pred->args.uid < 100) ? 0.99 : 0.2;
2054 (*arg_ptr)++;
2055 return true;
2058 static boolean
2059 parse_version (const struct parser_table* entry, char **argv, int *arg_ptr)
2061 extern char *version_string;
2062 int features = 0;
2063 int flags;
2065 (void) argv;
2066 (void) arg_ptr;
2067 (void) entry;
2069 fflush (stderr);
2070 printf (_("GNU find version %s\n"), version_string);
2071 printf (_("Built using GNU gnulib version %s\n"), gnulib_version);
2072 printf (_("Features enabled: "));
2074 #if CACHE_IDS
2075 printf("CACHE_IDS ");
2076 ++features;
2077 #endif
2078 #if DEBUG
2079 printf("DEBUG ");
2080 ++features;
2081 #endif
2082 #if DEBUG_STAT
2083 printf("DEBUG_STAT ");
2084 ++features;
2085 #endif
2086 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
2087 printf("D_TYPE ");
2088 ++features;
2089 #endif
2090 #if defined(O_NOFOLLOW)
2091 printf("O_NOFOLLOW(%s) ",
2092 (options.open_nofollow_available ? "enabled" : "disabled"));
2093 ++features;
2094 #endif
2095 #if defined(LEAF_OPTIMISATION)
2096 printf("LEAF_OPTIMISATION ");
2097 ++features;
2098 #endif
2100 flags = 0;
2101 if (is_fts_enabled(&flags))
2103 int nflags = 0;
2104 printf("FTS(");
2105 ++features;
2107 if (flags & FTS_CWDFD)
2109 if (nflags)
2111 printf(",");
2113 printf("FTS_CWDFD");
2114 ++nflags;
2116 printf(") ");
2119 printf("CBO(level=%d) ", (int)(options.optimisation_level));
2120 ++features;
2122 if (0 == features)
2124 /* For the moment, leave this as English in case someone wants
2125 to parse these strings. */
2126 printf("none");
2128 printf("\n");
2130 exit (0);
2133 static boolean
2134 parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr)
2136 (void) argv;
2137 (void) arg_ptr;
2138 (void) entry;
2139 options.stay_on_filesystem = true;
2140 return parse_noop(entry, argv, arg_ptr);
2143 static boolean
2144 parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
2146 (void) argv;
2147 (void) arg_ptr;
2148 (void) entry;
2149 options.ignore_readdir_race = true;
2150 return parse_noop(entry, argv, arg_ptr);
2153 static boolean
2154 parse_noignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
2156 (void) argv;
2157 (void) arg_ptr;
2158 (void) entry;
2159 options.ignore_readdir_race = false;
2160 return parse_noop(entry, argv, arg_ptr);
2163 static boolean
2164 parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr)
2166 (void) argv;
2167 (void) arg_ptr;
2168 (void) entry;
2169 options.warnings = true;
2170 return parse_noop(entry, argv, arg_ptr);
2173 static boolean
2174 parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr)
2176 (void) argv;
2177 (void) arg_ptr;
2178 return insert_type (argv, arg_ptr, entry, pred_xtype);
2181 static boolean
2182 insert_type (char **argv, int *arg_ptr, const struct parser_table *entry, PRED_FUNC which_pred)
2184 mode_t type_cell;
2185 struct predicate *our_pred;
2186 float rate = 0.5;
2188 if ((argv == NULL) || (argv[*arg_ptr] == NULL)
2189 || (strlen (argv[*arg_ptr]) != 1))
2190 return false;
2191 switch (argv[*arg_ptr][0])
2193 case 'b': /* block special */
2194 type_cell = S_IFBLK;
2195 rate = 0.01f;
2196 break;
2197 case 'c': /* character special */
2198 type_cell = S_IFCHR;
2199 rate = 0.01f;
2200 break;
2201 case 'd': /* directory */
2202 type_cell = S_IFDIR;
2203 rate = 0.4f;
2204 break;
2205 case 'f': /* regular file */
2206 type_cell = S_IFREG;
2207 rate = 0.95f;
2208 break;
2209 #ifdef S_IFLNK
2210 case 'l': /* symbolic link */
2211 type_cell = S_IFLNK;
2212 rate = 0.1f;
2213 break;
2214 #endif
2215 #ifdef S_IFIFO
2216 case 'p': /* pipe */
2217 type_cell = S_IFIFO;
2218 rate = 0.01f;
2219 break;
2220 #endif
2221 #ifdef S_IFSOCK
2222 case 's': /* socket */
2223 type_cell = S_IFSOCK;
2224 rate = 0.01f;
2225 break;
2226 #endif
2227 #ifdef S_IFDOOR
2228 case 'D': /* Solaris door */
2229 type_cell = S_IFDOOR;
2230 rate = 0.01f;
2231 break;
2232 #endif
2233 default: /* None of the above ... nuke 'em. */
2234 return false;
2236 our_pred = insert_primary_withpred (entry, which_pred);
2237 our_pred->est_success_rate = rate;
2239 /* Figure out if we will need to stat the file, because if we don't
2240 * need to follow symlinks, we can avoid a stat call by using
2241 * struct dirent.d_type.
2243 if (which_pred == pred_xtype)
2245 our_pred->need_stat = true;
2246 our_pred->need_type = false;
2248 else
2250 our_pred->need_stat = false; /* struct dirent is enough */
2251 our_pred->need_type = true;
2253 our_pred->args.type = type_cell;
2254 (*arg_ptr)++; /* Move on to next argument. */
2255 return true;
2259 /* Return true if the file accessed via FP is a terminal.
2261 static boolean
2262 stream_is_tty(FILE *fp)
2264 int fd = fileno(fp);
2265 if (-1 == fd)
2267 return false; /* not a valid stream */
2269 else
2271 return isatty(fd) ? true : false;
2279 /* XXX: do we need to pass FUNC to this function? */
2280 static boolean
2281 insert_fprintf (FILE *fp, const struct parser_table *entry, PRED_FUNC func, char **argv, int *arg_ptr)
2283 char *format; /* Beginning of unprocessed format string. */
2284 register char *scan; /* Current address in scanning `format'. */
2285 register char *scan2; /* Address inside of element being scanned. */
2286 struct segment **segmentp; /* Address of current segment. */
2287 struct predicate *our_pred;
2289 format = argv[(*arg_ptr)++];
2291 our_pred = insert_primary_withpred (entry, func);
2292 our_pred->side_effects = our_pred->no_default_print = true;
2293 our_pred->args.printf_vec.stream = fp;
2294 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(fp);
2295 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
2296 our_pred->need_type = false;
2297 our_pred->need_stat = false;
2298 our_pred->p_cost = NeedsNothing;
2300 segmentp = &our_pred->args.printf_vec.segment;
2301 *segmentp = NULL;
2303 for (scan = format; *scan; scan++)
2305 if (*scan == '\\')
2307 scan2 = scan + 1;
2308 if (*scan2 >= '0' && *scan2 <= '7')
2310 register int n, i;
2312 for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
2313 i++, scan2++)
2314 n = 8 * n + *scan2 - '0';
2315 scan2--;
2316 *scan = n;
2318 else
2320 switch (*scan2)
2322 case 'a':
2323 *scan = 7;
2324 break;
2325 case 'b':
2326 *scan = '\b';
2327 break;
2328 case 'c':
2329 make_segment (segmentp, format, scan - format,
2330 KIND_STOP, 0, 0,
2331 our_pred);
2332 if (our_pred->need_stat && (our_pred->p_cost < NeedsStatInfo))
2333 our_pred->p_cost = NeedsStatInfo;
2334 return true;
2335 case 'f':
2336 *scan = '\f';
2337 break;
2338 case 'n':
2339 *scan = '\n';
2340 break;
2341 case 'r':
2342 *scan = '\r';
2343 break;
2344 case 't':
2345 *scan = '\t';
2346 break;
2347 case 'v':
2348 *scan = '\v';
2349 break;
2350 case '\\':
2351 /* *scan = '\\'; * it already is */
2352 break;
2353 default:
2354 error (0, 0,
2355 _("warning: unrecognized escape `\\%c'"), *scan2);
2356 scan++;
2357 continue;
2360 segmentp = make_segment (segmentp, format, scan - format + 1,
2361 KIND_PLAIN, 0, 0,
2362 our_pred);
2363 format = scan2 + 1; /* Move past the escape. */
2364 scan = scan2; /* Incremented immediately by `for'. */
2366 else if (*scan == '%')
2368 if (scan[1] == 0)
2370 /* Trailing %. We don't like those. */
2371 error (1, 0, _("error: %s at end of format string"), scan);
2373 else if (scan[1] == '%')
2375 segmentp = make_segment (segmentp, format, scan - format + 1,
2376 KIND_PLAIN, 0, 0,
2377 our_pred);
2378 scan++;
2379 format = scan + 1;
2380 continue;
2382 /* Scan past flags, width and precision, to verify kind. */
2383 for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
2384 /* Do nothing. */ ;
2385 while (ISDIGIT (*scan2))
2386 scan2++;
2387 if (*scan2 == '.')
2388 for (scan2++; ISDIGIT (*scan2); scan2++)
2389 /* Do nothing. */ ;
2390 if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2))
2392 segmentp = make_segment (segmentp, format, scan2 - format,
2393 KIND_FORMAT, *scan2, 0,
2394 our_pred);
2395 scan = scan2;
2396 format = scan + 1;
2398 else if (strchr ("ABCT", *scan2) && scan2[1])
2400 segmentp = make_segment (segmentp, format, scan2 - format,
2401 KIND_FORMAT, scan2[0], scan2[1],
2402 our_pred);
2403 scan = scan2 + 1;
2404 format = scan + 1;
2405 continue;
2407 else
2409 /* An unrecognized % escape. Print the char after the %. */
2410 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
2411 *scan2);
2412 segmentp = make_segment (segmentp, format, scan - format,
2413 KIND_PLAIN, 0, 0,
2414 our_pred);
2415 format = scan + 1;
2416 continue;
2421 if (scan > format)
2422 make_segment (segmentp, format, scan - format, KIND_PLAIN, 0, 0,
2423 our_pred);
2424 return true;
2427 /* Create a new fprintf segment in *SEGMENT, with type KIND,
2428 from the text in FORMAT, which has length LEN.
2429 Return the address of the `next' pointer of the new segment. */
2431 static struct segment **
2432 make_segment (struct segment **segment,
2433 char *format,
2434 int len,
2435 int kind,
2436 char format_char,
2437 char aux_format_char,
2438 struct predicate *pred)
2440 enum EvaluationCost mycost = NeedsNothing;
2441 char *fmt;
2443 *segment = (struct segment *) xmalloc (sizeof (struct segment));
2445 (*segment)->segkind = kind;
2446 (*segment)->format_char[0] = format_char;
2447 (*segment)->format_char[1] = aux_format_char;
2448 (*segment)->next = NULL;
2449 (*segment)->text_len = len;
2451 fmt = (*segment)->text = xmalloc (len + sizeof "d");
2452 strncpy (fmt, format, len);
2453 fmt += len;
2455 switch (kind)
2457 case KIND_PLAIN: /* Plain text string, no % conversion. */
2458 case KIND_STOP: /* Terminate argument, no newline. */
2459 assert(0 == format_char);
2460 assert(0 == aux_format_char);
2461 *fmt = '\0';
2462 if (mycost > pred->p_cost)
2463 pred->p_cost = NeedsNothing;
2464 return &(*segment)->next;
2465 break;
2468 assert(kind == KIND_FORMAT);
2469 switch (format_char)
2471 case 'l': /* object of symlink */
2472 pred->need_stat = true;
2473 mycost = NeedsLinkName;
2474 *fmt++ = 's';
2475 break;
2477 case 'y': /* file type */
2478 pred->need_type = true;
2479 mycost = NeedsType;
2480 *fmt++ = 's';
2481 break;
2483 case 'a': /* atime in `ctime' format */
2484 case 'A': /* atime in user-specified strftime format */
2485 case 'B': /* birth time in user-specified strftime format */
2486 case 'c': /* ctime in `ctime' format */
2487 case 'C': /* ctime in user-specified strftime format */
2488 case 'F': /* filesystem type */
2489 case 'g': /* group name */
2490 case 'i': /* inode number */
2491 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
2492 case 's': /* size in bytes */
2493 case 't': /* mtime in `ctime' format */
2494 case 'T': /* mtime in user-specified strftime format */
2495 case 'u': /* user name */
2496 pred->need_stat = true;
2497 mycost = NeedsStatInfo;
2498 *fmt++ = 's';
2499 break;
2501 case 'S': /* sparseness */
2502 pred->need_stat = true;
2503 mycost = NeedsStatInfo;
2504 *fmt++ = 'g';
2505 break;
2507 case 'Y': /* symlink pointed file type */
2508 pred->need_stat = true;
2509 mycost = NeedsType; /* true for amortised effect */
2510 *fmt++ = 's';
2511 break;
2513 case 'f': /* basename of path */
2514 case 'h': /* leading directories part of path */
2515 case 'p': /* pathname */
2516 case 'P': /* pathname with ARGV element stripped */
2517 *fmt++ = 's';
2518 break;
2520 case 'H': /* ARGV element file was found under */
2521 *fmt++ = 's';
2522 break;
2524 /* Numeric items that one might expect to honour
2525 * #, 0, + flags but which do not.
2527 case 'G': /* GID number */
2528 case 'U': /* UID number */
2529 case 'b': /* size in 512-byte blocks (NOT birthtime in ctime fmt)*/
2530 case 'D': /* Filesystem device on which the file exits */
2531 case 'k': /* size in 1K blocks */
2532 case 'n': /* number of links */
2533 pred->need_stat = true;
2534 mycost = NeedsStatInfo;
2535 *fmt++ = 's';
2536 break;
2538 /* Numeric items that DO honour #, 0, + flags.
2540 case 'd': /* depth in search tree (0 = ARGV element) */
2541 *fmt++ = 'd';
2542 break;
2544 case 'm': /* mode as octal number (perms only) */
2545 *fmt++ = 'o';
2546 pred->need_stat = true;
2547 mycost = NeedsStatInfo;
2548 break;
2550 case '{':
2551 case '[':
2552 case '(':
2553 error (1, 0,
2554 _("error: the format directive `%%%c' is reserved for future use"),
2555 (int)kind);
2556 /*NOTREACHED*/
2557 break;
2559 *fmt = '\0';
2561 if (mycost > pred->p_cost)
2562 pred->p_cost = mycost;
2563 return &(*segment)->next;
2566 static void
2567 check_path_safety(const char *action, char **argv)
2569 const char *path = getenv("PATH");
2571 (void)argv;
2573 char *s;
2574 s = next_element(path, 1);
2575 while ((s = next_element ((char *) NULL, 1)) != NULL)
2577 if (0 == strcmp(s, "."))
2579 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)"),
2580 action);
2582 else if ('/' != s[0])
2584 /* Relative paths are also dangerous in $PATH. */
2585 error(1, 0, _("The ralative path %s is included in the PATH environment variable, which is insecure in combination with the %s action of find. Please remove that entry from $PATH"),
2586 s, action);
2592 /* handles both exec and ok predicate */
2593 static boolean
2594 new_insert_exec_ok (const char *action,
2595 const struct parser_table *entry,
2596 int dirfd,
2597 char **argv,
2598 int *arg_ptr)
2600 int start, end; /* Indexes in ARGV of start & end of cmd. */
2601 int i; /* Index into cmd args */
2602 int saw_braces; /* True if previous arg was '{}'. */
2603 boolean allow_plus; /* True if + is a valid terminator */
2604 int brace_count; /* Number of instances of {}. */
2605 PRED_FUNC func = entry->pred_func;
2606 enum BC_INIT_STATUS bcstatus;
2608 struct predicate *our_pred;
2609 struct exec_val *execp; /* Pointer for efficiency. */
2611 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2612 return false;
2614 our_pred = insert_primary_withpred (entry, func);
2615 our_pred->side_effects = our_pred->no_default_print = true;
2616 our_pred->need_type = our_pred->need_stat = false;
2618 execp = &our_pred->args.exec_vec;
2620 if ((func != pred_okdir) && (func != pred_ok))
2622 allow_plus = true;
2623 execp->close_stdin = false;
2625 else
2627 allow_plus = false;
2628 /* If find reads stdin (i.e. for -ok and similar), close stdin
2629 * in the child to prevent some script from consiming the output
2630 * intended for find.
2632 execp->close_stdin = true;
2636 if ((func == pred_execdir) || (func == pred_okdir))
2638 options.ignore_readdir_race = false;
2639 check_path_safety(action, argv);
2640 execp->use_current_dir = true;
2642 else
2644 execp->use_current_dir = false;
2647 our_pred->args.exec_vec.multiple = 0;
2649 /* Count the number of args with path replacements, up until the ';'.
2650 * Also figure out if the command is terminated by ";" or by "+".
2652 start = *arg_ptr;
2653 for (end = start, saw_braces=0, brace_count=0;
2654 (argv[end] != NULL)
2655 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2656 end++)
2658 /* For -exec and -execdir, "{} +" can terminate the command. */
2659 if ( allow_plus
2660 && argv[end][0] == '+' && argv[end][1] == 0
2661 && saw_braces)
2663 our_pred->args.exec_vec.multiple = 1;
2664 break;
2667 saw_braces = 0;
2668 if (strstr (argv[end], "{}")) /* XXX: wrong for multibyte locales */
2670 saw_braces = 1;
2671 ++brace_count;
2673 if (0 == end && (func == pred_execdir || func == pred_okdir))
2675 /* The POSIX standard says that {} replacement should
2676 * occur even in the utility name. This is insecure
2677 * since it means we will be executing a command whose
2678 * name is chosen according to whatever find finds in
2679 * the filesystem. That can be influenced by an
2680 * attacker. Hence for -execdir and -okdir this is not
2681 * allowed. We can specify this as those options are
2682 * not defined by POSIX.
2684 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2689 /* Fail if no command given or no semicolon found. */
2690 if ((end == start) || (argv[end] == NULL))
2692 *arg_ptr = end;
2693 free(our_pred);
2694 return false;
2697 if (our_pred->args.exec_vec.multiple && brace_count > 1)
2700 const char *suffix;
2701 if (func == pred_execdir)
2702 suffix = "dir";
2703 else
2704 suffix = "";
2706 error(1, 0,
2707 _("Only one instance of {} is supported with -exec%s ... +"),
2708 suffix);
2711 /* We use a switch statement here so that
2712 * the compiler warns us when we forget to handle a
2713 * newly invented enum value.
2715 bcstatus = bc_init_controlinfo(&execp->ctl);
2716 switch (bcstatus)
2718 case BC_INIT_ENV_TOO_BIG:
2719 error(1, 0,
2720 _("The environment is too large for exec()."));
2721 break;
2722 case BC_INIT_OK:
2723 /* Good news. Carry on. */
2724 break;
2726 bc_use_sensible_arg_max(&execp->ctl);
2729 execp->ctl.exec_callback = launch;
2731 if (our_pred->args.exec_vec.multiple)
2733 /* "+" terminator, so we can just append our arguments after the
2734 * command and initial arguments.
2736 execp->replace_vec = NULL;
2737 execp->ctl.replace_pat = NULL;
2738 execp->ctl.rplen = 0;
2739 execp->ctl.lines_per_exec = 0; /* no limit */
2740 execp->ctl.args_per_exec = 0; /* no limit */
2742 /* remember how many arguments there are */
2743 execp->ctl.initial_argc = (end-start) - 1;
2745 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2746 bc_init_state(&execp->ctl, &execp->state, execp);
2748 /* Gather the initial arguments. Skip the {}. */
2749 for (i=start; i<end-1; ++i)
2751 bc_push_arg(&execp->ctl, &execp->state,
2752 argv[i], strlen(argv[i])+1,
2753 NULL, 0,
2757 else
2759 /* Semicolon terminator - more than one {} is supported, so we
2760 * have to do brace-replacement.
2762 execp->num_args = end - start;
2764 execp->ctl.replace_pat = "{}";
2765 execp->ctl.rplen = strlen(execp->ctl.replace_pat);
2766 execp->ctl.lines_per_exec = 0; /* no limit */
2767 execp->ctl.args_per_exec = 0; /* no limit */
2768 execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
2771 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2772 bc_init_state(&execp->ctl, &execp->state, execp);
2774 /* Remember the (pre-replacement) arguments for later. */
2775 for (i=0; i<execp->num_args; ++i)
2777 execp->replace_vec[i] = argv[i+start];
2781 if (argv[end] == NULL)
2782 *arg_ptr = end;
2783 else
2784 *arg_ptr = end + 1;
2786 return true;
2791 static boolean
2792 insert_exec_ok (const char *action, const struct parser_table *entry, int dirfd, char **argv, int *arg_ptr)
2794 return new_insert_exec_ok(action, entry, dirfd, argv, arg_ptr);
2799 /* Get a timestamp and comparison type.
2801 STR is the ASCII representation.
2802 Set *NUM_DAYS to the number of days/minutes/whatever, taken as being
2803 relative to ORIGIN (usually the current moment or midnight).
2804 Thus the sense of the comparison type appears to be reversed.
2805 Set *COMP_TYPE to the kind of comparison that is requested.
2806 Issue OVERFLOWMESSAGE if overflow occurs.
2807 Return true if all okay, false if input error.
2809 Used by -atime, -ctime and -mtime (parsers) to
2810 get the appropriate information for a time predicate processor. */
2812 static boolean
2813 get_relative_timestamp (char *str,
2814 struct time_val *result,
2815 time_t origin,
2816 double sec_per_unit,
2817 const char *overflowmessage)
2819 uintmax_t checkval;
2820 double offset, seconds, f;
2822 if (get_comp_type(&str, &result->kind))
2824 /* Invert the sense of the comparison */
2825 switch (result->kind)
2827 case COMP_LT: result->kind = COMP_GT; break;
2828 case COMP_GT: result->kind = COMP_LT; break;
2829 default: break;
2832 /* Convert the ASCII number into floating-point. */
2833 if (xstrtod(str, NULL, &offset, strtod))
2835 /* Separate the floating point number the user specified
2836 * (which is a number of days, or minutes, etc) into an
2837 * integral number of seconds (SECONDS) and a fraction (F).
2839 f = modf(offset * sec_per_unit, &seconds);
2841 result->ts.tv_sec = origin - seconds;
2842 result->ts.tv_nsec = fabs(f * 1e9);
2844 /* Check for overflow. */
2845 checkval = (uintmax_t)origin - seconds;
2846 if (checkval != result->ts.tv_sec)
2848 /* an overflow has occurred. */
2849 error (1, 0, overflowmessage, str);
2851 return true;
2853 else
2855 /* Conversion from ASCII to double failed. */
2856 return false;
2859 else
2861 return false;
2865 /* Insert a time predicate based on the information in ENTRY.
2866 ARGV is a pointer to the argument array.
2867 ARG_PTR is a pointer to an index into the array, incremented if
2868 all went well.
2870 Return true if input is valid, false if not.
2872 A new predicate node is assigned, along with an argument node
2873 obtained with malloc.
2875 Used by -atime, -ctime, and -mtime parsers. */
2877 static boolean
2878 parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr)
2880 struct predicate *our_pred;
2881 struct time_val tval;
2882 enum comparison_type comp;
2883 char *s;
2884 const char *errmsg = "arithmetic overflow while converting %s days to a number of seconds";
2885 time_t origin;
2887 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2888 return false;
2890 /* Decide the origin by previewing the comparison type. */
2891 origin = options.cur_day_start;
2892 s = argv[*arg_ptr];
2893 if (get_comp_type(&s, &comp))
2895 /* Remember, we invert the sense of the comparison, so this tests against COMP_LT instead of COMP_GT... */
2896 if (COMP_LT == tval.kind)
2898 uintmax_t expected = origin + (DAYSECS-1);
2899 origin += (DAYSECS-1);
2900 if (origin != expected)
2902 error(1, 0,
2903 _("arithmetic overflow when trying to calculate the end of today"));
2906 /* We discard the value of comp here, as get_relative_timestamp
2907 * will set tval.kind.
2911 if (!get_relative_timestamp(argv[*arg_ptr], &tval, origin, DAYSECS, errmsg))
2912 return false;
2914 our_pred = insert_primary (entry);
2915 our_pred->args.reftime = tval;
2916 our_pred->est_success_rate = estimate_timestamp_success_rate(tval.ts.tv_sec);
2917 (*arg_ptr)++;
2919 if (options.debug_options & DebugExpressionTree)
2921 time_t t;
2923 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2924 fprintf (stderr, " type: %s %s ",
2925 (tval.kind == COMP_GT) ? "gt" :
2926 ((tval.kind == COMP_LT) ? "lt" : ((tval.kind == COMP_EQ) ? "eq" : "?")),
2927 (tval.kind == COMP_GT) ? " >" :
2928 ((tval.kind == COMP_LT) ? " <" : ((tval.kind == COMP_EQ) ? ">=" : " ?")));
2929 t = our_pred->args.reftime.ts.tv_sec;
2930 fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.reftime.ts.tv_sec, ctime (&t));
2931 if (tval.kind == COMP_EQ)
2933 t = our_pred->args.reftime.ts.tv_sec += DAYSECS;
2934 fprintf (stderr, " < %ju %s",
2935 (uintmax_t) our_pred->args.reftime.ts.tv_sec, ctime (&t));
2936 our_pred->args.reftime.ts.tv_sec -= DAYSECS;
2940 return true;
2943 /* Get the comparison type prefix (if any) from a number argument.
2944 The prefix is at *STR.
2945 Set *COMP_TYPE to the kind of comparison that is requested.
2946 Advance *STR beyond any initial comparison prefix.
2948 Return true if all okay, false if input error. */
2949 static boolean
2950 get_comp_type(char **str, enum comparison_type *comp_type)
2952 switch (**str)
2954 case '+':
2955 *comp_type = COMP_GT;
2956 (*str)++;
2957 break;
2958 case '-':
2959 *comp_type = COMP_LT;
2960 (*str)++;
2961 break;
2962 default:
2963 *comp_type = COMP_EQ;
2964 break;
2966 return true;
2973 /* Get a number with comparison information.
2974 The sense of the comparison information is 'normal'; that is,
2975 '+' looks for a count > than the number and '-' less than.
2977 STR is the ASCII representation of the number.
2978 Set *NUM to the number.
2979 Set *COMP_TYPE to the kind of comparison that is requested.
2981 Return true if all okay, false if input error. */
2983 static boolean
2984 get_num (char *str,
2985 uintmax_t *num,
2986 enum comparison_type *comp_type)
2988 char *pend;
2990 if (str == NULL)
2991 return false;
2993 /* Figure out the comparison type if the caller accepts one. */
2994 if (comp_type)
2996 if (!get_comp_type(&str, comp_type))
2997 return false;
3000 return xstrtoumax (str, &pend, 10, num, "") == LONGINT_OK;
3003 /* Insert a number predicate.
3004 ARGV is a pointer to the argument array.
3005 *ARG_PTR is an index into ARGV, incremented if all went well.
3006 *PRED is the predicate processor to insert.
3008 Return true if input is valid, false if error.
3010 A new predicate node is assigned, along with an argument node
3011 obtained with malloc.
3013 Used by -inum and -links parsers. */
3015 static struct predicate *
3016 insert_num (char **argv, int *arg_ptr, const struct parser_table *entry)
3018 struct predicate *our_pred;
3019 uintmax_t num;
3020 enum comparison_type c_type;
3022 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
3023 return NULL;
3024 if (!get_num (argv[*arg_ptr], &num, &c_type))
3025 return NULL;
3026 our_pred = insert_primary (entry);
3027 our_pred->args.numinfo.kind = c_type;
3028 our_pred->args.numinfo.l_val = num;
3029 (*arg_ptr)++;
3031 if (options.debug_options & DebugExpressionTree)
3033 fprintf (stderr, "inserting %s\n", our_pred->p_name);
3034 fprintf (stderr, " type: %s %s ",
3035 (c_type == COMP_GT) ? "gt" :
3036 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
3037 (c_type == COMP_GT) ? " >" :
3038 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
3039 fprintf (stderr, "%ju\n", our_pred->args.numinfo.l_val);
3041 return our_pred;
3044 static FILE *
3045 open_output_file (char *path)
3047 FILE *f;
3049 if (!strcmp (path, "/dev/stderr"))
3050 return stderr;
3051 else if (!strcmp (path, "/dev/stdout"))
3052 return stdout;
3053 f = fopen_safer (path, "w");
3054 if (f == NULL)
3055 error (1, errno, "%s", path);
3056 return f;