Invoke gl_INCLUDED_REGEX directly to ensure successful compilation on
[findutils.git] / find / parser.c
blob5608a8afc380c3ff880de0763232b971f9ad06e3
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"
34 #if ENABLE_NLS
35 # include <libintl.h>
36 # define _(Text) gettext (Text)
37 #else
38 # define _(Text) Text
39 #endif
40 #ifdef gettext_noop
41 # define N_(String) gettext_noop (String)
42 #else
43 /* See locate.c for explanation as to why not use (String) */
44 # define N_(String) String
45 #endif
47 #if !defined (isascii) || defined (STDC_HEADERS)
48 #ifdef isascii
49 #undef isascii
50 #endif
51 #define isascii(c) 1
52 #endif
54 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
55 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
57 #ifndef HAVE_ENDGRENT
58 #define endgrent()
59 #endif
60 #ifndef HAVE_ENDPWENT
61 #define endpwent()
62 #endif
64 static boolean parse_amin PARAMS((char *argv[], int *arg_ptr));
65 static boolean parse_and PARAMS((char *argv[], int *arg_ptr));
66 static boolean parse_anewer PARAMS((char *argv[], int *arg_ptr));
67 static boolean parse_atime PARAMS((char *argv[], int *arg_ptr));
68 boolean parse_close PARAMS((char *argv[], int *arg_ptr));
69 static boolean parse_cmin PARAMS((char *argv[], int *arg_ptr));
70 static boolean parse_cnewer PARAMS((char *argv[], int *arg_ptr));
71 static boolean parse_comma PARAMS((char *argv[], int *arg_ptr));
72 static boolean parse_ctime PARAMS((char *argv[], int *arg_ptr));
73 static boolean parse_daystart PARAMS((char *argv[], int *arg_ptr));
74 static boolean parse_delete PARAMS((char *argv[], int *arg_ptr));
75 static boolean parse_d PARAMS((char *argv[], int *arg_ptr));
76 static boolean parse_depth PARAMS((char *argv[], int *arg_ptr));
77 static boolean parse_empty PARAMS((char *argv[], int *arg_ptr));
78 static boolean parse_exec PARAMS((char *argv[], int *arg_ptr));
79 static boolean parse_execdir PARAMS((char *argv[], int *arg_ptr));
80 static boolean parse_false PARAMS((char *argv[], int *arg_ptr));
81 static boolean parse_fls PARAMS((char *argv[], int *arg_ptr));
82 static boolean parse_fprintf PARAMS((char *argv[], int *arg_ptr));
83 static boolean parse_follow PARAMS((char *argv[], int *arg_ptr));
84 static boolean parse_fprint PARAMS((char *argv[], int *arg_ptr));
85 static boolean parse_fprint0 PARAMS((char *argv[], int *arg_ptr));
86 static boolean parse_fstype PARAMS((char *argv[], int *arg_ptr));
87 static boolean parse_gid PARAMS((char *argv[], int *arg_ptr));
88 static boolean parse_group PARAMS((char *argv[], int *arg_ptr));
89 static boolean parse_help PARAMS((char *argv[], int *arg_ptr));
90 static boolean parse_ilname PARAMS((char *argv[], int *arg_ptr));
91 static boolean parse_iname PARAMS((char *argv[], int *arg_ptr));
92 static boolean parse_inum PARAMS((char *argv[], int *arg_ptr));
93 static boolean parse_ipath PARAMS((char *argv[], int *arg_ptr));
94 static boolean parse_iregex PARAMS((char *argv[], int *arg_ptr));
95 static boolean parse_iwholename PARAMS((char *argv[], int *arg_ptr));
96 static boolean parse_links PARAMS((char *argv[], int *arg_ptr));
97 static boolean parse_lname PARAMS((char *argv[], int *arg_ptr));
98 static boolean parse_ls PARAMS((char *argv[], int *arg_ptr));
99 static boolean parse_maxdepth PARAMS((char *argv[], int *arg_ptr));
100 static boolean parse_mindepth PARAMS((char *argv[], int *arg_ptr));
101 static boolean parse_mmin PARAMS((char *argv[], int *arg_ptr));
102 static boolean parse_mtime PARAMS((char *argv[], int *arg_ptr));
103 static boolean parse_name PARAMS((char *argv[], int *arg_ptr));
104 static boolean parse_negate PARAMS((char *argv[], int *arg_ptr));
105 static boolean parse_newer PARAMS((char *argv[], int *arg_ptr));
106 static boolean parse_noleaf PARAMS((char *argv[], int *arg_ptr));
107 static boolean parse_nogroup PARAMS((char *argv[], int *arg_ptr));
108 static boolean parse_nouser PARAMS((char *argv[], int *arg_ptr));
109 static boolean parse_nowarn PARAMS((char *argv[], int *arg_ptr));
110 static boolean parse_ok PARAMS((char *argv[], int *arg_ptr));
111 static boolean parse_okdir PARAMS((char *argv[], int *arg_ptr));
112 boolean parse_open PARAMS((char *argv[], int *arg_ptr));
113 static boolean parse_or PARAMS((char *argv[], int *arg_ptr));
114 static boolean parse_path PARAMS((char *argv[], int *arg_ptr));
115 static boolean parse_perm PARAMS((char *argv[], int *arg_ptr));
116 boolean parse_print PARAMS((char *argv[], int *arg_ptr));
117 static boolean parse_print0 PARAMS((char *argv[], int *arg_ptr));
118 static boolean parse_printf PARAMS((char *argv[], int *arg_ptr));
119 static boolean parse_prune PARAMS((char *argv[], int *arg_ptr));
120 static boolean parse_regex PARAMS((char *argv[], int *arg_ptr));
121 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case));
122 static boolean parse_samefile PARAMS((char *argv[], int *arg_ptr));
123 static boolean parse_size PARAMS((char *argv[], int *arg_ptr));
124 static boolean parse_true PARAMS((char *argv[], int *arg_ptr));
125 static boolean parse_type PARAMS((char *argv[], int *arg_ptr));
126 static boolean parse_uid PARAMS((char *argv[], int *arg_ptr));
127 static boolean parse_used PARAMS((char *argv[], int *arg_ptr));
128 static boolean parse_user PARAMS((char *argv[], int *arg_ptr));
129 static boolean parse_version PARAMS((char *argv[], int *arg_ptr));
130 static boolean parse_wholename PARAMS((char *argv[], int *arg_ptr));
131 static boolean parse_xdev PARAMS((char *argv[], int *arg_ptr));
132 static boolean parse_ignore_race PARAMS((char *argv[], int *arg_ptr));
133 static boolean parse_noignore_race PARAMS((char *argv[], int *arg_ptr));
134 static boolean parse_warn PARAMS((char *argv[], int *arg_ptr));
135 static boolean parse_xtype PARAMS((char *argv[], int *arg_ptr));
136 static boolean parse_quit PARAMS((char *argv[], int *arg_ptr));
138 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case));
139 static boolean insert_type PARAMS((char *argv[], int *arg_ptr, boolean (*which_pred )()));
140 static boolean insert_fprintf PARAMS((FILE *fp, boolean (*func )(), char *argv[], int *arg_ptr));
141 static struct segment **make_segment PARAMS((struct segment **segment, char *format, int len, int kind));
142 static boolean insert_exec_ok PARAMS((const char *action, boolean (*func )(), char *argv[], int *arg_ptr));
143 static boolean get_num_days PARAMS((char *str, uintmax_t *num_days, enum comparison_type *comp_type));
144 static boolean insert_time PARAMS((char *argv[], int *arg_ptr, PFB pred));
145 static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type));
146 static boolean insert_num PARAMS((char *argv[], int *arg_ptr, PFB pred));
147 static FILE *open_output_file PARAMS((char *path));
149 #ifdef DEBUG
150 char *find_pred_name PARAMS((PFB pred_func));
151 #endif /* DEBUG */
155 enum arg_type
157 ARG_OPTION, /* regular options like -maxdepth */
158 ARG_POSITIONAL_OPTION, /* options whose position is important (-follow) */
159 ARG_TEST, /* a like -name */
160 ARG_PUNCTUATION, /* like -o or ( */
161 ARG_ACTION /* like -print */
165 struct parser_table
167 enum arg_type type;
168 char *parser_name;
169 PFB parser_func;
172 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
173 If they are in some Unix versions of find, they are marked `Unix'. */
175 static struct parser_table const parse_table[] =
177 {ARG_PUNCTUATION, "!", parse_negate},
178 {ARG_PUNCTUATION, "not", parse_negate}, /* GNU */
179 {ARG_PUNCTUATION, "(", parse_open},
180 {ARG_PUNCTUATION, ")", parse_close},
181 {ARG_PUNCTUATION, ",", parse_comma}, /* GNU */
182 {ARG_PUNCTUATION, "a", parse_and},
183 {ARG_TEST, "amin", parse_amin}, /* GNU */
184 {ARG_PUNCTUATION, "and", parse_and}, /* GNU */
185 {ARG_TEST, "anewer", parse_anewer}, /* GNU */
186 {ARG_TEST, "atime", parse_atime},
187 {ARG_TEST, "cmin", parse_cmin}, /* GNU */
188 {ARG_TEST, "cnewer", parse_cnewer}, /* GNU */
189 #ifdef UNIMPLEMENTED_UNIX
190 /* It's pretty ugly for find to know about archive formats.
191 Plus what it could do with cpio archives is very limited.
192 Better to leave it out. */
193 {ARG_UNIMPLEMENTED, "cpio", parse_cpio}, /* Unix */
194 #endif
195 {ARG_TEST, "ctime", parse_ctime},
196 {ARG_POSITIONAL_OPTION, "daystart", parse_daystart}, /* GNU */
197 {ARG_ACTION, "delete", parse_delete}, /* GNU, Mac OS, FreeBSD */
198 {ARG_OPTION, "d", parse_d}, /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
199 {ARG_OPTION, "depth", parse_depth},
200 {ARG_TEST, "empty", parse_empty}, /* GNU */
201 {ARG_ACTION, "exec", parse_exec},
202 {ARG_ACTION, "execdir", parse_execdir}, /* *BSD, GNU */
203 {ARG_TEST, "false", parse_false}, /* GNU */
204 {ARG_ACTION, "fls", parse_fls}, /* GNU */
205 {ARG_POSITIONAL_OPTION, "follow", parse_follow}, /* GNU, Unix */
206 {ARG_ACTION, "fprint", parse_fprint}, /* GNU */
207 {ARG_ACTION, "fprint0", parse_fprint0}, /* GNU */
208 {ARG_ACTION, "fprintf", parse_fprintf}, /* GNU */
209 {ARG_TEST, "fstype", parse_fstype}, /* GNU, Unix */
210 {ARG_TEST, "gid", parse_gid}, /* GNU */
211 {ARG_TEST, "group", parse_group},
212 {ARG_TEST, "help", parse_help}, /* GNU */
213 {ARG_TEST, "-help", parse_help}, /* GNU */
214 {ARG_OPTION, "ignore_readdir_race", parse_ignore_race}, /* GNU */
215 {ARG_TEST, "ilname", parse_ilname}, /* GNU */
216 {ARG_TEST, "iname", parse_iname}, /* GNU */
217 {ARG_TEST, "inum", parse_inum}, /* GNU, Unix */
218 {ARG_TEST, "ipath", parse_ipath}, /* GNU, deprecated in favour of iwholename */
219 {ARG_TEST, "iregex", parse_iregex}, /* GNU */
220 {ARG_TEST, "iwholename", parse_iwholename}, /* GNU */
221 {ARG_TEST, "links", parse_links},
222 {ARG_TEST, "lname", parse_lname}, /* GNU */
223 {ARG_ACTION, "ls", parse_ls}, /* GNU, Unix */
224 {ARG_OPTION, "maxdepth", parse_maxdepth}, /* GNU */
225 {ARG_OPTION, "mindepth", parse_mindepth}, /* GNU */
226 {ARG_TEST, "mmin", parse_mmin}, /* GNU */
227 {ARG_OPTION, "mount", parse_xdev}, /* Unix */
228 {ARG_TEST, "mtime", parse_mtime},
229 {ARG_TEST, "name", parse_name},
230 #ifdef UNIMPLEMENTED_UNIX
231 {ARG_UNIMPLEMENTED, "ncpio", parse_ncpio}, /* Unix */
232 #endif
233 {ARG_TEST, "newer", parse_newer},
234 {ARG_OPTION, "noleaf", parse_noleaf}, /* GNU */
235 {ARG_TEST, "nogroup", parse_nogroup},
236 {ARG_TEST, "nouser", parse_nouser},
237 {ARG_OPTION, "noignore_readdir_race", parse_noignore_race},/* GNU */
238 {ARG_OPTION, "nowarn", parse_nowarn}, /* GNU */
239 {ARG_PUNCTUATION, "o", parse_or},
240 {ARG_PUNCTUATION, "or", parse_or}, /* GNU */
241 {ARG_ACTION, "ok", parse_ok},
242 {ARG_ACTION, "okdir", parse_okdir}, /* GNU (-execdir is BSD) */
243 {ARG_TEST, "path", parse_path}, /* GNU, HP-UX, GNU prefers wholename */
244 {ARG_TEST, "perm", parse_perm},
245 {ARG_ACTION, "print", parse_print},
246 {ARG_ACTION, "print0", parse_print0}, /* GNU */
247 {ARG_ACTION, "printf", parse_printf}, /* GNU */
248 {ARG_TEST, "prune", parse_prune},
249 {ARG_ACTION, "quit", parse_quit}, /* GNU */
250 {ARG_TEST, "regex", parse_regex}, /* GNU */
251 {ARG_TEST, "samefile", parse_samefile}, /* GNU */
252 {ARG_TEST, "size", parse_size},
253 {ARG_TEST, "true", parse_true}, /* GNU */
254 {ARG_TEST, "type", parse_type},
255 {ARG_TEST, "uid", parse_uid}, /* GNU */
256 {ARG_TEST, "used", parse_used}, /* GNU */
257 {ARG_TEST, "user", parse_user},
258 {ARG_TEST, "version", parse_version}, /* GNU */
259 {ARG_TEST, "-version", parse_version}, /* GNU */
260 {ARG_OPTION, "warn", parse_warn}, /* GNU */
261 {ARG_TEST, "wholename", parse_wholename}, /* GNU, replaces -path */
262 {ARG_OPTION, "xdev", parse_xdev},
263 {ARG_TEST, "xtype", parse_xtype}, /* GNU */
264 {0, 0, 0}
268 static const char *first_nonoption_arg = NULL;
270 /* Return a pointer to the parser function to invoke for predicate
271 SEARCH_NAME.
272 Return NULL if SEARCH_NAME is not a valid predicate name. */
275 find_parser (char *search_name)
277 int i;
278 const char *original_arg = search_name;
280 if (*search_name == '-')
281 search_name++;
282 for (i = 0; parse_table[i].parser_name != 0; i++)
284 if (strcmp (parse_table[i].parser_name, search_name) == 0)
286 /* If this is an option, but we have already had a
287 * non-option argument, the user may be under the
288 * impression that the behaviour of the option
289 * argument is conditional on some preceding
290 * tests. This might typically be the case with,
291 * for example, -maxdepth.
293 * The options -daystart and -follow are exempt
294 * from this treatment, since their positioning
295 * in the command line does have an effect on
296 * subsequent tests but not previous ones. That
297 * might be intentional on the part of the user.
299 if (parse_table[i].type != ARG_POSITIONAL_OPTION)
301 /* Something other than -follow/-daystart.
302 * If this is an option, check if it followed
303 * a non-option and if so, issue a warning.
305 if (parse_table[i].type == ARG_OPTION)
307 if ((first_nonoption_arg != NULL)
308 && options.warnings )
310 /* option which folows a non-option */
311 error (0, 0,
312 _("warning: you have specified the %s "
313 "option after a non-option argument %s, "
314 "but options are not positional (%s affects "
315 "tests specified before it as well as those "
316 "specified after it). Please specify options "
317 "before other arguments.\n"),
318 original_arg,
319 first_nonoption_arg,
320 original_arg);
323 else
325 /* Not an option or a positional option,
326 * so remember we've seen it in order to
327 * use it in a possible future warning message.
329 if (first_nonoption_arg == NULL)
331 first_nonoption_arg = original_arg;
336 return (parse_table[i].parser_func);
339 return NULL;
342 /* The parsers are responsible to continue scanning ARGV for
343 their arguments. Each parser knows what is and isn't
344 allowed for itself.
346 ARGV is the argument array.
347 *ARG_PTR is the index to start at in ARGV,
348 updated to point beyond the last element consumed.
350 The predicate structure is updated with the new information. */
352 static boolean
353 parse_amin (char **argv, int *arg_ptr)
355 struct predicate *our_pred;
356 uintmax_t num;
357 enum comparison_type c_type;
358 time_t t;
360 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
361 return (false);
362 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
363 return (false);
364 t = options.cur_day_start + DAYSECS - num * 60;
365 our_pred = insert_primary (pred_amin);
366 our_pred->args.info.kind = c_type;
367 our_pred->args.info.negative = t < 0;
368 our_pred->args.info.l_val = t;
369 (*arg_ptr)++;
370 return (true);
373 static boolean
374 parse_and (char **argv, int *arg_ptr)
376 struct predicate *our_pred;
378 (void) argv;
379 (void) arg_ptr;
381 our_pred = get_new_pred ();
382 our_pred->pred_func = pred_and;
383 #ifdef DEBUG
384 our_pred->p_name = find_pred_name (pred_and);
385 #endif /* DEBUG */
386 our_pred->p_type = BI_OP;
387 our_pred->p_prec = AND_PREC;
388 our_pred->need_stat = our_pred->need_type = false;
389 return (true);
392 static boolean
393 parse_anewer (char **argv, int *arg_ptr)
395 struct predicate *our_pred;
396 struct stat stat_newer;
398 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
399 return (false);
400 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
401 error (1, errno, "%s", argv[*arg_ptr]);
402 our_pred = insert_primary (pred_anewer);
403 our_pred->args.time = stat_newer.st_mtime;
404 (*arg_ptr)++;
405 return (true);
408 static boolean
409 parse_atime (char **argv, int *arg_ptr)
411 return (insert_time (argv, arg_ptr, pred_atime));
414 boolean
415 parse_close (char **argv, int *arg_ptr)
417 struct predicate *our_pred;
419 (void) argv;
420 (void) arg_ptr;
422 our_pred = get_new_pred ();
423 our_pred->pred_func = pred_close;
424 #ifdef DEBUG
425 our_pred->p_name = find_pred_name (pred_close);
426 #endif /* DEBUG */
427 our_pred->p_type = CLOSE_PAREN;
428 our_pred->p_prec = NO_PREC;
429 our_pred->need_stat = our_pred->need_type = false;
430 return (true);
433 static boolean
434 parse_cmin (char **argv, int *arg_ptr)
436 struct predicate *our_pred;
437 uintmax_t num;
438 enum comparison_type c_type;
439 time_t t;
441 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
442 return (false);
443 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
444 return (false);
445 t = options.cur_day_start + DAYSECS - num * 60;
446 our_pred = insert_primary (pred_cmin);
447 our_pred->args.info.kind = c_type;
448 our_pred->args.info.negative = t < 0;
449 our_pred->args.info.l_val = t;
450 (*arg_ptr)++;
451 return (true);
454 static boolean
455 parse_cnewer (char **argv, int *arg_ptr)
457 struct predicate *our_pred;
458 struct stat stat_newer;
460 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
461 return (false);
462 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
463 error (1, errno, "%s", argv[*arg_ptr]);
464 our_pred = insert_primary (pred_cnewer);
465 our_pred->args.time = stat_newer.st_mtime;
466 (*arg_ptr)++;
467 return (true);
470 static boolean
471 parse_comma (char **argv, int *arg_ptr)
473 struct predicate *our_pred;
475 (void) argv;
476 (void) arg_ptr;
478 our_pred = get_new_pred ();
479 our_pred->pred_func = pred_comma;
480 #ifdef DEBUG
481 our_pred->p_name = find_pred_name (pred_comma);
482 #endif /* DEBUG */
483 our_pred->p_type = BI_OP;
484 our_pred->p_prec = COMMA_PREC;
485 our_pred->need_stat = our_pred->need_type = false;
486 return (true);
489 static boolean
490 parse_ctime (char **argv, int *arg_ptr)
492 return (insert_time (argv, arg_ptr, pred_ctime));
495 static boolean
496 parse_daystart (char **argv, int *arg_ptr)
498 struct tm *local;
500 (void) argv;
501 (void) arg_ptr;
503 if (options.full_days == false)
505 options.cur_day_start += DAYSECS;
506 local = localtime (&options.cur_day_start);
507 options.cur_day_start -= (local
508 ? (local->tm_sec + local->tm_min * 60
509 + local->tm_hour * 3600)
510 : options.cur_day_start % DAYSECS);
511 options.full_days = true;
513 return true;
516 static boolean
517 parse_delete (argv, arg_ptr)
518 char *argv[];
519 int *arg_ptr;
521 struct predicate *our_pred;
522 (void) argv;
523 (void) arg_ptr;
525 our_pred = insert_primary (pred_delete);
526 our_pred->side_effects = true;
527 our_pred->no_default_print = true;
528 /* -delete implies -depth */
529 options.do_dir_first = false;
530 return (true);
533 static boolean
534 parse_depth (char **argv, int *arg_ptr)
536 (void) argv;
537 (void) arg_ptr;
539 options.do_dir_first = false;
540 return (true);
543 static boolean
544 parse_d (char **argv, int *arg_ptr)
546 (void) argv;
547 (void) arg_ptr;
549 if (options.warnings)
551 error (0, 0,
552 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
554 return parse_depth(argv, arg_ptr);
557 static boolean
558 parse_empty (char **argv, int *arg_ptr)
560 (void) argv;
561 (void) arg_ptr;
563 insert_primary (pred_empty);
564 return (true);
567 static boolean
568 parse_exec (char **argv, int *arg_ptr)
570 return (insert_exec_ok ("-exec", pred_exec, argv, arg_ptr));
573 static boolean
574 parse_execdir (char **argv, int *arg_ptr)
576 return (insert_exec_ok ("-execdir", pred_execdir, argv, arg_ptr));
579 static boolean
580 parse_false (char **argv, int *arg_ptr)
582 struct predicate *our_pred;
584 (void) argv;
585 (void) arg_ptr;
587 our_pred = insert_primary (pred_false);
588 our_pred->need_stat = our_pred->need_type = false;
589 return (true);
592 static boolean
593 parse_fls (char **argv, int *arg_ptr)
595 struct predicate *our_pred;
597 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
598 return (false);
599 our_pred = insert_primary (pred_fls);
600 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
601 our_pred->side_effects = true;
602 our_pred->no_default_print = true;
603 (*arg_ptr)++;
604 return (true);
607 static boolean
608 parse_fprintf (char **argv, int *arg_ptr)
610 FILE *fp;
612 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
613 return (false);
614 if (argv[*arg_ptr + 1] == NULL)
616 /* Ensure we get "missing arg" message, not "invalid arg". */
617 (*arg_ptr)++;
618 return (false);
620 fp = open_output_file (argv[*arg_ptr]);
621 (*arg_ptr)++;
622 return (insert_fprintf (fp, pred_fprintf, argv, arg_ptr));
625 static boolean
626 parse_follow (char **argv, int *arg_ptr)
628 (void) argv;
629 (void) arg_ptr;
631 set_follow_state(SYMLINK_ALWAYS_DEREF);
632 return true;
635 static boolean
636 parse_fprint (char **argv, int *arg_ptr)
638 struct predicate *our_pred;
640 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
641 return (false);
642 our_pred = insert_primary (pred_fprint);
643 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
644 our_pred->side_effects = true;
645 our_pred->no_default_print = true;
646 our_pred->need_stat = our_pred->need_type = false;
647 (*arg_ptr)++;
648 return true;
651 static boolean
652 parse_fprint0 (char **argv, int *arg_ptr)
654 struct predicate *our_pred;
656 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
657 return (false);
658 our_pred = insert_primary (pred_fprint0);
659 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
660 our_pred->side_effects = true;
661 our_pred->no_default_print = true;
662 our_pred->need_stat = our_pred->need_type = false;
663 (*arg_ptr)++;
664 return (true);
667 static boolean
668 parse_fstype (char **argv, int *arg_ptr)
670 struct predicate *our_pred;
672 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
673 return (false);
674 our_pred = insert_primary (pred_fstype);
675 our_pred->args.str = argv[*arg_ptr];
676 (*arg_ptr)++;
677 return (true);
680 static boolean
681 parse_gid (char **argv, int *arg_ptr)
683 return (insert_num (argv, arg_ptr, pred_gid));
686 static boolean
687 parse_group (char **argv, int *arg_ptr)
689 struct group *cur_gr;
690 struct predicate *our_pred;
691 gid_t gid;
692 int gid_len;
694 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
695 return (false);
696 cur_gr = getgrnam (argv[*arg_ptr]);
697 endgrent ();
698 if (cur_gr != NULL)
699 gid = cur_gr->gr_gid;
700 else
702 gid_len = strspn (argv[*arg_ptr], "0123456789");
703 if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0'))
704 return (false);
705 gid = atoi (argv[*arg_ptr]);
707 our_pred = insert_primary (pred_group);
708 our_pred->args.gid = gid;
709 (*arg_ptr)++;
710 return (true);
713 static boolean
714 parse_help (char **argv, int *arg_ptr)
716 (void) argv;
717 (void) arg_ptr;
719 printf (_("\
720 Usage: %s [path...] [expression]\n"), program_name);
721 puts (_("\n\
722 default path is the current directory; default expression is -print\n\
723 expression may consist of: operators, options, tests, and actions:\n"));
724 puts (_("\
725 operators (decreasing precedence; -and is implicit where no others are given):\n\
726 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
727 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
728 puts (_("\
729 positional options (always true): -daystart -follow\n\
730 normal options (always true, specified before other expressions):\n\
731 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
732 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
733 puts (_("\
734 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
735 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
736 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
737 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
738 puts (_("\
739 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
740 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
741 -used N -user NAME -xtype [bcdpfls]\n"));
742 puts (_("\
743 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
744 -fls FILE -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls -delete\n\
745 -quit\n"));
746 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
747 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
748 email to <bug-findutils@gnu.org>."));
749 exit (0);
752 static boolean
753 parse_ilname (char **argv, int *arg_ptr)
755 struct predicate *our_pred;
757 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
758 return (false);
759 our_pred = insert_primary (pred_ilname);
760 our_pred->args.str = argv[*arg_ptr];
761 (*arg_ptr)++;
762 return (true);
766 /* sanity check the fnmatch() function to make sure
767 * it really is the GNU version.
769 static boolean
770 fnmatch_sanitycheck()
772 /* fprintf(stderr, "Performing find sanity check..."); */
773 if (0 != fnmatch("foo", "foo", 0)
774 || 0 == fnmatch("Foo", "foo", 0)
775 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
777 error (1, 0, _("sanity check of the fnmatch() library function failed."));
778 /* fprintf(stderr, "FAILED\n"); */
779 return false;
782 /* fprintf(stderr, "OK\n"); */
783 return true;
788 static boolean
789 parse_iname (char **argv, int *arg_ptr)
791 struct predicate *our_pred;
793 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
794 return (false);
796 fnmatch_sanitycheck();
798 our_pred = insert_primary (pred_iname);
799 our_pred->need_stat = our_pred->need_type = false;
800 our_pred->args.str = argv[*arg_ptr];
801 (*arg_ptr)++;
802 return (true);
805 static boolean
806 parse_inum (char **argv, int *arg_ptr)
808 return (insert_num (argv, arg_ptr, pred_inum));
811 /* -ipath is deprecated (at RMS's request) in favour of
812 * -iwholename. See the node "GNU Manuals" in standards.texi
813 * for the rationale for this (basically, GNU prefers the use
814 * of the phrase "file name" to "path name"
816 static boolean
817 parse_ipath (char **argv, int *arg_ptr)
819 error (0, 0,
820 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
822 return parse_iwholename(argv, arg_ptr);
825 static boolean
826 parse_iwholename (char **argv, int *arg_ptr)
828 struct predicate *our_pred;
830 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
831 return (false);
833 fnmatch_sanitycheck();
835 our_pred = insert_primary (pred_ipath);
836 our_pred->need_stat = our_pred->need_type = false;
837 our_pred->args.str = argv[*arg_ptr];
838 (*arg_ptr)++;
839 return (true);
842 static boolean
843 parse_iregex (char **argv, int *arg_ptr)
845 return insert_regex (argv, arg_ptr, true);
848 static boolean
849 parse_links (char **argv, int *arg_ptr)
851 return (insert_num (argv, arg_ptr, pred_links));
854 static boolean
855 parse_lname (char **argv, int *arg_ptr)
857 struct predicate *our_pred;
859 (void) argv;
860 (void) arg_ptr;
862 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
863 return (false);
865 fnmatch_sanitycheck();
867 our_pred = insert_primary (pred_lname);
868 our_pred->args.str = argv[*arg_ptr];
869 (*arg_ptr)++;
870 return (true);
873 static boolean
874 parse_ls (char **argv, int *arg_ptr)
876 struct predicate *our_pred;
878 (void) &argv;
879 (void) &arg_ptr;
881 our_pred = insert_primary (pred_ls);
882 our_pred->side_effects = true;
883 our_pred->no_default_print = true;
884 return (true);
887 static boolean
888 parse_maxdepth (char **argv, int *arg_ptr)
890 int depth_len;
892 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
893 return false;
894 depth_len = strspn (argv[*arg_ptr], "0123456789");
895 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
896 return false;
897 options.maxdepth = atoi (argv[*arg_ptr]);
898 if (options.maxdepth < 0)
899 return false;
900 (*arg_ptr)++;
901 return true;
904 static boolean
905 parse_mindepth (char **argv, int *arg_ptr)
907 int depth_len;
909 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
910 return false;
911 depth_len = strspn (argv[*arg_ptr], "0123456789");
912 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
913 return false;
914 options.mindepth = atoi (argv[*arg_ptr]);
915 if (options.mindepth < 0)
916 return false;
917 (*arg_ptr)++;
918 return true;
921 static boolean
922 parse_mmin (char **argv, int *arg_ptr)
924 struct predicate *our_pred;
925 uintmax_t num;
926 enum comparison_type c_type;
927 time_t t;
929 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
930 return (false);
931 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
932 return (false);
933 t = options.cur_day_start + DAYSECS - num * 60;
934 our_pred = insert_primary (pred_mmin);
935 our_pred->args.info.kind = c_type;
936 our_pred->args.info.negative = t < 0;
937 our_pred->args.info.l_val = t;
938 (*arg_ptr)++;
939 return (true);
942 static boolean
943 parse_mtime (char **argv, int *arg_ptr)
945 return (insert_time (argv, arg_ptr, pred_mtime));
948 static boolean
949 parse_name (char **argv, int *arg_ptr)
951 struct predicate *our_pred;
953 (void) argv;
954 (void) arg_ptr;
956 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
957 return (false);
958 our_pred = insert_primary (pred_name);
959 our_pred->need_stat = our_pred->need_type = false;
960 our_pred->args.str = argv[*arg_ptr];
961 (*arg_ptr)++;
962 return (true);
965 static boolean
966 parse_negate (char **argv, int *arg_ptr)
968 struct predicate *our_pred;
970 (void) &argv;
971 (void) &arg_ptr;
973 our_pred = get_new_pred_chk_op ();
974 our_pred->pred_func = pred_negate;
975 #ifdef DEBUG
976 our_pred->p_name = find_pred_name (pred_negate);
977 #endif /* DEBUG */
978 our_pred->p_type = UNI_OP;
979 our_pred->p_prec = NEGATE_PREC;
980 our_pred->need_stat = our_pred->need_type = false;
981 return (true);
984 static boolean
985 parse_newer (char **argv, int *arg_ptr)
987 struct predicate *our_pred;
988 struct stat stat_newer;
990 (void) argv;
991 (void) arg_ptr;
993 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
994 return (false);
995 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
996 error (1, errno, "%s", argv[*arg_ptr]);
997 our_pred = insert_primary (pred_newer);
998 our_pred->args.time = stat_newer.st_mtime;
999 (*arg_ptr)++;
1000 return (true);
1003 static boolean
1004 parse_noleaf (char **argv, int *arg_ptr)
1006 (void) &argv;
1007 (void) &arg_ptr;
1009 options.no_leaf_check = true;
1010 return true;
1013 #ifdef CACHE_IDS
1014 /* Arbitrary amount by which to increase size
1015 of `uid_unused' and `gid_unused'. */
1016 #define ALLOC_STEP 2048
1018 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1019 char *uid_unused = NULL;
1021 /* Number of elements in `uid_unused'. */
1022 unsigned uid_allocated;
1024 /* Similar for GIDs and group entries. */
1025 char *gid_unused = NULL;
1026 unsigned gid_allocated;
1027 #endif
1029 static boolean
1030 parse_nogroup (char **argv, int *arg_ptr)
1032 struct predicate *our_pred;
1034 (void) &argv;
1035 (void) &arg_ptr;
1037 our_pred = insert_primary (pred_nogroup);
1038 #ifdef CACHE_IDS
1039 if (gid_unused == NULL)
1041 struct group *gr;
1043 gid_allocated = ALLOC_STEP;
1044 gid_unused = xmalloc (gid_allocated);
1045 memset (gid_unused, 1, gid_allocated);
1046 setgrent ();
1047 while ((gr = getgrent ()) != NULL)
1049 if ((unsigned) gr->gr_gid >= gid_allocated)
1051 unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
1052 gid_unused = xrealloc (gid_unused, new_allocated);
1053 memset (gid_unused + gid_allocated, 1,
1054 new_allocated - gid_allocated);
1055 gid_allocated = new_allocated;
1057 gid_unused[(unsigned) gr->gr_gid] = 0;
1059 endgrent ();
1061 #endif
1062 return (true);
1065 static boolean
1066 parse_nouser (char **argv, int *arg_ptr)
1068 struct predicate *our_pred;
1069 (void) argv;
1070 (void) arg_ptr;
1073 our_pred = insert_primary (pred_nouser);
1074 #ifdef CACHE_IDS
1075 if (uid_unused == NULL)
1077 struct passwd *pw;
1079 uid_allocated = ALLOC_STEP;
1080 uid_unused = xmalloc (uid_allocated);
1081 memset (uid_unused, 1, uid_allocated);
1082 setpwent ();
1083 while ((pw = getpwent ()) != NULL)
1085 if ((unsigned) pw->pw_uid >= uid_allocated)
1087 unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
1088 uid_unused = xrealloc (uid_unused, new_allocated);
1089 memset (uid_unused + uid_allocated, 1,
1090 new_allocated - uid_allocated);
1091 uid_allocated = new_allocated;
1093 uid_unused[(unsigned) pw->pw_uid] = 0;
1095 endpwent ();
1097 #endif
1098 return (true);
1101 static boolean
1102 parse_nowarn (char **argv, int *arg_ptr)
1104 (void) argv;
1105 (void) arg_ptr;
1107 options.warnings = false;
1108 return true;;
1111 static boolean
1112 parse_ok (char **argv, int *arg_ptr)
1114 return (insert_exec_ok ("-ok", pred_ok, argv, arg_ptr));
1117 static boolean
1118 parse_okdir (char **argv, int *arg_ptr)
1120 return (insert_exec_ok ("-okdir", pred_okdir, argv, arg_ptr));
1123 boolean
1124 parse_open (char **argv, int *arg_ptr)
1126 struct predicate *our_pred;
1128 (void) argv;
1129 (void) arg_ptr;
1131 our_pred = get_new_pred_chk_op ();
1132 our_pred->pred_func = pred_open;
1133 #ifdef DEBUG
1134 our_pred->p_name = find_pred_name (pred_open);
1135 #endif /* DEBUG */
1136 our_pred->p_type = OPEN_PAREN;
1137 our_pred->p_prec = NO_PREC;
1138 our_pred->need_stat = our_pred->need_type = false;
1139 return (true);
1142 static boolean
1143 parse_or (char **argv, int *arg_ptr)
1145 struct predicate *our_pred;
1147 (void) argv;
1148 (void) arg_ptr;
1150 our_pred = get_new_pred ();
1151 our_pred->pred_func = pred_or;
1152 #ifdef DEBUG
1153 our_pred->p_name = find_pred_name (pred_or);
1154 #endif /* DEBUG */
1155 our_pred->p_type = BI_OP;
1156 our_pred->p_prec = OR_PREC;
1157 our_pred->need_stat = our_pred->need_type = false;
1158 return (true);
1161 /* -path is deprecated (at RMS's request) in favour of
1162 * -iwholename. See the node "GNU Manuals" in standards.texi
1163 * for the rationale for this (basically, GNU prefers the use
1164 * of the phrase "file name" to "path name".
1166 * We do not issue a warning that this usage is deprecated
1167 * since HPUX find supports this predicate also.
1169 static boolean
1170 parse_path (char **argv, int *arg_ptr)
1172 return parse_wholename(argv, arg_ptr);
1175 static boolean
1176 parse_wholename (char **argv, int *arg_ptr)
1178 struct predicate *our_pred;
1180 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1181 return (false);
1182 our_pred = insert_primary (pred_path);
1183 our_pred->need_stat = our_pred->need_type = false;
1184 our_pred->args.str = argv[*arg_ptr];
1185 (*arg_ptr)++;
1186 return (true);
1189 static boolean
1190 parse_perm (char **argv, int *arg_ptr)
1192 mode_t perm_val;
1193 int mode_start = 0;
1194 struct mode_change *change;
1195 struct predicate *our_pred;
1197 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1198 return (false);
1200 switch (argv[*arg_ptr][0])
1202 case '-':
1203 case '+':
1204 mode_start = 1;
1205 break;
1206 default:
1207 /* empty */
1208 break;
1211 change = mode_compile (argv[*arg_ptr] + mode_start, MODE_MASK_PLUS);
1212 if (change == MODE_INVALID)
1213 error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
1214 else if (change == MODE_MEMORY_EXHAUSTED)
1215 error (1, 0, _("virtual memory exhausted"));
1216 perm_val = mode_adjust (0, change);
1217 mode_free (change);
1219 our_pred = insert_primary (pred_perm);
1221 switch (argv[*arg_ptr][0])
1223 case '-':
1224 our_pred->args.perm.kind = PERM_AT_LEAST;
1225 break;
1226 case '+':
1227 our_pred->args.perm.kind = PERM_ANY;
1228 break;
1229 default:
1230 our_pred->args.perm.kind = PERM_EXACT;
1231 break;
1233 our_pred->args.perm.val = perm_val & MODE_ALL;
1234 (*arg_ptr)++;
1235 return (true);
1238 boolean
1239 parse_print (char **argv, int *arg_ptr)
1241 struct predicate *our_pred;
1243 (void) argv;
1244 (void) arg_ptr;
1246 our_pred = insert_primary (pred_print);
1247 /* -print has the side effect of printing. This prevents us
1248 from doing undesired multiple printing when the user has
1249 already specified -print. */
1250 our_pred->side_effects = true;
1251 our_pred->no_default_print = true;
1252 our_pred->need_stat = our_pred->need_type = false;
1253 return (true);
1256 static boolean
1257 parse_print0 (char **argv, int *arg_ptr)
1259 struct predicate *our_pred;
1261 (void) argv;
1262 (void) arg_ptr;
1264 our_pred = insert_primary (pred_print0);
1265 /* -print0 has the side effect of printing. This prevents us
1266 from doing undesired multiple printing when the user has
1267 already specified -print0. */
1268 our_pred->side_effects = true;
1269 our_pred->no_default_print = true;
1270 our_pred->need_stat = our_pred->need_type = false;
1271 return (true);
1274 static boolean
1275 parse_printf (char **argv, int *arg_ptr)
1277 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1278 return (false);
1279 return (insert_fprintf (stdout, pred_fprintf, argv, arg_ptr));
1282 static boolean
1283 parse_prune (char **argv, int *arg_ptr)
1285 struct predicate *our_pred;
1287 (void) argv;
1288 (void) arg_ptr;
1290 our_pred = insert_primary (pred_prune);
1291 our_pred->need_stat = our_pred->need_type = false;
1292 /* -prune has a side effect that it does not descend into
1293 the current directory. */
1294 our_pred->side_effects = true;
1295 return (true);
1298 static boolean
1299 parse_quit (char **argv, int *arg_ptr)
1301 struct predicate *our_pred = insert_primary (pred_quit);
1302 (void) argv;
1303 (void) arg_ptr;
1304 our_pred->need_stat = our_pred->need_type = false;
1305 return true;
1309 static boolean
1310 parse_regex (char **argv, int *arg_ptr)
1312 return insert_regex (argv, arg_ptr, false);
1315 static boolean
1316 insert_regex (char **argv, int *arg_ptr, boolean ignore_case)
1318 struct predicate *our_pred;
1319 struct re_pattern_buffer *re;
1320 const char *error_message;
1322 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1323 return (false);
1324 our_pred = insert_primary (pred_regex);
1325 our_pred->need_stat = our_pred->need_type = false;
1326 re = (struct re_pattern_buffer *)
1327 xmalloc (sizeof (struct re_pattern_buffer));
1328 our_pred->args.regex = re;
1329 re->allocated = 100;
1330 re->buffer = (unsigned char *) xmalloc (re->allocated);
1331 re->fastmap = NULL;
1333 if (ignore_case)
1335 re_syntax_options |= RE_ICASE;
1337 else
1339 re_syntax_options &= ~RE_ICASE;
1341 re->translate = NULL;
1343 error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
1344 re);
1345 if (error_message)
1346 error (1, 0, "%s", error_message);
1347 (*arg_ptr)++;
1348 return (true);
1351 static boolean
1352 parse_size (char **argv, int *arg_ptr)
1354 struct predicate *our_pred;
1355 uintmax_t num;
1356 enum comparison_type c_type;
1357 int blksize = 512;
1358 int len;
1360 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1361 return (false);
1362 len = strlen (argv[*arg_ptr]);
1363 if (len == 0)
1364 error (1, 0, _("invalid null argument to -size"));
1365 switch (argv[*arg_ptr][len - 1])
1367 case 'b':
1368 blksize = 512;
1369 argv[*arg_ptr][len - 1] = '\0';
1370 break;
1372 case 'c':
1373 blksize = 1;
1374 argv[*arg_ptr][len - 1] = '\0';
1375 break;
1377 case 'k':
1378 blksize = 1024;
1379 argv[*arg_ptr][len - 1] = '\0';
1380 break;
1382 case 'M': /* Megabytes */
1383 blksize = 1024*1024;
1384 argv[*arg_ptr][len - 1] = '\0';
1385 break;
1387 case 'G': /* Gigabytes */
1388 blksize = 1024*1024*1024;
1389 argv[*arg_ptr][len - 1] = '\0';
1390 break;
1392 case 'w':
1393 blksize = 2;
1394 argv[*arg_ptr][len - 1] = '\0';
1395 break;
1397 case '0':
1398 case '1':
1399 case '2':
1400 case '3':
1401 case '4':
1402 case '5':
1403 case '6':
1404 case '7':
1405 case '8':
1406 case '9':
1407 break;
1409 default:
1410 error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
1412 if (!get_num (argv[*arg_ptr], &num, &c_type))
1413 return (false);
1414 our_pred = insert_primary (pred_size);
1415 our_pred->args.size.kind = c_type;
1416 our_pred->args.size.blocksize = blksize;
1417 our_pred->args.size.size = num;
1418 (*arg_ptr)++;
1419 return (true);
1423 static boolean
1424 parse_samefile (char **argv, int *arg_ptr)
1426 struct predicate *our_pred;
1427 struct stat st;
1429 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1430 return (false);
1431 if ((*options.xstat) (argv[*arg_ptr], &st))
1432 error (1, errno, "%s", argv[*arg_ptr]);
1434 our_pred = insert_primary (pred_samefile);
1435 our_pred->args.fileid.ino = st.st_ino;
1436 our_pred->args.fileid.dev = st.st_dev;
1437 our_pred->need_type = false;
1438 our_pred->need_stat = true;
1439 (*arg_ptr)++;
1440 return (true);
1444 static boolean
1445 parse_true (char **argv, int *arg_ptr)
1447 struct predicate *our_pred;
1449 (void) argv;
1450 (void) arg_ptr;
1452 our_pred = insert_primary (pred_true);
1453 our_pred->need_stat = our_pred->need_type = false;
1454 return (true);
1457 static boolean
1458 parse_type (char **argv, int *arg_ptr)
1460 return insert_type (argv, arg_ptr, pred_type);
1463 static boolean
1464 parse_uid (char **argv, int *arg_ptr)
1466 return (insert_num (argv, arg_ptr, pred_uid));
1469 static boolean
1470 parse_used (char **argv, int *arg_ptr)
1472 struct predicate *our_pred;
1473 uintmax_t num_days;
1474 enum comparison_type c_type;
1475 time_t t;
1477 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1478 return (false);
1479 if (!get_num (argv[*arg_ptr], &num_days, &c_type))
1480 return (false);
1481 t = num_days * DAYSECS;
1482 our_pred = insert_primary (pred_used);
1483 our_pred->args.info.kind = c_type;
1484 our_pred->args.info.negative = t < 0;
1485 our_pred->args.info.l_val = t;
1486 (*arg_ptr)++;
1487 return (true);
1490 static boolean
1491 parse_user (char **argv, int *arg_ptr)
1493 struct passwd *cur_pwd;
1494 struct predicate *our_pred;
1495 uid_t uid;
1496 int uid_len;
1498 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1499 return (false);
1500 cur_pwd = getpwnam (argv[*arg_ptr]);
1501 endpwent ();
1502 if (cur_pwd != NULL)
1503 uid = cur_pwd->pw_uid;
1504 else
1506 uid_len = strspn (argv[*arg_ptr], "0123456789");
1507 if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
1508 return (false);
1509 uid = atoi (argv[*arg_ptr]);
1511 our_pred = insert_primary (pred_user);
1512 our_pred->args.uid = uid;
1513 (*arg_ptr)++;
1514 return (true);
1517 static boolean
1518 parse_version (char **argv, int *arg_ptr)
1520 extern char *version_string;
1521 (void) argv;
1522 (void) arg_ptr;
1524 fflush (stderr);
1525 printf (_("GNU find version %s\n"), version_string);
1526 exit (0);
1529 static boolean
1530 parse_xdev (char **argv, int *arg_ptr)
1532 (void) argv;
1533 (void) arg_ptr;
1534 options.stay_on_filesystem = true;
1535 return true;
1538 static boolean
1539 parse_ignore_race (char **argv, int *arg_ptr)
1541 (void) argv;
1542 (void) arg_ptr;
1543 options.ignore_readdir_race = true;
1544 return true;
1547 static boolean
1548 parse_noignore_race (char **argv, int *arg_ptr)
1550 (void) argv;
1551 (void) arg_ptr;
1552 options.ignore_readdir_race = false;
1553 return true;
1556 static boolean
1557 parse_warn (char **argv, int *arg_ptr)
1559 (void) argv;
1560 (void) arg_ptr;
1561 options.warnings = true;
1562 return true;
1565 static boolean
1566 parse_xtype (char **argv, int *arg_ptr)
1568 (void) argv;
1569 (void) arg_ptr;
1570 return insert_type (argv, arg_ptr, pred_xtype);
1573 static boolean
1574 insert_type (char **argv, int *arg_ptr, boolean (*which_pred) (/* ??? */))
1576 mode_t type_cell;
1577 struct predicate *our_pred;
1579 if ((argv == NULL) || (argv[*arg_ptr] == NULL)
1580 || (strlen (argv[*arg_ptr]) != 1))
1581 return (false);
1582 switch (argv[*arg_ptr][0])
1584 case 'b': /* block special */
1585 type_cell = S_IFBLK;
1586 break;
1587 case 'c': /* character special */
1588 type_cell = S_IFCHR;
1589 break;
1590 case 'd': /* directory */
1591 type_cell = S_IFDIR;
1592 break;
1593 case 'f': /* regular file */
1594 type_cell = S_IFREG;
1595 break;
1596 #ifdef S_IFLNK
1597 case 'l': /* symbolic link */
1598 type_cell = S_IFLNK;
1599 break;
1600 #endif
1601 #ifdef S_IFIFO
1602 case 'p': /* pipe */
1603 type_cell = S_IFIFO;
1604 break;
1605 #endif
1606 #ifdef S_IFSOCK
1607 case 's': /* socket */
1608 type_cell = S_IFSOCK;
1609 break;
1610 #endif
1611 #ifdef S_IFDOOR
1612 case 'D': /* Solaris door */
1613 type_cell = S_IFDOOR;
1614 break;
1615 #endif
1616 default: /* None of the above ... nuke 'em. */
1617 return (false);
1619 our_pred = insert_primary (which_pred);
1621 /* Figure out if we will need to stat the file, because if we don't
1622 * need to follow symlinks, we can avoid a stat call by using
1623 * struct dirent.d_type.
1625 if (which_pred == pred_xtype)
1627 our_pred->need_stat = true;
1628 our_pred->need_type = false;
1630 else
1632 our_pred->need_stat = false; /* struct dirent is enough */
1633 our_pred->need_type = true;
1635 our_pred->args.type = type_cell;
1636 (*arg_ptr)++; /* Move on to next argument. */
1637 return (true);
1640 /* If true, we've determined that the current fprintf predicate
1641 uses stat information. */
1642 static boolean fprintf_stat_needed;
1644 static boolean
1645 insert_fprintf (FILE *fp, boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
1647 char *format; /* Beginning of unprocessed format string. */
1648 register char *scan; /* Current address in scanning `format'. */
1649 register char *scan2; /* Address inside of element being scanned. */
1650 struct segment **segmentp; /* Address of current segment. */
1651 struct predicate *our_pred;
1653 format = argv[(*arg_ptr)++];
1655 fprintf_stat_needed = false; /* Might be overridden later. */
1656 our_pred = insert_primary (func);
1657 our_pred->side_effects = true;
1658 our_pred->no_default_print = true;
1659 our_pred->args.printf_vec.stream = fp;
1660 segmentp = &our_pred->args.printf_vec.segment;
1661 *segmentp = NULL;
1663 for (scan = format; *scan; scan++)
1665 if (*scan == '\\')
1667 scan2 = scan + 1;
1668 if (*scan2 >= '0' && *scan2 <= '7')
1670 register int n, i;
1672 for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
1673 i++, scan2++)
1674 n = 8 * n + *scan2 - '0';
1675 scan2--;
1676 *scan = n;
1678 else
1680 switch (*scan2)
1682 case 'a':
1683 *scan = 7;
1684 break;
1685 case 'b':
1686 *scan = '\b';
1687 break;
1688 case 'c':
1689 make_segment (segmentp, format, scan - format, KIND_STOP);
1690 our_pred->need_stat = fprintf_stat_needed;
1691 return (true);
1692 case 'f':
1693 *scan = '\f';
1694 break;
1695 case 'n':
1696 *scan = '\n';
1697 break;
1698 case 'r':
1699 *scan = '\r';
1700 break;
1701 case 't':
1702 *scan = '\t';
1703 break;
1704 case 'v':
1705 *scan = '\v';
1706 break;
1707 case '\\':
1708 /* *scan = '\\'; * it already is */
1709 break;
1710 default:
1711 error (0, 0,
1712 _("warning: unrecognized escape `\\%c'"), *scan2);
1713 scan++;
1714 continue;
1717 segmentp = make_segment (segmentp, format, scan - format + 1,
1718 KIND_PLAIN);
1719 format = scan2 + 1; /* Move past the escape. */
1720 scan = scan2; /* Incremented immediately by `for'. */
1722 else if (*scan == '%')
1724 if (scan[1] == '%')
1726 segmentp = make_segment (segmentp, format, scan - format + 1,
1727 KIND_PLAIN);
1728 scan++;
1729 format = scan + 1;
1730 continue;
1732 /* Scan past flags, width and precision, to verify kind. */
1733 for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
1734 /* Do nothing. */ ;
1735 while (ISDIGIT (*scan2))
1736 scan2++;
1737 if (*scan2 == '.')
1738 for (scan2++; ISDIGIT (*scan2); scan2++)
1739 /* Do nothing. */ ;
1740 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2))
1742 segmentp = make_segment (segmentp, format, scan2 - format,
1743 (int) *scan2);
1744 scan = scan2;
1745 format = scan + 1;
1747 else if (strchr ("ACT", *scan2) && scan2[1])
1749 segmentp = make_segment (segmentp, format, scan2 - format,
1750 *scan2 | (scan2[1] << 8));
1751 scan = scan2 + 1;
1752 format = scan + 1;
1753 continue;
1755 else
1757 /* An unrecognized % escape. Print the char after the %. */
1758 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1759 *scan2);
1760 segmentp = make_segment (segmentp, format, scan - format,
1761 KIND_PLAIN);
1762 format = scan + 1;
1763 continue;
1768 if (scan > format)
1769 make_segment (segmentp, format, scan - format, KIND_PLAIN);
1770 our_pred->need_type = false;
1771 our_pred->need_stat = fprintf_stat_needed;
1772 return (true);
1775 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1776 from the text in FORMAT, which has length LEN.
1777 Return the address of the `next' pointer of the new segment. */
1779 static struct segment **
1780 make_segment (struct segment **segment, char *format, int len, int kind)
1782 char *fmt;
1784 *segment = (struct segment *) xmalloc (sizeof (struct segment));
1786 (*segment)->kind = kind;
1787 (*segment)->next = NULL;
1788 (*segment)->text_len = len;
1790 fmt = (*segment)->text = xmalloc (len + sizeof "d");
1791 strncpy (fmt, format, len);
1792 fmt += len;
1794 switch (kind & 0xff)
1796 case KIND_PLAIN: /* Plain text string, no % conversion. */
1797 case KIND_STOP: /* Terminate argument, no newline. */
1798 break;
1800 case 'a': /* atime in `ctime' format */
1801 case 'A': /* atime in user-specified strftime format */
1802 case 'c': /* ctime in `ctime' format */
1803 case 'C': /* ctime in user-specified strftime format */
1804 case 'F': /* filesystem type */
1805 case 'g': /* group name */
1806 case 'i': /* inode number */
1807 case 'l': /* object of symlink */
1808 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1809 case 's': /* size in bytes */
1810 case 't': /* mtime in `ctime' format */
1811 case 'T': /* mtime in user-specified strftime format */
1812 case 'u': /* user name */
1813 case 'y': /* file type */
1814 case 'Y': /* symlink pointed file type */
1815 fprintf_stat_needed = true;
1816 /* FALLTHROUGH */
1817 case 'f': /* basename of path */
1818 case 'h': /* leading directories part of path */
1819 case 'H': /* ARGV element file was found under */
1820 case 'p': /* pathname */
1821 case 'P': /* pathname with ARGV element stripped */
1822 *fmt++ = 's';
1823 break;
1825 /* Numeric items that one might expect to honour
1826 * #, 0, + flags but which do not.
1828 case 'G': /* GID number */
1829 case 'U': /* UID number */
1830 case 'b': /* size in 512-byte blocks */
1831 case 'D': /* Filesystem device on which the file exits */
1832 case 'k': /* size in 1K blocks */
1833 case 'n': /* number of links */
1834 fprintf_stat_needed = true;
1835 *fmt++ = 's';
1836 break;
1838 /* Numeric items that DO honour #, 0, + flags.
1840 case 'd': /* depth in search tree (0 = ARGV element) */
1841 *fmt++ = 'd';
1842 break;
1844 case 'm': /* mode as octal number (perms only) */
1845 *fmt++ = 'o';
1846 fprintf_stat_needed = true;
1847 break;
1849 *fmt = '\0';
1851 return (&(*segment)->next);
1854 static void
1855 check_path_safety(const char *action)
1857 const char *path = getenv("PATH");
1858 char *s;
1859 s = next_element(path, 1);
1860 while ((s = next_element ((char *) NULL, 1)) != NULL)
1862 if (0 == strcmp(s, "."))
1864 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)"),
1865 action);
1871 /* handles both exec and ok predicate */
1872 #if defined(NEW_EXEC)
1873 /* handles both exec and ok predicate */
1874 static boolean
1875 new_insert_exec_ok (const char *action,
1876 boolean (*func) (/* ??? */),
1877 char **argv, int *arg_ptr)
1879 int start, end; /* Indexes in ARGV of start & end of cmd. */
1880 int i; /* Index into cmd args */
1881 int saw_braces; /* True if previous arg was '{}'. */
1882 boolean allow_plus; /* True if + is a valid terminator */
1883 int brace_count; /* Number of instances of {}. */
1884 const char *prefix;
1885 size_t pfxlen;
1887 struct predicate *our_pred;
1888 struct exec_val *execp; /* Pointer for efficiency. */
1890 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1891 return (false);
1893 our_pred = insert_primary (func);
1894 our_pred->side_effects = true;
1895 our_pred->no_default_print = true;
1896 execp = &our_pred->args.exec_vec;
1898 if ((func != pred_okdir) && (func != pred_ok))
1899 allow_plus = true;
1900 else
1901 allow_plus = false;
1903 if ((func == pred_execdir) || (func == pred_okdir))
1905 check_path_safety(action);
1906 execp->use_current_dir = true;
1908 else
1910 execp->use_current_dir = false;
1913 our_pred->args.exec_vec.multiple = 0;
1915 /* Count the number of args with path replacements, up until the ';'.
1916 * Also figure out if the command is terminated by ";" or by "+".
1918 start = *arg_ptr;
1919 for (end = start, saw_braces=0, brace_count=0;
1920 (argv[end] != NULL)
1921 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
1922 end++)
1924 /* For -exec and -execdir, "{} +" can terminate the command. */
1925 if ( allow_plus
1926 && argv[end][0] == '+' && argv[end][1] == 0
1927 && saw_braces)
1929 our_pred->args.exec_vec.multiple = 1;
1930 break;
1933 saw_braces = 0;
1934 if (strstr (argv[end], "{}"))
1936 saw_braces = 1;
1937 ++brace_count;
1939 if (0 == end && (func == pred_execdir || func == pred_okdir))
1941 /* The POSIX standard says that {} replacement should
1942 * occur even in the utility name. This is insecure
1943 * since it means we will be executing a command whose
1944 * name is chosen according to whatever find finds in
1945 * the filesystem. That can be influenced by an
1946 * attacker. Hence for -execdir and -okdir this is not
1947 * allowed. We can specify this as those options are
1948 * not defined by POSIX.
1950 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
1955 /* Fail if no command given or no semicolon found. */
1956 if ((end == start) || (argv[end] == NULL))
1958 *arg_ptr = end;
1959 free(our_pred);
1960 return false;
1963 if (our_pred->args.exec_vec.multiple && brace_count > 1)
1966 const char *suffix;
1967 if (func == pred_execdir)
1968 suffix = "dir";
1969 else
1970 suffix = "";
1972 error(1, 0,
1973 _("Only one instance of {} is supported with -exec%s ... +"),
1974 suffix);
1977 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
1978 bc_init_controlinfo(&execp->ctl);
1979 execp->ctl.exec_callback = launch;
1981 if (our_pred->args.exec_vec.multiple)
1983 /* "+" terminator, so we can just append our arguments after the
1984 * command and initial arguments.
1986 execp->replace_vec = NULL;
1987 execp->ctl.replace_pat = NULL;
1988 execp->ctl.rplen = 0;
1989 execp->ctl.lines_per_exec = 0; /* no limit */
1990 execp->ctl.args_per_exec = 0; /* no limit */
1992 /* remember how many arguments there are */
1993 execp->ctl.initial_argc = (end-start) - 1;
1995 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
1996 bc_init_state(&execp->ctl, &execp->state, execp);
1998 /* Gather the initial arguments. Skip the {}. */
1999 for (i=start; i<end-1; ++i)
2001 bc_push_arg(&execp->ctl, &execp->state,
2002 argv[i], strlen(argv[i])+1,
2003 NULL, 0,
2007 else
2009 /* Semicolon terminator - more than one {} is supported, so we
2010 * have to do brace-replacement.
2012 execp->num_args = end - start;
2014 execp->ctl.replace_pat = "{}";
2015 execp->ctl.rplen = strlen(execp->ctl.replace_pat);
2016 execp->ctl.lines_per_exec = 0; /* no limit */
2017 execp->ctl.args_per_exec = 0; /* no limit */
2018 execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
2021 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2022 bc_init_state(&execp->ctl, &execp->state, execp);
2024 /* Remember the (pre-replacement) arguments for later. */
2025 for (i=0; i<execp->num_args; ++i)
2027 execp->replace_vec[i] = argv[i+start];
2031 if (argv[end] == NULL)
2032 *arg_ptr = end;
2033 else
2034 *arg_ptr = end + 1;
2036 return true;
2038 #else
2039 /* handles both exec and ok predicate */
2040 static boolean
2041 old_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
2043 int start, end; /* Indexes in ARGV of start & end of cmd. */
2044 int num_paths; /* Number of args with path replacements. */
2045 int path_pos; /* Index in array of path replacements. */
2046 int vec_pos; /* Index in array of args. */
2047 struct predicate *our_pred;
2048 struct exec_val *execp; /* Pointer for efficiency. */
2050 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2051 return (false);
2053 /* Count the number of args with path replacements, up until the ';'. */
2054 start = *arg_ptr;
2055 for (end = start, num_paths = 0;
2056 (argv[end] != NULL)
2057 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2058 end++)
2059 if (strstr (argv[end], "{}"))
2060 num_paths++;
2061 /* Fail if no command given or no semicolon found. */
2062 if ((end == start) || (argv[end] == NULL))
2064 *arg_ptr = end;
2065 return (false);
2068 our_pred = insert_primary (func);
2069 our_pred->side_effects = true;
2070 our_pred->no_default_print = true;
2071 execp = &our_pred->args.exec_vec;
2072 execp->usercontext = our_pred;
2073 execp->use_current_dir = false;
2074 execp->paths =
2075 (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1));
2076 execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1));
2077 /* Record the positions of all args, and the args with path replacements. */
2078 for (end = start, path_pos = vec_pos = 0;
2079 (argv[end] != NULL)
2080 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2081 end++)
2083 register char *p;
2085 execp->paths[path_pos].count = 0;
2086 for (p = argv[end]; *p; ++p)
2087 if (p[0] == '{' && p[1] == '}')
2089 execp->paths[path_pos].count++;
2090 ++p;
2092 if (execp->paths[path_pos].count)
2094 execp->paths[path_pos].offset = vec_pos;
2095 execp->paths[path_pos].origarg = argv[end];
2096 path_pos++;
2098 execp->vec[vec_pos++] = argv[end];
2100 execp->paths[path_pos].offset = -1;
2101 execp->vec[vec_pos] = NULL;
2103 if (argv[end] == NULL)
2104 *arg_ptr = end;
2105 else
2106 *arg_ptr = end + 1;
2107 return (true);
2109 #endif
2113 static boolean
2114 insert_exec_ok (const char *action,
2115 boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
2117 #if defined(NEW_EXEC)
2118 return new_insert_exec_ok(action, func, argv, arg_ptr);
2119 #else
2120 return old_insert_exec_ok(func, argv, arg_ptr);
2121 #endif
2126 /* Get a number of days and comparison type.
2127 STR is the ASCII representation.
2128 Set *NUM_DAYS to the number of days, taken as being from
2129 the current moment (or possibly midnight). Thus the sense of the
2130 comparison type appears to be reversed.
2131 Set *COMP_TYPE to the kind of comparison that is requested.
2133 Return true if all okay, false if input error.
2135 Used by -atime, -ctime and -mtime (parsers) to
2136 get the appropriate information for a time predicate processor. */
2138 static boolean
2139 get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
2141 boolean r = get_num (str, num_days, comp_type);
2142 if (r)
2143 switch (*comp_type)
2145 case COMP_LT: *comp_type = COMP_GT; break;
2146 case COMP_GT: *comp_type = COMP_LT; break;
2147 default: break;
2149 return r;
2152 /* Insert a time predicate PRED.
2153 ARGV is a pointer to the argument array.
2154 ARG_PTR is a pointer to an index into the array, incremented if
2155 all went well.
2157 Return true if input is valid, false if not.
2159 A new predicate node is assigned, along with an argument node
2160 obtained with malloc.
2162 Used by -atime, -ctime, and -mtime parsers. */
2164 static boolean
2165 insert_time (char **argv, int *arg_ptr, PFB pred)
2167 struct predicate *our_pred;
2168 uintmax_t num_days;
2169 enum comparison_type c_type;
2170 time_t t;
2172 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2173 return (false);
2174 if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
2175 return (false);
2177 /* Figure out the timestamp value we are looking for. */
2178 t = ( options.cur_day_start - num_days * DAYSECS
2179 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2181 if (1)
2183 /* We introduce a scope in which 'val' can be declared, for the
2184 * benefit of compilers that are really C89 compilers
2185 * which support intmax_t because config.h #defines it
2187 intmax_t val = ( (intmax_t)options.cur_day_start - num_days * DAYSECS
2188 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2189 t = val;
2191 /* Check for possibility of an overflow */
2192 if ( (intmax_t)t != val )
2194 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv[*arg_ptr]);
2198 our_pred = insert_primary (pred);
2199 our_pred->args.info.kind = c_type;
2200 our_pred->args.info.negative = t < 0;
2201 our_pred->args.info.l_val = t;
2202 (*arg_ptr)++;
2203 #ifdef DEBUG
2204 fprintf (stderr, _("inserting %s\n"), our_pred->p_name);
2205 fprintf (stderr, _(" type: %s %s "),
2206 (c_type == COMP_GT) ? "gt" :
2207 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2208 (c_type == COMP_GT) ? " >" :
2209 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
2210 t = our_pred->args.info.l_val;
2211 fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2212 if (c_type == COMP_EQ)
2214 t = our_pred->args.info.l_val += DAYSECS;
2215 fprintf (stderr,
2216 " < %ju %s",
2217 (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2218 our_pred->args.info.l_val -= DAYSECS;
2220 #endif /* DEBUG */
2221 return (true);
2224 /* Get a number with comparision information.
2225 The sense of the comparision information is 'normal'; that is,
2226 '+' looks for a count > than the number and '-' less than.
2228 STR is the ASCII representation of the number.
2229 Set *NUM to the number.
2230 Set *COMP_TYPE to the kind of comparison that is requested.
2232 Return true if all okay, false if input error. */
2234 static boolean
2235 get_num (char *str, uintmax_t *num, enum comparison_type *comp_type)
2237 if (str == NULL)
2238 return (false);
2239 switch (str[0])
2241 case '+':
2242 *comp_type = COMP_GT;
2243 str++;
2244 break;
2245 case '-':
2246 *comp_type = COMP_LT;
2247 str++;
2248 break;
2249 default:
2250 *comp_type = COMP_EQ;
2251 break;
2254 return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK;
2257 /* Insert a number predicate.
2258 ARGV is a pointer to the argument array.
2259 *ARG_PTR is an index into ARGV, incremented if all went well.
2260 *PRED is the predicate processor to insert.
2262 Return true if input is valid, false if error.
2264 A new predicate node is assigned, along with an argument node
2265 obtained with malloc.
2267 Used by -inum and -links parsers. */
2269 static boolean
2270 insert_num (char **argv, int *arg_ptr, PFB pred)
2272 struct predicate *our_pred;
2273 uintmax_t num;
2274 enum comparison_type c_type;
2276 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2277 return (false);
2278 if (!get_num (argv[*arg_ptr], &num, &c_type))
2279 return (false);
2280 our_pred = insert_primary (pred);
2281 our_pred->args.info.kind = c_type;
2282 our_pred->args.info.l_val = num;
2283 (*arg_ptr)++;
2284 #ifdef DEBUG
2285 fprintf (stderr, _("inserting %s\n"), our_pred->p_name);
2286 fprintf (stderr, _(" type: %s %s "),
2287 (c_type == COMP_GT) ? "gt" :
2288 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2289 (c_type == COMP_GT) ? " >" :
2290 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
2291 fprintf (stderr, "%ju\n", our_pred->args.info.l_val);
2292 #endif /* DEBUG */
2293 return (true);
2296 static FILE *
2297 open_output_file (char *path)
2299 FILE *f;
2301 if (!strcmp (path, "/dev/stderr"))
2302 return (stderr);
2303 else if (!strcmp (path, "/dev/stdout"))
2304 return (stdout);
2305 f = fopen (path, "w");
2306 if (f == NULL)
2307 error (1, errno, "%s", path);
2308 return (f);