Ran update-po target
[findutils.git] / find / parser.c
blob6e0befcba77efb747adffbddf653ed61ea798041
1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 91, 92, 93, 94, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 USA.
21 #include "defs.h"
22 #include <ctype.h>
23 #include <pwd.h>
24 #include <grp.h>
25 #include <fnmatch.h>
26 #include "../gnulib/lib/modechange.h"
27 #include "modetype.h"
28 #include "../gnulib/lib/xstrtol.h"
29 #include "../gnulib/lib/xalloc.h"
30 #include "buildcmd.h"
31 #include "nextelem.h"
33 #ifdef HAVE_FCNTL_H
34 #include <fcntl.h>
35 #else
36 #include <sys/file.h>
37 #endif
39 #if ENABLE_NLS
40 # include <libintl.h>
41 # define _(Text) gettext (Text)
42 #else
43 # define _(Text) Text
44 #endif
45 #ifdef gettext_noop
46 # define N_(String) gettext_noop (String)
47 #else
48 /* See locate.c for explanation as to why not use (String) */
49 # define N_(String) String
50 #endif
52 #if !defined (isascii) || defined (STDC_HEADERS)
53 #ifdef isascii
54 #undef isascii
55 #endif
56 #define isascii(c) 1
57 #endif
59 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
60 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
62 #ifndef HAVE_ENDGRENT
63 #define endgrent()
64 #endif
65 #ifndef HAVE_ENDPWENT
66 #define endpwent()
67 #endif
69 static boolean parse_amin PARAMS((char *argv[], int *arg_ptr));
70 static boolean parse_and PARAMS((char *argv[], int *arg_ptr));
71 static boolean parse_anewer PARAMS((char *argv[], int *arg_ptr));
72 static boolean parse_atime PARAMS((char *argv[], int *arg_ptr));
73 boolean parse_close PARAMS((char *argv[], int *arg_ptr));
74 static boolean parse_cmin PARAMS((char *argv[], int *arg_ptr));
75 static boolean parse_cnewer PARAMS((char *argv[], int *arg_ptr));
76 static boolean parse_comma PARAMS((char *argv[], int *arg_ptr));
77 static boolean parse_ctime PARAMS((char *argv[], int *arg_ptr));
78 static boolean parse_daystart PARAMS((char *argv[], int *arg_ptr));
79 static boolean parse_delete PARAMS((char *argv[], int *arg_ptr));
80 static boolean parse_d PARAMS((char *argv[], int *arg_ptr));
81 static boolean parse_depth PARAMS((char *argv[], int *arg_ptr));
82 static boolean parse_empty PARAMS((char *argv[], int *arg_ptr));
83 static boolean parse_exec PARAMS((char *argv[], int *arg_ptr));
84 static boolean parse_execdir PARAMS((char *argv[], int *arg_ptr));
85 static boolean parse_false PARAMS((char *argv[], int *arg_ptr));
86 static boolean parse_fls PARAMS((char *argv[], int *arg_ptr));
87 static boolean parse_fprintf PARAMS((char *argv[], int *arg_ptr));
88 static boolean parse_follow PARAMS((char *argv[], int *arg_ptr));
89 static boolean parse_fprint PARAMS((char *argv[], int *arg_ptr));
90 static boolean parse_fprint0 PARAMS((char *argv[], int *arg_ptr));
91 static boolean parse_fstype PARAMS((char *argv[], int *arg_ptr));
92 static boolean parse_gid PARAMS((char *argv[], int *arg_ptr));
93 static boolean parse_group PARAMS((char *argv[], int *arg_ptr));
94 static boolean parse_help PARAMS((char *argv[], int *arg_ptr));
95 static boolean parse_ilname PARAMS((char *argv[], int *arg_ptr));
96 static boolean parse_iname PARAMS((char *argv[], int *arg_ptr));
97 static boolean parse_inum PARAMS((char *argv[], int *arg_ptr));
98 static boolean parse_ipath PARAMS((char *argv[], int *arg_ptr));
99 static boolean parse_iregex PARAMS((char *argv[], int *arg_ptr));
100 static boolean parse_iwholename PARAMS((char *argv[], int *arg_ptr));
101 static boolean parse_links PARAMS((char *argv[], int *arg_ptr));
102 static boolean parse_lname PARAMS((char *argv[], int *arg_ptr));
103 static boolean parse_ls PARAMS((char *argv[], int *arg_ptr));
104 static boolean parse_maxdepth PARAMS((char *argv[], int *arg_ptr));
105 static boolean parse_mindepth PARAMS((char *argv[], int *arg_ptr));
106 static boolean parse_mmin PARAMS((char *argv[], int *arg_ptr));
107 static boolean parse_mtime PARAMS((char *argv[], int *arg_ptr));
108 static boolean parse_name PARAMS((char *argv[], int *arg_ptr));
109 static boolean parse_negate PARAMS((char *argv[], int *arg_ptr));
110 static boolean parse_newer PARAMS((char *argv[], int *arg_ptr));
111 static boolean parse_noleaf PARAMS((char *argv[], int *arg_ptr));
112 static boolean parse_nogroup PARAMS((char *argv[], int *arg_ptr));
113 static boolean parse_nouser PARAMS((char *argv[], int *arg_ptr));
114 static boolean parse_nowarn PARAMS((char *argv[], int *arg_ptr));
115 static boolean parse_ok PARAMS((char *argv[], int *arg_ptr));
116 static boolean parse_okdir PARAMS((char *argv[], int *arg_ptr));
117 boolean parse_open PARAMS((char *argv[], int *arg_ptr));
118 static boolean parse_or PARAMS((char *argv[], int *arg_ptr));
119 static boolean parse_path PARAMS((char *argv[], int *arg_ptr));
120 static boolean parse_perm PARAMS((char *argv[], int *arg_ptr));
121 boolean parse_print PARAMS((char *argv[], int *arg_ptr));
122 static boolean parse_print0 PARAMS((char *argv[], int *arg_ptr));
123 static boolean parse_printf PARAMS((char *argv[], int *arg_ptr));
124 static boolean parse_prune PARAMS((char *argv[], int *arg_ptr));
125 static boolean parse_regex PARAMS((char *argv[], int *arg_ptr));
126 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case));
127 static boolean parse_samefile PARAMS((char *argv[], int *arg_ptr));
128 static boolean parse_size PARAMS((char *argv[], int *arg_ptr));
129 static boolean parse_true PARAMS((char *argv[], int *arg_ptr));
130 static boolean parse_type PARAMS((char *argv[], int *arg_ptr));
131 static boolean parse_uid PARAMS((char *argv[], int *arg_ptr));
132 static boolean parse_used PARAMS((char *argv[], int *arg_ptr));
133 static boolean parse_user PARAMS((char *argv[], int *arg_ptr));
134 static boolean parse_version PARAMS((char *argv[], int *arg_ptr));
135 static boolean parse_wholename PARAMS((char *argv[], int *arg_ptr));
136 static boolean parse_xdev PARAMS((char *argv[], int *arg_ptr));
137 static boolean parse_ignore_race PARAMS((char *argv[], int *arg_ptr));
138 static boolean parse_noignore_race PARAMS((char *argv[], int *arg_ptr));
139 static boolean parse_warn PARAMS((char *argv[], int *arg_ptr));
140 static boolean parse_xtype PARAMS((char *argv[], int *arg_ptr));
141 static boolean parse_quit PARAMS((char *argv[], int *arg_ptr));
143 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case));
144 static boolean insert_type PARAMS((char *argv[], int *arg_ptr, boolean (*which_pred )()));
145 static boolean insert_fprintf PARAMS((FILE *fp, boolean (*func )(), char *argv[], int *arg_ptr));
146 static struct segment **make_segment PARAMS((struct segment **segment, char *format, int len, int kind));
147 static boolean insert_exec_ok PARAMS((const char *action, boolean (*func )(), char *argv[], int *arg_ptr));
148 static boolean get_num_days PARAMS((char *str, uintmax_t *num_days, enum comparison_type *comp_type));
149 static boolean insert_time PARAMS((char *argv[], int *arg_ptr, PFB pred));
150 static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type));
151 static boolean insert_num PARAMS((char *argv[], int *arg_ptr, PFB pred));
152 static FILE *open_output_file PARAMS((char *path));
154 #ifdef DEBUG
155 char *find_pred_name PARAMS((PFB pred_func));
156 #endif /* DEBUG */
160 enum arg_type
162 ARG_OPTION, /* regular options like -maxdepth */
163 ARG_POSITIONAL_OPTION, /* options whose position is important (-follow) */
164 ARG_TEST, /* a like -name */
165 ARG_PUNCTUATION, /* like -o or ( */
166 ARG_ACTION /* like -print */
170 struct parser_table
172 enum arg_type type;
173 char *parser_name;
174 PFB parser_func;
177 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
178 If they are in some Unix versions of find, they are marked `Unix'. */
180 static struct parser_table const parse_table[] =
182 {ARG_PUNCTUATION, "!", parse_negate},
183 {ARG_PUNCTUATION, "not", parse_negate}, /* GNU */
184 {ARG_PUNCTUATION, "(", parse_open},
185 {ARG_PUNCTUATION, ")", parse_close},
186 {ARG_PUNCTUATION, ",", parse_comma}, /* GNU */
187 {ARG_PUNCTUATION, "a", parse_and},
188 {ARG_TEST, "amin", parse_amin}, /* GNU */
189 {ARG_PUNCTUATION, "and", parse_and}, /* GNU */
190 {ARG_TEST, "anewer", parse_anewer}, /* GNU */
191 {ARG_TEST, "atime", parse_atime},
192 {ARG_TEST, "cmin", parse_cmin}, /* GNU */
193 {ARG_TEST, "cnewer", parse_cnewer}, /* GNU */
194 #ifdef UNIMPLEMENTED_UNIX
195 /* It's pretty ugly for find to know about archive formats.
196 Plus what it could do with cpio archives is very limited.
197 Better to leave it out. */
198 {ARG_UNIMPLEMENTED, "cpio", parse_cpio}, /* Unix */
199 #endif
200 {ARG_TEST, "ctime", parse_ctime},
201 {ARG_POSITIONAL_OPTION, "daystart", parse_daystart}, /* GNU */
202 {ARG_ACTION, "delete", parse_delete}, /* GNU, Mac OS, FreeBSD */
203 {ARG_OPTION, "d", parse_d}, /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
204 {ARG_OPTION, "depth", parse_depth},
205 {ARG_TEST, "empty", parse_empty}, /* GNU */
206 {ARG_ACTION, "exec", parse_exec},
207 {ARG_ACTION, "execdir", parse_execdir}, /* *BSD, GNU */
208 {ARG_TEST, "false", parse_false}, /* GNU */
209 {ARG_ACTION, "fls", parse_fls}, /* GNU */
210 {ARG_POSITIONAL_OPTION, "follow", parse_follow}, /* GNU, Unix */
211 {ARG_ACTION, "fprint", parse_fprint}, /* GNU */
212 {ARG_ACTION, "fprint0", parse_fprint0}, /* GNU */
213 {ARG_ACTION, "fprintf", parse_fprintf}, /* GNU */
214 {ARG_TEST, "fstype", parse_fstype}, /* GNU, Unix */
215 {ARG_TEST, "gid", parse_gid}, /* GNU */
216 {ARG_TEST, "group", parse_group},
217 {ARG_TEST, "help", parse_help}, /* GNU */
218 {ARG_TEST, "-help", parse_help}, /* GNU */
219 {ARG_OPTION, "ignore_readdir_race", parse_ignore_race}, /* GNU */
220 {ARG_TEST, "ilname", parse_ilname}, /* GNU */
221 {ARG_TEST, "iname", parse_iname}, /* GNU */
222 {ARG_TEST, "inum", parse_inum}, /* GNU, Unix */
223 {ARG_TEST, "ipath", parse_ipath}, /* GNU, deprecated in favour of iwholename */
224 {ARG_TEST, "iregex", parse_iregex}, /* GNU */
225 {ARG_TEST, "iwholename", parse_iwholename}, /* GNU */
226 {ARG_TEST, "links", parse_links},
227 {ARG_TEST, "lname", parse_lname}, /* GNU */
228 {ARG_ACTION, "ls", parse_ls}, /* GNU, Unix */
229 {ARG_OPTION, "maxdepth", parse_maxdepth}, /* GNU */
230 {ARG_OPTION, "mindepth", parse_mindepth}, /* GNU */
231 {ARG_TEST, "mmin", parse_mmin}, /* GNU */
232 {ARG_OPTION, "mount", parse_xdev}, /* Unix */
233 {ARG_TEST, "mtime", parse_mtime},
234 {ARG_TEST, "name", parse_name},
235 #ifdef UNIMPLEMENTED_UNIX
236 {ARG_UNIMPLEMENTED, "ncpio", parse_ncpio}, /* Unix */
237 #endif
238 {ARG_TEST, "newer", parse_newer},
239 {ARG_OPTION, "noleaf", parse_noleaf}, /* GNU */
240 {ARG_TEST, "nogroup", parse_nogroup},
241 {ARG_TEST, "nouser", parse_nouser},
242 {ARG_OPTION, "noignore_readdir_race", parse_noignore_race},/* GNU */
243 {ARG_OPTION, "nowarn", parse_nowarn}, /* GNU */
244 {ARG_PUNCTUATION, "o", parse_or},
245 {ARG_PUNCTUATION, "or", parse_or}, /* GNU */
246 {ARG_ACTION, "ok", parse_ok},
247 {ARG_ACTION, "okdir", parse_okdir}, /* GNU (-execdir is BSD) */
248 {ARG_TEST, "path", parse_path}, /* GNU, HP-UX, GNU prefers wholename */
249 {ARG_TEST, "perm", parse_perm},
250 {ARG_ACTION, "print", parse_print},
251 {ARG_ACTION, "print0", parse_print0}, /* GNU */
252 {ARG_ACTION, "printf", parse_printf}, /* GNU */
253 {ARG_TEST, "prune", parse_prune},
254 {ARG_ACTION, "quit", parse_quit}, /* GNU */
255 {ARG_TEST, "regex", parse_regex}, /* GNU */
256 {ARG_TEST, "samefile", parse_samefile}, /* GNU */
257 {ARG_TEST, "size", parse_size},
258 {ARG_TEST, "true", parse_true}, /* GNU */
259 {ARG_TEST, "type", parse_type},
260 {ARG_TEST, "uid", parse_uid}, /* GNU */
261 {ARG_TEST, "used", parse_used}, /* GNU */
262 {ARG_TEST, "user", parse_user},
263 {ARG_TEST, "version", parse_version}, /* GNU */
264 {ARG_TEST, "-version", parse_version}, /* GNU */
265 {ARG_OPTION, "warn", parse_warn}, /* GNU */
266 {ARG_TEST, "wholename", parse_wholename}, /* GNU, replaces -path */
267 {ARG_OPTION, "xdev", parse_xdev},
268 {ARG_TEST, "xtype", parse_xtype}, /* GNU */
269 {0, 0, 0}
273 static const char *first_nonoption_arg = NULL;
275 /* Return a pointer to the parser function to invoke for predicate
276 SEARCH_NAME.
277 Return NULL if SEARCH_NAME is not a valid predicate name. */
280 find_parser (char *search_name)
282 int i;
283 const char *original_arg = search_name;
285 if (*search_name == '-')
286 search_name++;
287 for (i = 0; parse_table[i].parser_name != 0; i++)
289 if (strcmp (parse_table[i].parser_name, search_name) == 0)
291 /* If this is an option, but we have already had a
292 * non-option argument, the user may be under the
293 * impression that the behaviour of the option
294 * argument is conditional on some preceding
295 * tests. This might typically be the case with,
296 * for example, -maxdepth.
298 * The options -daystart and -follow are exempt
299 * from this treatment, since their positioning
300 * in the command line does have an effect on
301 * subsequent tests but not previous ones. That
302 * might be intentional on the part of the user.
304 if (parse_table[i].type != ARG_POSITIONAL_OPTION)
306 /* Something other than -follow/-daystart.
307 * If this is an option, check if it followed
308 * a non-option and if so, issue a warning.
310 if (parse_table[i].type == ARG_OPTION)
312 if ((first_nonoption_arg != NULL)
313 && options.warnings )
315 /* option which folows a non-option */
316 error (0, 0,
317 _("warning: you have specified the %s "
318 "option after a non-option argument %s, "
319 "but options are not positional (%s affects "
320 "tests specified before it as well as those "
321 "specified after it). Please specify options "
322 "before other arguments.\n"),
323 original_arg,
324 first_nonoption_arg,
325 original_arg);
328 else
330 /* Not an option or a positional option,
331 * so remember we've seen it in order to
332 * use it in a possible future warning message.
334 if (first_nonoption_arg == NULL)
336 first_nonoption_arg = original_arg;
341 return (parse_table[i].parser_func);
344 return NULL;
347 /* The parsers are responsible to continue scanning ARGV for
348 their arguments. Each parser knows what is and isn't
349 allowed for itself.
351 ARGV is the argument array.
352 *ARG_PTR is the index to start at in ARGV,
353 updated to point beyond the last element consumed.
355 The predicate structure is updated with the new information. */
357 static boolean
358 parse_amin (char **argv, int *arg_ptr)
360 struct predicate *our_pred;
361 uintmax_t num;
362 enum comparison_type c_type;
363 time_t t;
365 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
366 return (false);
367 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
368 return (false);
369 t = options.cur_day_start + DAYSECS - num * 60;
370 our_pred = insert_primary (pred_amin);
371 our_pred->args.info.kind = c_type;
372 our_pred->args.info.negative = t < 0;
373 our_pred->args.info.l_val = t;
374 (*arg_ptr)++;
375 return (true);
378 static boolean
379 parse_and (char **argv, int *arg_ptr)
381 struct predicate *our_pred;
383 (void) argv;
384 (void) arg_ptr;
386 our_pred = get_new_pred ();
387 our_pred->pred_func = pred_and;
388 #ifdef DEBUG
389 our_pred->p_name = find_pred_name (pred_and);
390 #endif /* DEBUG */
391 our_pred->p_type = BI_OP;
392 our_pred->p_prec = AND_PREC;
393 our_pred->need_stat = our_pred->need_type = false;
394 return (true);
397 static boolean
398 parse_anewer (char **argv, int *arg_ptr)
400 struct predicate *our_pred;
401 struct stat stat_newer;
403 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
404 return (false);
405 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
406 error (1, errno, "%s", argv[*arg_ptr]);
407 our_pred = insert_primary (pred_anewer);
408 our_pred->args.time = stat_newer.st_mtime;
409 (*arg_ptr)++;
410 return (true);
413 static boolean
414 parse_atime (char **argv, int *arg_ptr)
416 return (insert_time (argv, arg_ptr, pred_atime));
419 boolean
420 parse_close (char **argv, int *arg_ptr)
422 struct predicate *our_pred;
424 (void) argv;
425 (void) arg_ptr;
427 our_pred = get_new_pred ();
428 our_pred->pred_func = pred_close;
429 #ifdef DEBUG
430 our_pred->p_name = find_pred_name (pred_close);
431 #endif /* DEBUG */
432 our_pred->p_type = CLOSE_PAREN;
433 our_pred->p_prec = NO_PREC;
434 our_pred->need_stat = our_pred->need_type = false;
435 return (true);
438 static boolean
439 parse_cmin (char **argv, int *arg_ptr)
441 struct predicate *our_pred;
442 uintmax_t num;
443 enum comparison_type c_type;
444 time_t t;
446 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
447 return (false);
448 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
449 return (false);
450 t = options.cur_day_start + DAYSECS - num * 60;
451 our_pred = insert_primary (pred_cmin);
452 our_pred->args.info.kind = c_type;
453 our_pred->args.info.negative = t < 0;
454 our_pred->args.info.l_val = t;
455 (*arg_ptr)++;
456 return (true);
459 static boolean
460 parse_cnewer (char **argv, int *arg_ptr)
462 struct predicate *our_pred;
463 struct stat stat_newer;
465 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
466 return (false);
467 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
468 error (1, errno, "%s", argv[*arg_ptr]);
469 our_pred = insert_primary (pred_cnewer);
470 our_pred->args.time = stat_newer.st_mtime;
471 (*arg_ptr)++;
472 return (true);
475 static boolean
476 parse_comma (char **argv, int *arg_ptr)
478 struct predicate *our_pred;
480 (void) argv;
481 (void) arg_ptr;
483 our_pred = get_new_pred ();
484 our_pred->pred_func = pred_comma;
485 #ifdef DEBUG
486 our_pred->p_name = find_pred_name (pred_comma);
487 #endif /* DEBUG */
488 our_pred->p_type = BI_OP;
489 our_pred->p_prec = COMMA_PREC;
490 our_pred->need_stat = our_pred->need_type = false;
491 return (true);
494 static boolean
495 parse_ctime (char **argv, int *arg_ptr)
497 return (insert_time (argv, arg_ptr, pred_ctime));
500 static boolean
501 parse_daystart (char **argv, int *arg_ptr)
503 struct tm *local;
505 (void) argv;
506 (void) arg_ptr;
508 if (options.full_days == false)
510 options.cur_day_start += DAYSECS;
511 local = localtime (&options.cur_day_start);
512 options.cur_day_start -= (local
513 ? (local->tm_sec + local->tm_min * 60
514 + local->tm_hour * 3600)
515 : options.cur_day_start % DAYSECS);
516 options.full_days = true;
518 return true;
521 static boolean
522 parse_delete (argv, arg_ptr)
523 char *argv[];
524 int *arg_ptr;
526 struct predicate *our_pred;
527 (void) argv;
528 (void) arg_ptr;
530 our_pred = insert_primary (pred_delete);
531 our_pred->side_effects = true;
532 our_pred->no_default_print = true;
533 /* -delete implies -depth */
534 options.do_dir_first = false;
535 return (true);
538 static boolean
539 parse_depth (char **argv, int *arg_ptr)
541 (void) argv;
542 (void) arg_ptr;
544 options.do_dir_first = false;
545 return (true);
548 static boolean
549 parse_d (char **argv, int *arg_ptr)
551 (void) argv;
552 (void) arg_ptr;
554 if (options.warnings)
556 error (0, 0,
557 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
559 return parse_depth(argv, arg_ptr);
562 static boolean
563 parse_empty (char **argv, int *arg_ptr)
565 (void) argv;
566 (void) arg_ptr;
568 insert_primary (pred_empty);
569 return (true);
572 static boolean
573 parse_exec (char **argv, int *arg_ptr)
575 return (insert_exec_ok ("-exec", pred_exec, argv, arg_ptr));
578 static boolean
579 parse_execdir (char **argv, int *arg_ptr)
581 return (insert_exec_ok ("-execdir", pred_execdir, argv, arg_ptr));
584 static boolean
585 parse_false (char **argv, int *arg_ptr)
587 struct predicate *our_pred;
589 (void) argv;
590 (void) arg_ptr;
592 our_pred = insert_primary (pred_false);
593 our_pred->need_stat = our_pred->need_type = false;
594 return (true);
597 static boolean
598 parse_fls (char **argv, int *arg_ptr)
600 struct predicate *our_pred;
602 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
603 return (false);
604 our_pred = insert_primary (pred_fls);
605 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
606 our_pred->side_effects = true;
607 our_pred->no_default_print = true;
608 (*arg_ptr)++;
609 return (true);
612 static boolean
613 parse_fprintf (char **argv, int *arg_ptr)
615 FILE *fp;
617 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
618 return (false);
619 if (argv[*arg_ptr + 1] == NULL)
621 /* Ensure we get "missing arg" message, not "invalid arg". */
622 (*arg_ptr)++;
623 return (false);
625 fp = open_output_file (argv[*arg_ptr]);
626 (*arg_ptr)++;
627 return (insert_fprintf (fp, pred_fprintf, argv, arg_ptr));
630 static boolean
631 parse_follow (char **argv, int *arg_ptr)
633 (void) argv;
634 (void) arg_ptr;
636 set_follow_state(SYMLINK_ALWAYS_DEREF);
637 return true;
640 static boolean
641 parse_fprint (char **argv, int *arg_ptr)
643 struct predicate *our_pred;
645 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
646 return (false);
647 our_pred = insert_primary (pred_fprint);
648 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
649 our_pred->side_effects = true;
650 our_pred->no_default_print = true;
651 our_pred->need_stat = our_pred->need_type = false;
652 (*arg_ptr)++;
653 return true;
656 static boolean
657 parse_fprint0 (char **argv, int *arg_ptr)
659 struct predicate *our_pred;
661 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
662 return (false);
663 our_pred = insert_primary (pred_fprint0);
664 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
665 our_pred->side_effects = true;
666 our_pred->no_default_print = true;
667 our_pred->need_stat = our_pred->need_type = false;
668 (*arg_ptr)++;
669 return (true);
672 static boolean
673 parse_fstype (char **argv, int *arg_ptr)
675 struct predicate *our_pred;
677 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
678 return (false);
679 our_pred = insert_primary (pred_fstype);
680 our_pred->args.str = argv[*arg_ptr];
681 (*arg_ptr)++;
682 return (true);
685 static boolean
686 parse_gid (char **argv, int *arg_ptr)
688 return (insert_num (argv, arg_ptr, pred_gid));
691 static boolean
692 parse_group (char **argv, int *arg_ptr)
694 struct group *cur_gr;
695 struct predicate *our_pred;
696 gid_t gid;
697 int gid_len;
699 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
700 return (false);
701 cur_gr = getgrnam (argv[*arg_ptr]);
702 endgrent ();
703 if (cur_gr != NULL)
704 gid = cur_gr->gr_gid;
705 else
707 gid_len = strspn (argv[*arg_ptr], "0123456789");
708 if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0'))
709 return (false);
710 gid = atoi (argv[*arg_ptr]);
712 our_pred = insert_primary (pred_group);
713 our_pred->args.gid = gid;
714 (*arg_ptr)++;
715 return (true);
718 static boolean
719 parse_help (char **argv, int *arg_ptr)
721 (void) argv;
722 (void) arg_ptr;
724 printf (_("\
725 Usage: %s [path...] [expression]\n"), program_name);
726 puts (_("\n\
727 default path is the current directory; default expression is -print\n\
728 expression may consist of: operators, options, tests, and actions:\n"));
729 puts (_("\
730 operators (decreasing precedence; -and is implicit where no others are given):\n\
731 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
732 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
733 puts (_("\
734 positional options (always true): -daystart -follow\n\
735 normal options (always true, specified before other expressions):\n\
736 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
737 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
738 puts (_("\
739 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
740 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
741 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
742 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
743 puts (_("\
744 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
745 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
746 -used N -user NAME -xtype [bcdpfls]\n"));
747 puts (_("\
748 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
749 -fls FILE -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls -delete\n\
750 -quit\n"));
751 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
752 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
753 email to <bug-findutils@gnu.org>."));
754 exit (0);
757 static boolean
758 parse_ilname (char **argv, int *arg_ptr)
760 struct predicate *our_pred;
762 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
763 return (false);
764 our_pred = insert_primary (pred_ilname);
765 our_pred->args.str = argv[*arg_ptr];
766 (*arg_ptr)++;
767 return (true);
771 /* sanity check the fnmatch() function to make sure
772 * it really is the GNU version.
774 static boolean
775 fnmatch_sanitycheck()
777 /* fprintf(stderr, "Performing find sanity check..."); */
778 if (0 != fnmatch("foo", "foo", 0)
779 || 0 == fnmatch("Foo", "foo", 0)
780 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
782 error (1, 0, _("sanity check of the fnmatch() library function failed."));
783 /* fprintf(stderr, "FAILED\n"); */
784 return false;
787 /* fprintf(stderr, "OK\n"); */
788 return true;
793 static boolean
794 parse_iname (char **argv, int *arg_ptr)
796 struct predicate *our_pred;
798 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
799 return (false);
801 fnmatch_sanitycheck();
803 our_pred = insert_primary (pred_iname);
804 our_pred->need_stat = our_pred->need_type = false;
805 our_pred->args.str = argv[*arg_ptr];
806 (*arg_ptr)++;
807 return (true);
810 static boolean
811 parse_inum (char **argv, int *arg_ptr)
813 return (insert_num (argv, arg_ptr, pred_inum));
816 /* -ipath is deprecated (at RMS's request) in favour of
817 * -iwholename. See the node "GNU Manuals" in standards.texi
818 * for the rationale for this (basically, GNU prefers the use
819 * of the phrase "file name" to "path name"
821 static boolean
822 parse_ipath (char **argv, int *arg_ptr)
824 error (0, 0,
825 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
827 return parse_iwholename(argv, arg_ptr);
830 static boolean
831 parse_iwholename (char **argv, int *arg_ptr)
833 struct predicate *our_pred;
835 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
836 return (false);
838 fnmatch_sanitycheck();
840 our_pred = insert_primary (pred_ipath);
841 our_pred->need_stat = our_pred->need_type = false;
842 our_pred->args.str = argv[*arg_ptr];
843 (*arg_ptr)++;
844 return (true);
847 static boolean
848 parse_iregex (char **argv, int *arg_ptr)
850 return insert_regex (argv, arg_ptr, true);
853 static boolean
854 parse_links (char **argv, int *arg_ptr)
856 return (insert_num (argv, arg_ptr, pred_links));
859 static boolean
860 parse_lname (char **argv, int *arg_ptr)
862 struct predicate *our_pred;
864 (void) argv;
865 (void) arg_ptr;
867 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
868 return (false);
870 fnmatch_sanitycheck();
872 our_pred = insert_primary (pred_lname);
873 our_pred->args.str = argv[*arg_ptr];
874 (*arg_ptr)++;
875 return (true);
878 static boolean
879 parse_ls (char **argv, int *arg_ptr)
881 struct predicate *our_pred;
883 (void) &argv;
884 (void) &arg_ptr;
886 our_pred = insert_primary (pred_ls);
887 our_pred->side_effects = true;
888 our_pred->no_default_print = true;
889 return (true);
892 static boolean
893 parse_maxdepth (char **argv, int *arg_ptr)
895 int depth_len;
897 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
898 return false;
899 depth_len = strspn (argv[*arg_ptr], "0123456789");
900 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
901 return false;
902 options.maxdepth = atoi (argv[*arg_ptr]);
903 if (options.maxdepth < 0)
904 return false;
905 (*arg_ptr)++;
906 return true;
909 static boolean
910 parse_mindepth (char **argv, int *arg_ptr)
912 int depth_len;
914 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
915 return false;
916 depth_len = strspn (argv[*arg_ptr], "0123456789");
917 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
918 return false;
919 options.mindepth = atoi (argv[*arg_ptr]);
920 if (options.mindepth < 0)
921 return false;
922 (*arg_ptr)++;
923 return true;
926 static boolean
927 parse_mmin (char **argv, int *arg_ptr)
929 struct predicate *our_pred;
930 uintmax_t num;
931 enum comparison_type c_type;
932 time_t t;
934 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
935 return (false);
936 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
937 return (false);
938 t = options.cur_day_start + DAYSECS - num * 60;
939 our_pred = insert_primary (pred_mmin);
940 our_pred->args.info.kind = c_type;
941 our_pred->args.info.negative = t < 0;
942 our_pred->args.info.l_val = t;
943 (*arg_ptr)++;
944 return (true);
947 static boolean
948 parse_mtime (char **argv, int *arg_ptr)
950 return (insert_time (argv, arg_ptr, pred_mtime));
953 static boolean
954 parse_name (char **argv, int *arg_ptr)
956 struct predicate *our_pred;
958 (void) argv;
959 (void) arg_ptr;
961 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
962 return (false);
963 our_pred = insert_primary (pred_name);
964 our_pred->need_stat = our_pred->need_type = false;
965 our_pred->args.str = argv[*arg_ptr];
966 (*arg_ptr)++;
967 return (true);
970 static boolean
971 parse_negate (char **argv, int *arg_ptr)
973 struct predicate *our_pred;
975 (void) &argv;
976 (void) &arg_ptr;
978 our_pred = get_new_pred_chk_op ();
979 our_pred->pred_func = pred_negate;
980 #ifdef DEBUG
981 our_pred->p_name = find_pred_name (pred_negate);
982 #endif /* DEBUG */
983 our_pred->p_type = UNI_OP;
984 our_pred->p_prec = NEGATE_PREC;
985 our_pred->need_stat = our_pred->need_type = false;
986 return (true);
989 static boolean
990 parse_newer (char **argv, int *arg_ptr)
992 struct predicate *our_pred;
993 struct stat stat_newer;
995 (void) argv;
996 (void) arg_ptr;
998 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
999 return (false);
1000 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
1001 error (1, errno, "%s", argv[*arg_ptr]);
1002 our_pred = insert_primary (pred_newer);
1003 our_pred->args.time = stat_newer.st_mtime;
1004 (*arg_ptr)++;
1005 return (true);
1008 static boolean
1009 parse_noleaf (char **argv, int *arg_ptr)
1011 (void) &argv;
1012 (void) &arg_ptr;
1014 options.no_leaf_check = true;
1015 return true;
1018 #ifdef CACHE_IDS
1019 /* Arbitrary amount by which to increase size
1020 of `uid_unused' and `gid_unused'. */
1021 #define ALLOC_STEP 2048
1023 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1024 char *uid_unused = NULL;
1026 /* Number of elements in `uid_unused'. */
1027 unsigned uid_allocated;
1029 /* Similar for GIDs and group entries. */
1030 char *gid_unused = NULL;
1031 unsigned gid_allocated;
1032 #endif
1034 static boolean
1035 parse_nogroup (char **argv, int *arg_ptr)
1037 struct predicate *our_pred;
1039 (void) &argv;
1040 (void) &arg_ptr;
1042 our_pred = insert_primary (pred_nogroup);
1043 #ifdef CACHE_IDS
1044 if (gid_unused == NULL)
1046 struct group *gr;
1048 gid_allocated = ALLOC_STEP;
1049 gid_unused = xmalloc (gid_allocated);
1050 memset (gid_unused, 1, gid_allocated);
1051 setgrent ();
1052 while ((gr = getgrent ()) != NULL)
1054 if ((unsigned) gr->gr_gid >= gid_allocated)
1056 unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
1057 gid_unused = xrealloc (gid_unused, new_allocated);
1058 memset (gid_unused + gid_allocated, 1,
1059 new_allocated - gid_allocated);
1060 gid_allocated = new_allocated;
1062 gid_unused[(unsigned) gr->gr_gid] = 0;
1064 endgrent ();
1066 #endif
1067 return (true);
1070 static boolean
1071 parse_nouser (char **argv, int *arg_ptr)
1073 struct predicate *our_pred;
1074 (void) argv;
1075 (void) arg_ptr;
1078 our_pred = insert_primary (pred_nouser);
1079 #ifdef CACHE_IDS
1080 if (uid_unused == NULL)
1082 struct passwd *pw;
1084 uid_allocated = ALLOC_STEP;
1085 uid_unused = xmalloc (uid_allocated);
1086 memset (uid_unused, 1, uid_allocated);
1087 setpwent ();
1088 while ((pw = getpwent ()) != NULL)
1090 if ((unsigned) pw->pw_uid >= uid_allocated)
1092 unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
1093 uid_unused = xrealloc (uid_unused, new_allocated);
1094 memset (uid_unused + uid_allocated, 1,
1095 new_allocated - uid_allocated);
1096 uid_allocated = new_allocated;
1098 uid_unused[(unsigned) pw->pw_uid] = 0;
1100 endpwent ();
1102 #endif
1103 return (true);
1106 static boolean
1107 parse_nowarn (char **argv, int *arg_ptr)
1109 (void) argv;
1110 (void) arg_ptr;
1112 options.warnings = false;
1113 return true;;
1116 static boolean
1117 parse_ok (char **argv, int *arg_ptr)
1119 return (insert_exec_ok ("-ok", pred_ok, argv, arg_ptr));
1122 static boolean
1123 parse_okdir (char **argv, int *arg_ptr)
1125 return (insert_exec_ok ("-okdir", pred_okdir, argv, arg_ptr));
1128 boolean
1129 parse_open (char **argv, int *arg_ptr)
1131 struct predicate *our_pred;
1133 (void) argv;
1134 (void) arg_ptr;
1136 our_pred = get_new_pred_chk_op ();
1137 our_pred->pred_func = pred_open;
1138 #ifdef DEBUG
1139 our_pred->p_name = find_pred_name (pred_open);
1140 #endif /* DEBUG */
1141 our_pred->p_type = OPEN_PAREN;
1142 our_pred->p_prec = NO_PREC;
1143 our_pred->need_stat = our_pred->need_type = false;
1144 return (true);
1147 static boolean
1148 parse_or (char **argv, int *arg_ptr)
1150 struct predicate *our_pred;
1152 (void) argv;
1153 (void) arg_ptr;
1155 our_pred = get_new_pred ();
1156 our_pred->pred_func = pred_or;
1157 #ifdef DEBUG
1158 our_pred->p_name = find_pred_name (pred_or);
1159 #endif /* DEBUG */
1160 our_pred->p_type = BI_OP;
1161 our_pred->p_prec = OR_PREC;
1162 our_pred->need_stat = our_pred->need_type = false;
1163 return (true);
1166 /* -path is deprecated (at RMS's request) in favour of
1167 * -iwholename. See the node "GNU Manuals" in standards.texi
1168 * for the rationale for this (basically, GNU prefers the use
1169 * of the phrase "file name" to "path name".
1171 * We do not issue a warning that this usage is deprecated
1172 * since HPUX find supports this predicate also.
1174 static boolean
1175 parse_path (char **argv, int *arg_ptr)
1177 return parse_wholename(argv, arg_ptr);
1180 static boolean
1181 parse_wholename (char **argv, int *arg_ptr)
1183 struct predicate *our_pred;
1185 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1186 return (false);
1187 our_pred = insert_primary (pred_path);
1188 our_pred->need_stat = our_pred->need_type = false;
1189 our_pred->args.str = argv[*arg_ptr];
1190 (*arg_ptr)++;
1191 return (true);
1194 static boolean
1195 parse_perm (char **argv, int *arg_ptr)
1197 mode_t perm_val;
1198 int mode_start = 0;
1199 struct mode_change *change;
1200 struct predicate *our_pred;
1202 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1203 return (false);
1205 switch (argv[*arg_ptr][0])
1207 case '-':
1208 case '+':
1209 mode_start = 1;
1210 break;
1211 default:
1212 /* empty */
1213 break;
1216 change = mode_compile (argv[*arg_ptr] + mode_start, MODE_MASK_PLUS);
1217 if (change == MODE_INVALID)
1218 error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
1219 else if (change == MODE_MEMORY_EXHAUSTED)
1220 error (1, 0, _("virtual memory exhausted"));
1221 perm_val = mode_adjust (0, change);
1222 mode_free (change);
1224 our_pred = insert_primary (pred_perm);
1226 switch (argv[*arg_ptr][0])
1228 case '-':
1229 our_pred->args.perm.kind = PERM_AT_LEAST;
1230 break;
1231 case '+':
1232 our_pred->args.perm.kind = PERM_ANY;
1233 break;
1234 default:
1235 our_pred->args.perm.kind = PERM_EXACT;
1236 break;
1238 our_pred->args.perm.val = perm_val & MODE_ALL;
1239 (*arg_ptr)++;
1240 return (true);
1243 boolean
1244 parse_print (char **argv, int *arg_ptr)
1246 struct predicate *our_pred;
1248 (void) argv;
1249 (void) arg_ptr;
1251 our_pred = insert_primary (pred_print);
1252 /* -print has the side effect of printing. This prevents us
1253 from doing undesired multiple printing when the user has
1254 already specified -print. */
1255 our_pred->side_effects = true;
1256 our_pred->no_default_print = true;
1257 our_pred->need_stat = our_pred->need_type = false;
1258 return (true);
1261 static boolean
1262 parse_print0 (char **argv, int *arg_ptr)
1264 struct predicate *our_pred;
1266 (void) argv;
1267 (void) arg_ptr;
1269 our_pred = insert_primary (pred_print0);
1270 /* -print0 has the side effect of printing. This prevents us
1271 from doing undesired multiple printing when the user has
1272 already specified -print0. */
1273 our_pred->side_effects = true;
1274 our_pred->no_default_print = true;
1275 our_pred->need_stat = our_pred->need_type = false;
1276 return (true);
1279 static boolean
1280 parse_printf (char **argv, int *arg_ptr)
1282 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1283 return (false);
1284 return (insert_fprintf (stdout, pred_fprintf, argv, arg_ptr));
1287 static boolean
1288 parse_prune (char **argv, int *arg_ptr)
1290 struct predicate *our_pred;
1292 (void) argv;
1293 (void) arg_ptr;
1295 our_pred = insert_primary (pred_prune);
1296 our_pred->need_stat = our_pred->need_type = false;
1297 /* -prune has a side effect that it does not descend into
1298 the current directory. */
1299 our_pred->side_effects = true;
1300 return (true);
1303 static boolean
1304 parse_quit (char **argv, int *arg_ptr)
1306 struct predicate *our_pred = insert_primary (pred_quit);
1307 (void) argv;
1308 (void) arg_ptr;
1309 our_pred->need_stat = our_pred->need_type = false;
1310 return true;
1314 static boolean
1315 parse_regex (char **argv, int *arg_ptr)
1317 return insert_regex (argv, arg_ptr, false);
1320 static boolean
1321 insert_regex (char **argv, int *arg_ptr, boolean ignore_case)
1323 struct predicate *our_pred;
1324 struct re_pattern_buffer *re;
1325 const char *error_message;
1327 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1328 return (false);
1329 our_pred = insert_primary (pred_regex);
1330 our_pred->need_stat = our_pred->need_type = false;
1331 re = (struct re_pattern_buffer *)
1332 xmalloc (sizeof (struct re_pattern_buffer));
1333 our_pred->args.regex = re;
1334 re->allocated = 100;
1335 re->buffer = (unsigned char *) xmalloc (re->allocated);
1336 re->fastmap = NULL;
1338 if (ignore_case)
1340 re_syntax_options |= RE_ICASE;
1342 else
1344 re_syntax_options &= ~RE_ICASE;
1346 re->translate = NULL;
1348 error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
1349 re);
1350 if (error_message)
1351 error (1, 0, "%s", error_message);
1352 (*arg_ptr)++;
1353 return (true);
1356 static boolean
1357 parse_size (char **argv, int *arg_ptr)
1359 struct predicate *our_pred;
1360 uintmax_t num;
1361 enum comparison_type c_type;
1362 int blksize = 512;
1363 int len;
1365 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1366 return (false);
1367 len = strlen (argv[*arg_ptr]);
1368 if (len == 0)
1369 error (1, 0, _("invalid null argument to -size"));
1370 switch (argv[*arg_ptr][len - 1])
1372 case 'b':
1373 blksize = 512;
1374 argv[*arg_ptr][len - 1] = '\0';
1375 break;
1377 case 'c':
1378 blksize = 1;
1379 argv[*arg_ptr][len - 1] = '\0';
1380 break;
1382 case 'k':
1383 blksize = 1024;
1384 argv[*arg_ptr][len - 1] = '\0';
1385 break;
1387 case 'M': /* Megabytes */
1388 blksize = 1024*1024;
1389 argv[*arg_ptr][len - 1] = '\0';
1390 break;
1392 case 'G': /* Gigabytes */
1393 blksize = 1024*1024*1024;
1394 argv[*arg_ptr][len - 1] = '\0';
1395 break;
1397 case 'w':
1398 blksize = 2;
1399 argv[*arg_ptr][len - 1] = '\0';
1400 break;
1402 case '0':
1403 case '1':
1404 case '2':
1405 case '3':
1406 case '4':
1407 case '5':
1408 case '6':
1409 case '7':
1410 case '8':
1411 case '9':
1412 break;
1414 default:
1415 error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
1417 if (!get_num (argv[*arg_ptr], &num, &c_type))
1418 return (false);
1419 our_pred = insert_primary (pred_size);
1420 our_pred->args.size.kind = c_type;
1421 our_pred->args.size.blocksize = blksize;
1422 our_pred->args.size.size = num;
1423 (*arg_ptr)++;
1424 return (true);
1428 static boolean
1429 parse_samefile (char **argv, int *arg_ptr)
1431 struct predicate *our_pred;
1432 struct stat st;
1434 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1435 return (false);
1436 if ((*options.xstat) (argv[*arg_ptr], &st))
1437 error (1, errno, "%s", argv[*arg_ptr]);
1439 our_pred = insert_primary (pred_samefile);
1440 our_pred->args.fileid.ino = st.st_ino;
1441 our_pred->args.fileid.dev = st.st_dev;
1442 our_pred->need_type = false;
1443 our_pred->need_stat = true;
1444 (*arg_ptr)++;
1445 return (true);
1449 static boolean
1450 parse_true (char **argv, int *arg_ptr)
1452 struct predicate *our_pred;
1454 (void) argv;
1455 (void) arg_ptr;
1457 our_pred = insert_primary (pred_true);
1458 our_pred->need_stat = our_pred->need_type = false;
1459 return (true);
1462 static boolean
1463 parse_type (char **argv, int *arg_ptr)
1465 return insert_type (argv, arg_ptr, pred_type);
1468 static boolean
1469 parse_uid (char **argv, int *arg_ptr)
1471 return (insert_num (argv, arg_ptr, pred_uid));
1474 static boolean
1475 parse_used (char **argv, int *arg_ptr)
1477 struct predicate *our_pred;
1478 uintmax_t num_days;
1479 enum comparison_type c_type;
1480 time_t t;
1482 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1483 return (false);
1484 if (!get_num (argv[*arg_ptr], &num_days, &c_type))
1485 return (false);
1486 t = num_days * DAYSECS;
1487 our_pred = insert_primary (pred_used);
1488 our_pred->args.info.kind = c_type;
1489 our_pred->args.info.negative = t < 0;
1490 our_pred->args.info.l_val = t;
1491 (*arg_ptr)++;
1492 return (true);
1495 static boolean
1496 parse_user (char **argv, int *arg_ptr)
1498 struct passwd *cur_pwd;
1499 struct predicate *our_pred;
1500 uid_t uid;
1501 int uid_len;
1503 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1504 return (false);
1505 cur_pwd = getpwnam (argv[*arg_ptr]);
1506 endpwent ();
1507 if (cur_pwd != NULL)
1508 uid = cur_pwd->pw_uid;
1509 else
1511 uid_len = strspn (argv[*arg_ptr], "0123456789");
1512 if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
1513 return (false);
1514 uid = atoi (argv[*arg_ptr]);
1516 our_pred = insert_primary (pred_user);
1517 our_pred->args.uid = uid;
1518 (*arg_ptr)++;
1519 return (true);
1522 static boolean
1523 parse_version (char **argv, int *arg_ptr)
1525 extern char *version_string;
1526 int features = 0;
1528 (void) argv;
1529 (void) arg_ptr;
1531 fflush (stderr);
1532 printf (_("GNU find version %s\n"), version_string);
1533 printf (_("Features enabled: "));
1535 #if CACHE_IDS
1536 printf("CACHE_IDS ");
1537 ++features;
1538 #endif
1539 #if DEBUG
1540 printf("DEBUG ");
1541 ++features;
1542 #endif
1543 #if DEBUG_STAT
1544 printf("DEBUG_STAT ");
1545 ++features;
1546 #endif
1547 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1548 printf("D_TYPE ");
1549 ++features;
1550 #endif
1551 #if defined(O_NOFOLLOW)
1552 printf("O_NOFOLLOW(%s) ",
1553 (options.open_nofollow_available ? "enabled" : "disabled"));
1554 ++features;
1555 #endif
1557 if (0 == features)
1559 /* For the moment, leave this as English in case someone wants
1560 to parse these strings. */
1561 printf("none");
1563 printf("\n");
1565 exit (0);
1568 static boolean
1569 parse_xdev (char **argv, int *arg_ptr)
1571 (void) argv;
1572 (void) arg_ptr;
1573 options.stay_on_filesystem = true;
1574 return true;
1577 static boolean
1578 parse_ignore_race (char **argv, int *arg_ptr)
1580 (void) argv;
1581 (void) arg_ptr;
1582 options.ignore_readdir_race = true;
1583 return true;
1586 static boolean
1587 parse_noignore_race (char **argv, int *arg_ptr)
1589 (void) argv;
1590 (void) arg_ptr;
1591 options.ignore_readdir_race = false;
1592 return true;
1595 static boolean
1596 parse_warn (char **argv, int *arg_ptr)
1598 (void) argv;
1599 (void) arg_ptr;
1600 options.warnings = true;
1601 return true;
1604 static boolean
1605 parse_xtype (char **argv, int *arg_ptr)
1607 (void) argv;
1608 (void) arg_ptr;
1609 return insert_type (argv, arg_ptr, pred_xtype);
1612 static boolean
1613 insert_type (char **argv, int *arg_ptr, boolean (*which_pred) (/* ??? */))
1615 mode_t type_cell;
1616 struct predicate *our_pred;
1618 if ((argv == NULL) || (argv[*arg_ptr] == NULL)
1619 || (strlen (argv[*arg_ptr]) != 1))
1620 return (false);
1621 switch (argv[*arg_ptr][0])
1623 case 'b': /* block special */
1624 type_cell = S_IFBLK;
1625 break;
1626 case 'c': /* character special */
1627 type_cell = S_IFCHR;
1628 break;
1629 case 'd': /* directory */
1630 type_cell = S_IFDIR;
1631 break;
1632 case 'f': /* regular file */
1633 type_cell = S_IFREG;
1634 break;
1635 #ifdef S_IFLNK
1636 case 'l': /* symbolic link */
1637 type_cell = S_IFLNK;
1638 break;
1639 #endif
1640 #ifdef S_IFIFO
1641 case 'p': /* pipe */
1642 type_cell = S_IFIFO;
1643 break;
1644 #endif
1645 #ifdef S_IFSOCK
1646 case 's': /* socket */
1647 type_cell = S_IFSOCK;
1648 break;
1649 #endif
1650 #ifdef S_IFDOOR
1651 case 'D': /* Solaris door */
1652 type_cell = S_IFDOOR;
1653 break;
1654 #endif
1655 default: /* None of the above ... nuke 'em. */
1656 return (false);
1658 our_pred = insert_primary (which_pred);
1660 /* Figure out if we will need to stat the file, because if we don't
1661 * need to follow symlinks, we can avoid a stat call by using
1662 * struct dirent.d_type.
1664 if (which_pred == pred_xtype)
1666 our_pred->need_stat = true;
1667 our_pred->need_type = false;
1669 else
1671 our_pred->need_stat = false; /* struct dirent is enough */
1672 our_pred->need_type = true;
1674 our_pred->args.type = type_cell;
1675 (*arg_ptr)++; /* Move on to next argument. */
1676 return (true);
1679 /* If true, we've determined that the current fprintf predicate
1680 uses stat information. */
1681 static boolean fprintf_stat_needed;
1683 static boolean
1684 insert_fprintf (FILE *fp, boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
1686 char *format; /* Beginning of unprocessed format string. */
1687 register char *scan; /* Current address in scanning `format'. */
1688 register char *scan2; /* Address inside of element being scanned. */
1689 struct segment **segmentp; /* Address of current segment. */
1690 struct predicate *our_pred;
1692 format = argv[(*arg_ptr)++];
1694 fprintf_stat_needed = false; /* Might be overridden later. */
1695 our_pred = insert_primary (func);
1696 our_pred->side_effects = true;
1697 our_pred->no_default_print = true;
1698 our_pred->args.printf_vec.stream = fp;
1699 segmentp = &our_pred->args.printf_vec.segment;
1700 *segmentp = NULL;
1702 for (scan = format; *scan; scan++)
1704 if (*scan == '\\')
1706 scan2 = scan + 1;
1707 if (*scan2 >= '0' && *scan2 <= '7')
1709 register int n, i;
1711 for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
1712 i++, scan2++)
1713 n = 8 * n + *scan2 - '0';
1714 scan2--;
1715 *scan = n;
1717 else
1719 switch (*scan2)
1721 case 'a':
1722 *scan = 7;
1723 break;
1724 case 'b':
1725 *scan = '\b';
1726 break;
1727 case 'c':
1728 make_segment (segmentp, format, scan - format, KIND_STOP);
1729 our_pred->need_stat = fprintf_stat_needed;
1730 return (true);
1731 case 'f':
1732 *scan = '\f';
1733 break;
1734 case 'n':
1735 *scan = '\n';
1736 break;
1737 case 'r':
1738 *scan = '\r';
1739 break;
1740 case 't':
1741 *scan = '\t';
1742 break;
1743 case 'v':
1744 *scan = '\v';
1745 break;
1746 case '\\':
1747 /* *scan = '\\'; * it already is */
1748 break;
1749 default:
1750 error (0, 0,
1751 _("warning: unrecognized escape `\\%c'"), *scan2);
1752 scan++;
1753 continue;
1756 segmentp = make_segment (segmentp, format, scan - format + 1,
1757 KIND_PLAIN);
1758 format = scan2 + 1; /* Move past the escape. */
1759 scan = scan2; /* Incremented immediately by `for'. */
1761 else if (*scan == '%')
1763 if (scan[1] == '%')
1765 segmentp = make_segment (segmentp, format, scan - format + 1,
1766 KIND_PLAIN);
1767 scan++;
1768 format = scan + 1;
1769 continue;
1771 /* Scan past flags, width and precision, to verify kind. */
1772 for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
1773 /* Do nothing. */ ;
1774 while (ISDIGIT (*scan2))
1775 scan2++;
1776 if (*scan2 == '.')
1777 for (scan2++; ISDIGIT (*scan2); scan2++)
1778 /* Do nothing. */ ;
1779 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2))
1781 segmentp = make_segment (segmentp, format, scan2 - format,
1782 (int) *scan2);
1783 scan = scan2;
1784 format = scan + 1;
1786 else if (strchr ("ACT", *scan2) && scan2[1])
1788 segmentp = make_segment (segmentp, format, scan2 - format,
1789 *scan2 | (scan2[1] << 8));
1790 scan = scan2 + 1;
1791 format = scan + 1;
1792 continue;
1794 else
1796 /* An unrecognized % escape. Print the char after the %. */
1797 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1798 *scan2);
1799 segmentp = make_segment (segmentp, format, scan - format,
1800 KIND_PLAIN);
1801 format = scan + 1;
1802 continue;
1807 if (scan > format)
1808 make_segment (segmentp, format, scan - format, KIND_PLAIN);
1809 our_pred->need_type = false;
1810 our_pred->need_stat = fprintf_stat_needed;
1811 return (true);
1814 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1815 from the text in FORMAT, which has length LEN.
1816 Return the address of the `next' pointer of the new segment. */
1818 static struct segment **
1819 make_segment (struct segment **segment, char *format, int len, int kind)
1821 char *fmt;
1823 *segment = (struct segment *) xmalloc (sizeof (struct segment));
1825 (*segment)->kind = kind;
1826 (*segment)->next = NULL;
1827 (*segment)->text_len = len;
1829 fmt = (*segment)->text = xmalloc (len + sizeof "d");
1830 strncpy (fmt, format, len);
1831 fmt += len;
1833 switch (kind & 0xff)
1835 case KIND_PLAIN: /* Plain text string, no % conversion. */
1836 case KIND_STOP: /* Terminate argument, no newline. */
1837 break;
1839 case 'a': /* atime in `ctime' format */
1840 case 'A': /* atime in user-specified strftime format */
1841 case 'c': /* ctime in `ctime' format */
1842 case 'C': /* ctime in user-specified strftime format */
1843 case 'F': /* filesystem type */
1844 case 'g': /* group name */
1845 case 'i': /* inode number */
1846 case 'l': /* object of symlink */
1847 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1848 case 's': /* size in bytes */
1849 case 't': /* mtime in `ctime' format */
1850 case 'T': /* mtime in user-specified strftime format */
1851 case 'u': /* user name */
1852 case 'y': /* file type */
1853 case 'Y': /* symlink pointed file type */
1854 fprintf_stat_needed = true;
1855 /* FALLTHROUGH */
1856 case 'f': /* basename of path */
1857 case 'h': /* leading directories part of path */
1858 case 'H': /* ARGV element file was found under */
1859 case 'p': /* pathname */
1860 case 'P': /* pathname with ARGV element stripped */
1861 *fmt++ = 's';
1862 break;
1864 /* Numeric items that one might expect to honour
1865 * #, 0, + flags but which do not.
1867 case 'G': /* GID number */
1868 case 'U': /* UID number */
1869 case 'b': /* size in 512-byte blocks */
1870 case 'D': /* Filesystem device on which the file exits */
1871 case 'k': /* size in 1K blocks */
1872 case 'n': /* number of links */
1873 fprintf_stat_needed = true;
1874 *fmt++ = 's';
1875 break;
1877 /* Numeric items that DO honour #, 0, + flags.
1879 case 'd': /* depth in search tree (0 = ARGV element) */
1880 *fmt++ = 'd';
1881 break;
1883 case 'm': /* mode as octal number (perms only) */
1884 *fmt++ = 'o';
1885 fprintf_stat_needed = true;
1886 break;
1888 *fmt = '\0';
1890 return (&(*segment)->next);
1893 static void
1894 check_path_safety(const char *action)
1896 const char *path = getenv("PATH");
1897 char *s;
1898 s = next_element(path, 1);
1899 while ((s = next_element ((char *) NULL, 1)) != NULL)
1901 if (0 == strcmp(s, "."))
1903 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)"),
1904 action);
1910 /* handles both exec and ok predicate */
1911 #if defined(NEW_EXEC)
1912 /* handles both exec and ok predicate */
1913 static boolean
1914 new_insert_exec_ok (const char *action,
1915 boolean (*func) (/* ??? */),
1916 char **argv, int *arg_ptr)
1918 int start, end; /* Indexes in ARGV of start & end of cmd. */
1919 int i; /* Index into cmd args */
1920 int saw_braces; /* True if previous arg was '{}'. */
1921 boolean allow_plus; /* True if + is a valid terminator */
1922 int brace_count; /* Number of instances of {}. */
1924 struct predicate *our_pred;
1925 struct exec_val *execp; /* Pointer for efficiency. */
1927 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1928 return (false);
1930 our_pred = insert_primary (func);
1931 our_pred->side_effects = true;
1932 our_pred->no_default_print = true;
1933 execp = &our_pred->args.exec_vec;
1935 if ((func != pred_okdir) && (func != pred_ok))
1936 allow_plus = true;
1937 else
1938 allow_plus = false;
1940 if ((func == pred_execdir) || (func == pred_okdir))
1942 options.ignore_readdir_race = false;
1943 check_path_safety(action);
1944 execp->use_current_dir = true;
1946 else
1948 execp->use_current_dir = false;
1951 our_pred->args.exec_vec.multiple = 0;
1953 /* Count the number of args with path replacements, up until the ';'.
1954 * Also figure out if the command is terminated by ";" or by "+".
1956 start = *arg_ptr;
1957 for (end = start, saw_braces=0, brace_count=0;
1958 (argv[end] != NULL)
1959 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
1960 end++)
1962 /* For -exec and -execdir, "{} +" can terminate the command. */
1963 if ( allow_plus
1964 && argv[end][0] == '+' && argv[end][1] == 0
1965 && saw_braces)
1967 our_pred->args.exec_vec.multiple = 1;
1968 break;
1971 saw_braces = 0;
1972 if (strstr (argv[end], "{}"))
1974 saw_braces = 1;
1975 ++brace_count;
1977 if (0 == end && (func == pred_execdir || func == pred_okdir))
1979 /* The POSIX standard says that {} replacement should
1980 * occur even in the utility name. This is insecure
1981 * since it means we will be executing a command whose
1982 * name is chosen according to whatever find finds in
1983 * the filesystem. That can be influenced by an
1984 * attacker. Hence for -execdir and -okdir this is not
1985 * allowed. We can specify this as those options are
1986 * not defined by POSIX.
1988 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
1993 /* Fail if no command given or no semicolon found. */
1994 if ((end == start) || (argv[end] == NULL))
1996 *arg_ptr = end;
1997 free(our_pred);
1998 return false;
2001 if (our_pred->args.exec_vec.multiple && brace_count > 1)
2004 const char *suffix;
2005 if (func == pred_execdir)
2006 suffix = "dir";
2007 else
2008 suffix = "";
2010 error(1, 0,
2011 _("Only one instance of {} is supported with -exec%s ... +"),
2012 suffix);
2015 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2016 bc_init_controlinfo(&execp->ctl);
2017 execp->ctl.exec_callback = launch;
2019 if (our_pred->args.exec_vec.multiple)
2021 /* "+" terminator, so we can just append our arguments after the
2022 * command and initial arguments.
2024 execp->replace_vec = NULL;
2025 execp->ctl.replace_pat = NULL;
2026 execp->ctl.rplen = 0;
2027 execp->ctl.lines_per_exec = 0; /* no limit */
2028 execp->ctl.args_per_exec = 0; /* no limit */
2030 /* remember how many arguments there are */
2031 execp->ctl.initial_argc = (end-start) - 1;
2033 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2034 bc_init_state(&execp->ctl, &execp->state, execp);
2036 /* Gather the initial arguments. Skip the {}. */
2037 for (i=start; i<end-1; ++i)
2039 bc_push_arg(&execp->ctl, &execp->state,
2040 argv[i], strlen(argv[i])+1,
2041 NULL, 0,
2045 else
2047 /* Semicolon terminator - more than one {} is supported, so we
2048 * have to do brace-replacement.
2050 execp->num_args = end - start;
2052 execp->ctl.replace_pat = "{}";
2053 execp->ctl.rplen = strlen(execp->ctl.replace_pat);
2054 execp->ctl.lines_per_exec = 0; /* no limit */
2055 execp->ctl.args_per_exec = 0; /* no limit */
2056 execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
2059 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2060 bc_init_state(&execp->ctl, &execp->state, execp);
2062 /* Remember the (pre-replacement) arguments for later. */
2063 for (i=0; i<execp->num_args; ++i)
2065 execp->replace_vec[i] = argv[i+start];
2069 if (argv[end] == NULL)
2070 *arg_ptr = end;
2071 else
2072 *arg_ptr = end + 1;
2074 return true;
2076 #else
2077 /* handles both exec and ok predicate */
2078 static boolean
2079 old_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
2081 int start, end; /* Indexes in ARGV of start & end of cmd. */
2082 int num_paths; /* Number of args with path replacements. */
2083 int path_pos; /* Index in array of path replacements. */
2084 int vec_pos; /* Index in array of args. */
2085 struct predicate *our_pred;
2086 struct exec_val *execp; /* Pointer for efficiency. */
2088 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2089 return (false);
2091 /* Count the number of args with path replacements, up until the ';'. */
2092 start = *arg_ptr;
2093 for (end = start, num_paths = 0;
2094 (argv[end] != NULL)
2095 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2096 end++)
2097 if (strstr (argv[end], "{}"))
2098 num_paths++;
2099 /* Fail if no command given or no semicolon found. */
2100 if ((end == start) || (argv[end] == NULL))
2102 *arg_ptr = end;
2103 return (false);
2106 our_pred = insert_primary (func);
2107 our_pred->side_effects = true;
2108 our_pred->no_default_print = true;
2109 execp = &our_pred->args.exec_vec;
2110 execp->usercontext = our_pred;
2111 execp->use_current_dir = false;
2112 execp->paths =
2113 (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1));
2114 execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1));
2115 /* Record the positions of all args, and the args with path replacements. */
2116 for (end = start, path_pos = vec_pos = 0;
2117 (argv[end] != NULL)
2118 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2119 end++)
2121 register char *p;
2123 execp->paths[path_pos].count = 0;
2124 for (p = argv[end]; *p; ++p)
2125 if (p[0] == '{' && p[1] == '}')
2127 execp->paths[path_pos].count++;
2128 ++p;
2130 if (execp->paths[path_pos].count)
2132 execp->paths[path_pos].offset = vec_pos;
2133 execp->paths[path_pos].origarg = argv[end];
2134 path_pos++;
2136 execp->vec[vec_pos++] = argv[end];
2138 execp->paths[path_pos].offset = -1;
2139 execp->vec[vec_pos] = NULL;
2141 if (argv[end] == NULL)
2142 *arg_ptr = end;
2143 else
2144 *arg_ptr = end + 1;
2145 return (true);
2147 #endif
2151 static boolean
2152 insert_exec_ok (const char *action,
2153 boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
2155 #if defined(NEW_EXEC)
2156 return new_insert_exec_ok(action, func, argv, arg_ptr);
2157 #else
2158 return old_insert_exec_ok(func, argv, arg_ptr);
2159 #endif
2164 /* Get a number of days and comparison type.
2165 STR is the ASCII representation.
2166 Set *NUM_DAYS to the number of days, taken as being from
2167 the current moment (or possibly midnight). Thus the sense of the
2168 comparison type appears to be reversed.
2169 Set *COMP_TYPE to the kind of comparison that is requested.
2171 Return true if all okay, false if input error.
2173 Used by -atime, -ctime and -mtime (parsers) to
2174 get the appropriate information for a time predicate processor. */
2176 static boolean
2177 get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
2179 boolean r = get_num (str, num_days, comp_type);
2180 if (r)
2181 switch (*comp_type)
2183 case COMP_LT: *comp_type = COMP_GT; break;
2184 case COMP_GT: *comp_type = COMP_LT; break;
2185 default: break;
2187 return r;
2190 /* Insert a time predicate PRED.
2191 ARGV is a pointer to the argument array.
2192 ARG_PTR is a pointer to an index into the array, incremented if
2193 all went well.
2195 Return true if input is valid, false if not.
2197 A new predicate node is assigned, along with an argument node
2198 obtained with malloc.
2200 Used by -atime, -ctime, and -mtime parsers. */
2202 static boolean
2203 insert_time (char **argv, int *arg_ptr, PFB pred)
2205 struct predicate *our_pred;
2206 uintmax_t num_days;
2207 enum comparison_type c_type;
2208 time_t t;
2210 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2211 return (false);
2212 if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
2213 return (false);
2215 /* Figure out the timestamp value we are looking for. */
2216 t = ( options.cur_day_start - num_days * DAYSECS
2217 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2219 if (1)
2221 /* We introduce a scope in which 'val' can be declared, for the
2222 * benefit of compilers that are really C89 compilers
2223 * which support intmax_t because config.h #defines it
2225 intmax_t val = ( (intmax_t)options.cur_day_start - num_days * DAYSECS
2226 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2227 t = val;
2229 /* Check for possibility of an overflow */
2230 if ( (intmax_t)t != val )
2232 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv[*arg_ptr]);
2236 our_pred = insert_primary (pred);
2237 our_pred->args.info.kind = c_type;
2238 our_pred->args.info.negative = t < 0;
2239 our_pred->args.info.l_val = t;
2240 (*arg_ptr)++;
2241 #ifdef DEBUG
2242 fprintf (stderr, _("inserting %s\n"), our_pred->p_name);
2243 fprintf (stderr, _(" type: %s %s "),
2244 (c_type == COMP_GT) ? "gt" :
2245 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2246 (c_type == COMP_GT) ? " >" :
2247 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
2248 t = our_pred->args.info.l_val;
2249 fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2250 if (c_type == COMP_EQ)
2252 t = our_pred->args.info.l_val += DAYSECS;
2253 fprintf (stderr,
2254 " < %ju %s",
2255 (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2256 our_pred->args.info.l_val -= DAYSECS;
2258 #endif /* DEBUG */
2259 return (true);
2262 /* Get a number with comparision information.
2263 The sense of the comparision information is 'normal'; that is,
2264 '+' looks for a count > than the number and '-' less than.
2266 STR is the ASCII representation of the number.
2267 Set *NUM to the number.
2268 Set *COMP_TYPE to the kind of comparison that is requested.
2270 Return true if all okay, false if input error. */
2272 static boolean
2273 get_num (char *str, uintmax_t *num, enum comparison_type *comp_type)
2275 if (str == NULL)
2276 return (false);
2277 switch (str[0])
2279 case '+':
2280 *comp_type = COMP_GT;
2281 str++;
2282 break;
2283 case '-':
2284 *comp_type = COMP_LT;
2285 str++;
2286 break;
2287 default:
2288 *comp_type = COMP_EQ;
2289 break;
2292 return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK;
2295 /* Insert a number predicate.
2296 ARGV is a pointer to the argument array.
2297 *ARG_PTR is an index into ARGV, incremented if all went well.
2298 *PRED is the predicate processor to insert.
2300 Return true if input is valid, false if error.
2302 A new predicate node is assigned, along with an argument node
2303 obtained with malloc.
2305 Used by -inum and -links parsers. */
2307 static boolean
2308 insert_num (char **argv, int *arg_ptr, PFB pred)
2310 struct predicate *our_pred;
2311 uintmax_t num;
2312 enum comparison_type c_type;
2314 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2315 return (false);
2316 if (!get_num (argv[*arg_ptr], &num, &c_type))
2317 return (false);
2318 our_pred = insert_primary (pred);
2319 our_pred->args.info.kind = c_type;
2320 our_pred->args.info.l_val = num;
2321 (*arg_ptr)++;
2322 #ifdef DEBUG
2323 fprintf (stderr, _("inserting %s\n"), our_pred->p_name);
2324 fprintf (stderr, _(" type: %s %s "),
2325 (c_type == COMP_GT) ? "gt" :
2326 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2327 (c_type == COMP_GT) ? " >" :
2328 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
2329 fprintf (stderr, "%ju\n", our_pred->args.info.l_val);
2330 #endif /* DEBUG */
2331 return (true);
2334 static FILE *
2335 open_output_file (char *path)
2337 FILE *f;
2339 if (!strcmp (path, "/dev/stderr"))
2340 return (stderr);
2341 else if (!strcmp (path, "/dev/stdout"))
2342 return (stdout);
2343 f = fopen (path, "w");
2344 if (f == NULL)
2345 error (1, errno, "%s", path);
2346 return (f);