Applied Paul Eggert's patch which corrects a POSIX non-conformance for '-perm ++r'
[findutils.git] / find / parser.c
blobb7f3f7ab2e2a1ea7ab5d9e8df56c199706d87ddd
1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 91, 92, 93, 94, 2000, 2001, 2003, 2004, 2005 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;
792 static boolean
793 check_name_arg(const char *pred, const char *arg)
795 if (strchr(arg, '/'))
797 error(0, 0,_("warning: Unix filenames usually don't contain slashes (though pathnames do). That means that '%s %s' will probably evaluate to false all the time on this system. You might find the '-wholename' test more useful, or perhaps '-samefile'. Alternatively, if you are using GNU grep, you could use 'find ... -print0 | grep -FzZ %s'."),
798 pred, arg, arg);
800 return true; /* allow it anyway */
805 static boolean
806 parse_iname (char **argv, int *arg_ptr)
808 struct predicate *our_pred;
810 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
811 return (false);
812 if (!check_name_arg("-iname", argv[*arg_ptr]))
813 return false;
815 fnmatch_sanitycheck();
817 our_pred = insert_primary (pred_iname);
818 our_pred->need_stat = our_pred->need_type = false;
819 our_pred->args.str = argv[*arg_ptr];
820 (*arg_ptr)++;
821 return (true);
824 static boolean
825 parse_inum (char **argv, int *arg_ptr)
827 return (insert_num (argv, arg_ptr, pred_inum));
830 /* -ipath is deprecated (at RMS's request) in favour of
831 * -iwholename. See the node "GNU Manuals" in standards.texi
832 * for the rationale for this (basically, GNU prefers the use
833 * of the phrase "file name" to "path name"
835 static boolean
836 parse_ipath (char **argv, int *arg_ptr)
838 error (0, 0,
839 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
841 return parse_iwholename(argv, arg_ptr);
844 static boolean
845 parse_iwholename (char **argv, int *arg_ptr)
847 struct predicate *our_pred;
849 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
850 return (false);
852 fnmatch_sanitycheck();
854 our_pred = insert_primary (pred_ipath);
855 our_pred->need_stat = our_pred->need_type = false;
856 our_pred->args.str = argv[*arg_ptr];
857 (*arg_ptr)++;
858 return (true);
861 static boolean
862 parse_iregex (char **argv, int *arg_ptr)
864 return insert_regex (argv, arg_ptr, true);
867 static boolean
868 parse_links (char **argv, int *arg_ptr)
870 return (insert_num (argv, arg_ptr, pred_links));
873 static boolean
874 parse_lname (char **argv, int *arg_ptr)
876 struct predicate *our_pred;
878 (void) argv;
879 (void) arg_ptr;
881 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
882 return (false);
884 fnmatch_sanitycheck();
886 our_pred = insert_primary (pred_lname);
887 our_pred->args.str = argv[*arg_ptr];
888 (*arg_ptr)++;
889 return (true);
892 static boolean
893 parse_ls (char **argv, int *arg_ptr)
895 struct predicate *our_pred;
897 (void) &argv;
898 (void) &arg_ptr;
900 our_pred = insert_primary (pred_ls);
901 our_pred->side_effects = true;
902 our_pred->no_default_print = true;
903 return (true);
906 static boolean
907 parse_maxdepth (char **argv, int *arg_ptr)
909 int depth_len;
911 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
912 return false;
913 depth_len = strspn (argv[*arg_ptr], "0123456789");
914 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
915 return false;
916 options.maxdepth = atoi (argv[*arg_ptr]);
917 if (options.maxdepth < 0)
918 return false;
919 (*arg_ptr)++;
920 return true;
923 static boolean
924 parse_mindepth (char **argv, int *arg_ptr)
926 int depth_len;
928 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
929 return false;
930 depth_len = strspn (argv[*arg_ptr], "0123456789");
931 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
932 return false;
933 options.mindepth = atoi (argv[*arg_ptr]);
934 if (options.mindepth < 0)
935 return false;
936 (*arg_ptr)++;
937 return true;
940 static boolean
941 parse_mmin (char **argv, int *arg_ptr)
943 struct predicate *our_pred;
944 uintmax_t num;
945 enum comparison_type c_type;
946 time_t t;
948 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
949 return (false);
950 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
951 return (false);
952 t = options.cur_day_start + DAYSECS - num * 60;
953 our_pred = insert_primary (pred_mmin);
954 our_pred->args.info.kind = c_type;
955 our_pred->args.info.negative = t < 0;
956 our_pred->args.info.l_val = t;
957 (*arg_ptr)++;
958 return (true);
961 static boolean
962 parse_mtime (char **argv, int *arg_ptr)
964 return (insert_time (argv, arg_ptr, pred_mtime));
967 static boolean
968 parse_name (char **argv, int *arg_ptr)
970 struct predicate *our_pred;
972 (void) argv;
973 (void) arg_ptr;
975 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
976 return (false);
977 if (!check_name_arg("-name", argv[*arg_ptr]))
978 return false;
979 fnmatch_sanitycheck();
981 our_pred = insert_primary (pred_name);
982 our_pred->need_stat = our_pred->need_type = false;
983 our_pred->args.str = argv[*arg_ptr];
984 (*arg_ptr)++;
985 return (true);
988 static boolean
989 parse_negate (char **argv, int *arg_ptr)
991 struct predicate *our_pred;
993 (void) &argv;
994 (void) &arg_ptr;
996 our_pred = get_new_pred_chk_op ();
997 our_pred->pred_func = pred_negate;
998 #ifdef DEBUG
999 our_pred->p_name = find_pred_name (pred_negate);
1000 #endif /* DEBUG */
1001 our_pred->p_type = UNI_OP;
1002 our_pred->p_prec = NEGATE_PREC;
1003 our_pred->need_stat = our_pred->need_type = false;
1004 return (true);
1007 static boolean
1008 parse_newer (char **argv, int *arg_ptr)
1010 struct predicate *our_pred;
1011 struct stat stat_newer;
1013 (void) argv;
1014 (void) arg_ptr;
1016 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1017 return (false);
1018 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
1019 error (1, errno, "%s", argv[*arg_ptr]);
1020 our_pred = insert_primary (pred_newer);
1021 our_pred->args.time = stat_newer.st_mtime;
1022 (*arg_ptr)++;
1023 return (true);
1026 static boolean
1027 parse_noleaf (char **argv, int *arg_ptr)
1029 (void) &argv;
1030 (void) &arg_ptr;
1032 options.no_leaf_check = true;
1033 return true;
1036 #ifdef CACHE_IDS
1037 /* Arbitrary amount by which to increase size
1038 of `uid_unused' and `gid_unused'. */
1039 #define ALLOC_STEP 2048
1041 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1042 char *uid_unused = NULL;
1044 /* Number of elements in `uid_unused'. */
1045 unsigned uid_allocated;
1047 /* Similar for GIDs and group entries. */
1048 char *gid_unused = NULL;
1049 unsigned gid_allocated;
1050 #endif
1052 static boolean
1053 parse_nogroup (char **argv, int *arg_ptr)
1055 struct predicate *our_pred;
1057 (void) &argv;
1058 (void) &arg_ptr;
1060 our_pred = insert_primary (pred_nogroup);
1061 #ifdef CACHE_IDS
1062 if (gid_unused == NULL)
1064 struct group *gr;
1066 gid_allocated = ALLOC_STEP;
1067 gid_unused = xmalloc (gid_allocated);
1068 memset (gid_unused, 1, gid_allocated);
1069 setgrent ();
1070 while ((gr = getgrent ()) != NULL)
1072 if ((unsigned) gr->gr_gid >= gid_allocated)
1074 unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
1075 gid_unused = xrealloc (gid_unused, new_allocated);
1076 memset (gid_unused + gid_allocated, 1,
1077 new_allocated - gid_allocated);
1078 gid_allocated = new_allocated;
1080 gid_unused[(unsigned) gr->gr_gid] = 0;
1082 endgrent ();
1084 #endif
1085 return (true);
1088 static boolean
1089 parse_nouser (char **argv, int *arg_ptr)
1091 struct predicate *our_pred;
1092 (void) argv;
1093 (void) arg_ptr;
1096 our_pred = insert_primary (pred_nouser);
1097 #ifdef CACHE_IDS
1098 if (uid_unused == NULL)
1100 struct passwd *pw;
1102 uid_allocated = ALLOC_STEP;
1103 uid_unused = xmalloc (uid_allocated);
1104 memset (uid_unused, 1, uid_allocated);
1105 setpwent ();
1106 while ((pw = getpwent ()) != NULL)
1108 if ((unsigned) pw->pw_uid >= uid_allocated)
1110 unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
1111 uid_unused = xrealloc (uid_unused, new_allocated);
1112 memset (uid_unused + uid_allocated, 1,
1113 new_allocated - uid_allocated);
1114 uid_allocated = new_allocated;
1116 uid_unused[(unsigned) pw->pw_uid] = 0;
1118 endpwent ();
1120 #endif
1121 return (true);
1124 static boolean
1125 parse_nowarn (char **argv, int *arg_ptr)
1127 (void) argv;
1128 (void) arg_ptr;
1130 options.warnings = false;
1131 return true;;
1134 static boolean
1135 parse_ok (char **argv, int *arg_ptr)
1137 return (insert_exec_ok ("-ok", pred_ok, argv, arg_ptr));
1140 static boolean
1141 parse_okdir (char **argv, int *arg_ptr)
1143 return (insert_exec_ok ("-okdir", pred_okdir, argv, arg_ptr));
1146 boolean
1147 parse_open (char **argv, int *arg_ptr)
1149 struct predicate *our_pred;
1151 (void) argv;
1152 (void) arg_ptr;
1154 our_pred = get_new_pred_chk_op ();
1155 our_pred->pred_func = pred_open;
1156 #ifdef DEBUG
1157 our_pred->p_name = find_pred_name (pred_open);
1158 #endif /* DEBUG */
1159 our_pred->p_type = OPEN_PAREN;
1160 our_pred->p_prec = NO_PREC;
1161 our_pred->need_stat = our_pred->need_type = false;
1162 return (true);
1165 static boolean
1166 parse_or (char **argv, int *arg_ptr)
1168 struct predicate *our_pred;
1170 (void) argv;
1171 (void) arg_ptr;
1173 our_pred = get_new_pred ();
1174 our_pred->pred_func = pred_or;
1175 #ifdef DEBUG
1176 our_pred->p_name = find_pred_name (pred_or);
1177 #endif /* DEBUG */
1178 our_pred->p_type = BI_OP;
1179 our_pred->p_prec = OR_PREC;
1180 our_pred->need_stat = our_pred->need_type = false;
1181 return (true);
1184 /* -path is deprecated (at RMS's request) in favour of
1185 * -iwholename. See the node "GNU Manuals" in standards.texi
1186 * for the rationale for this (basically, GNU prefers the use
1187 * of the phrase "file name" to "path name".
1189 * We do not issue a warning that this usage is deprecated
1190 * since HPUX find supports this predicate also.
1192 static boolean
1193 parse_path (char **argv, int *arg_ptr)
1195 return parse_wholename(argv, arg_ptr);
1198 static boolean
1199 parse_wholename (char **argv, int *arg_ptr)
1201 struct predicate *our_pred;
1203 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1204 return (false);
1205 our_pred = insert_primary (pred_path);
1206 our_pred->need_stat = our_pred->need_type = false;
1207 our_pred->args.str = argv[*arg_ptr];
1208 (*arg_ptr)++;
1209 return (true);
1212 static boolean
1213 parse_perm (char **argv, int *arg_ptr)
1215 mode_t perm_val;
1216 int mode_start = 0;
1217 enum permissions_type kind = PERM_EXACT;
1218 struct mode_change *change = NULL;
1219 struct predicate *our_pred;
1221 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1222 return (false);
1224 switch (argv[*arg_ptr][0])
1226 case '-':
1227 mode_start = 1;
1228 kind = PERM_AT_LEAST;
1229 break;
1231 case '+':
1232 change = mode_compile (argv[*arg_ptr]);
1233 if (NULL == change)
1235 /* Most likely the caller is an old script that is still
1236 * using the obsolete GNU syntax '-perm +MODE'. This old
1237 * syntax was withdrawn in favor of '-perm /MODE' because
1238 * it is incompatible with POSIX in some cases, but we
1239 * still support uses of it that are not incompatible with
1240 * POSIX.
1242 mode_start = 1;
1243 kind = PERM_ANY;
1245 else
1247 /* This is a POSIX-compatible usage */
1248 mode_start = 0;
1249 kind = PERM_EXACT;
1251 break;
1253 case '/': /* GNU extension */
1254 mode_start = 1;
1255 kind = PERM_ANY;
1256 break;
1258 default:
1259 /* For example, '-perm 0644', which is valid and matches
1260 * only files whose mode is exactly 0644.
1262 * We do nothing here, because mode_start and kind are already
1263 * correctly set.
1265 break;
1268 if (NULL == change)
1270 change = mode_compile (argv[*arg_ptr] + mode_start);
1271 if (NULL == change)
1272 error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
1274 perm_val = mode_adjust (0, change, 0);
1275 free (change);
1277 our_pred = insert_primary (pred_perm);
1279 switch (argv[*arg_ptr][0])
1281 case '-':
1282 our_pred->args.perm.kind = PERM_AT_LEAST;
1283 break;
1284 case '+':
1285 our_pred->args.perm.kind = PERM_ANY;
1286 break;
1287 default:
1288 our_pred->args.perm.kind = PERM_EXACT;
1289 break;
1291 our_pred->args.perm.val = perm_val & MODE_ALL;
1292 (*arg_ptr)++;
1293 return (true);
1296 boolean
1297 parse_print (char **argv, int *arg_ptr)
1299 struct predicate *our_pred;
1301 (void) argv;
1302 (void) arg_ptr;
1304 our_pred = insert_primary (pred_print);
1305 /* -print has the side effect of printing. This prevents us
1306 from doing undesired multiple printing when the user has
1307 already specified -print. */
1308 our_pred->side_effects = true;
1309 our_pred->no_default_print = true;
1310 our_pred->need_stat = our_pred->need_type = false;
1311 return (true);
1314 static boolean
1315 parse_print0 (char **argv, int *arg_ptr)
1317 struct predicate *our_pred;
1319 (void) argv;
1320 (void) arg_ptr;
1322 our_pred = insert_primary (pred_print0);
1323 /* -print0 has the side effect of printing. This prevents us
1324 from doing undesired multiple printing when the user has
1325 already specified -print0. */
1326 our_pred->side_effects = true;
1327 our_pred->no_default_print = true;
1328 our_pred->need_stat = our_pred->need_type = false;
1329 return (true);
1332 static boolean
1333 parse_printf (char **argv, int *arg_ptr)
1335 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1336 return (false);
1337 return (insert_fprintf (stdout, pred_fprintf, argv, arg_ptr));
1340 static boolean
1341 parse_prune (char **argv, int *arg_ptr)
1343 struct predicate *our_pred;
1345 (void) argv;
1346 (void) arg_ptr;
1348 our_pred = insert_primary (pred_prune);
1349 our_pred->need_stat = our_pred->need_type = false;
1350 /* -prune has a side effect that it does not descend into
1351 the current directory. */
1352 our_pred->side_effects = true;
1353 return (true);
1356 static boolean
1357 parse_quit (char **argv, int *arg_ptr)
1359 struct predicate *our_pred = insert_primary (pred_quit);
1360 (void) argv;
1361 (void) arg_ptr;
1362 our_pred->need_stat = our_pred->need_type = false;
1363 return true;
1367 static boolean
1368 parse_regex (char **argv, int *arg_ptr)
1370 return insert_regex (argv, arg_ptr, false);
1373 static boolean
1374 insert_regex (char **argv, int *arg_ptr, boolean ignore_case)
1376 struct predicate *our_pred;
1377 struct re_pattern_buffer *re;
1378 const char *error_message;
1380 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1381 return (false);
1382 our_pred = insert_primary (pred_regex);
1383 our_pred->need_stat = our_pred->need_type = false;
1384 re = (struct re_pattern_buffer *)
1385 xmalloc (sizeof (struct re_pattern_buffer));
1386 our_pred->args.regex = re;
1387 re->allocated = 100;
1388 re->buffer = (unsigned char *) xmalloc (re->allocated);
1389 re->fastmap = NULL;
1391 int options = RE_SYNTAX_POSIX_BASIC;
1392 if (ignore_case)
1393 options |= RE_ICASE;
1395 re_set_syntax(options);
1396 re->syntax = options;
1397 re->translate = NULL;
1399 error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
1400 re);
1401 if (error_message)
1402 error (1, 0, "%s", error_message);
1403 (*arg_ptr)++;
1404 return (true);
1407 static boolean
1408 parse_size (char **argv, int *arg_ptr)
1410 struct predicate *our_pred;
1411 uintmax_t num;
1412 enum comparison_type c_type;
1413 int blksize = 512;
1414 int len;
1416 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1417 return (false);
1418 len = strlen (argv[*arg_ptr]);
1419 if (len == 0)
1420 error (1, 0, _("invalid null argument to -size"));
1421 switch (argv[*arg_ptr][len - 1])
1423 case 'b':
1424 blksize = 512;
1425 argv[*arg_ptr][len - 1] = '\0';
1426 break;
1428 case 'c':
1429 blksize = 1;
1430 argv[*arg_ptr][len - 1] = '\0';
1431 break;
1433 case 'k':
1434 blksize = 1024;
1435 argv[*arg_ptr][len - 1] = '\0';
1436 break;
1438 case 'M': /* Megabytes */
1439 blksize = 1024*1024;
1440 argv[*arg_ptr][len - 1] = '\0';
1441 break;
1443 case 'G': /* Gigabytes */
1444 blksize = 1024*1024*1024;
1445 argv[*arg_ptr][len - 1] = '\0';
1446 break;
1448 case 'w':
1449 blksize = 2;
1450 argv[*arg_ptr][len - 1] = '\0';
1451 break;
1453 case '0':
1454 case '1':
1455 case '2':
1456 case '3':
1457 case '4':
1458 case '5':
1459 case '6':
1460 case '7':
1461 case '8':
1462 case '9':
1463 break;
1465 default:
1466 error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
1468 if (!get_num (argv[*arg_ptr], &num, &c_type))
1469 return (false);
1470 our_pred = insert_primary (pred_size);
1471 our_pred->args.size.kind = c_type;
1472 our_pred->args.size.blocksize = blksize;
1473 our_pred->args.size.size = num;
1474 (*arg_ptr)++;
1475 return (true);
1479 static boolean
1480 parse_samefile (char **argv, int *arg_ptr)
1482 struct predicate *our_pred;
1483 struct stat st;
1485 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1486 return (false);
1487 if ((*options.xstat) (argv[*arg_ptr], &st))
1488 error (1, errno, "%s", argv[*arg_ptr]);
1490 our_pred = insert_primary (pred_samefile);
1491 our_pred->args.fileid.ino = st.st_ino;
1492 our_pred->args.fileid.dev = st.st_dev;
1493 our_pred->need_type = false;
1494 our_pred->need_stat = true;
1495 (*arg_ptr)++;
1496 return (true);
1500 static boolean
1501 parse_true (char **argv, int *arg_ptr)
1503 struct predicate *our_pred;
1505 (void) argv;
1506 (void) arg_ptr;
1508 our_pred = insert_primary (pred_true);
1509 our_pred->need_stat = our_pred->need_type = false;
1510 return (true);
1513 static boolean
1514 parse_type (char **argv, int *arg_ptr)
1516 return insert_type (argv, arg_ptr, pred_type);
1519 static boolean
1520 parse_uid (char **argv, int *arg_ptr)
1522 return (insert_num (argv, arg_ptr, pred_uid));
1525 static boolean
1526 parse_used (char **argv, int *arg_ptr)
1528 struct predicate *our_pred;
1529 uintmax_t num_days;
1530 enum comparison_type c_type;
1531 time_t t;
1533 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1534 return (false);
1535 if (!get_num (argv[*arg_ptr], &num_days, &c_type))
1536 return (false);
1537 t = num_days * DAYSECS;
1538 our_pred = insert_primary (pred_used);
1539 our_pred->args.info.kind = c_type;
1540 our_pred->args.info.negative = t < 0;
1541 our_pred->args.info.l_val = t;
1542 (*arg_ptr)++;
1543 return (true);
1546 static boolean
1547 parse_user (char **argv, int *arg_ptr)
1549 struct passwd *cur_pwd;
1550 struct predicate *our_pred;
1551 uid_t uid;
1552 int uid_len;
1554 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1555 return (false);
1556 cur_pwd = getpwnam (argv[*arg_ptr]);
1557 endpwent ();
1558 if (cur_pwd != NULL)
1559 uid = cur_pwd->pw_uid;
1560 else
1562 uid_len = strspn (argv[*arg_ptr], "0123456789");
1563 if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
1564 return (false);
1565 uid = atoi (argv[*arg_ptr]);
1567 our_pred = insert_primary (pred_user);
1568 our_pred->args.uid = uid;
1569 (*arg_ptr)++;
1570 return (true);
1573 static boolean
1574 parse_version (char **argv, int *arg_ptr)
1576 extern char *version_string;
1577 int features = 0;
1579 (void) argv;
1580 (void) arg_ptr;
1582 fflush (stderr);
1583 printf (_("GNU find version %s\n"), version_string);
1584 printf (_("Features enabled: "));
1586 #if CACHE_IDS
1587 printf("CACHE_IDS ");
1588 ++features;
1589 #endif
1590 #if DEBUG
1591 printf("DEBUG ");
1592 ++features;
1593 #endif
1594 #if DEBUG_STAT
1595 printf("DEBUG_STAT ");
1596 ++features;
1597 #endif
1598 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1599 printf("D_TYPE ");
1600 ++features;
1601 #endif
1602 #if defined(O_NOFOLLOW)
1603 printf("O_NOFOLLOW(%s) ",
1604 (options.open_nofollow_available ? "enabled" : "disabled"));
1605 ++features;
1606 #endif
1608 if (0 == features)
1610 /* For the moment, leave this as English in case someone wants
1611 to parse these strings. */
1612 printf("none");
1614 printf("\n");
1616 exit (0);
1619 static boolean
1620 parse_xdev (char **argv, int *arg_ptr)
1622 (void) argv;
1623 (void) arg_ptr;
1624 options.stay_on_filesystem = true;
1625 return true;
1628 static boolean
1629 parse_ignore_race (char **argv, int *arg_ptr)
1631 (void) argv;
1632 (void) arg_ptr;
1633 options.ignore_readdir_race = true;
1634 return true;
1637 static boolean
1638 parse_noignore_race (char **argv, int *arg_ptr)
1640 (void) argv;
1641 (void) arg_ptr;
1642 options.ignore_readdir_race = false;
1643 return true;
1646 static boolean
1647 parse_warn (char **argv, int *arg_ptr)
1649 (void) argv;
1650 (void) arg_ptr;
1651 options.warnings = true;
1652 return true;
1655 static boolean
1656 parse_xtype (char **argv, int *arg_ptr)
1658 (void) argv;
1659 (void) arg_ptr;
1660 return insert_type (argv, arg_ptr, pred_xtype);
1663 static boolean
1664 insert_type (char **argv, int *arg_ptr, boolean (*which_pred) (/* ??? */))
1666 mode_t type_cell;
1667 struct predicate *our_pred;
1669 if ((argv == NULL) || (argv[*arg_ptr] == NULL)
1670 || (strlen (argv[*arg_ptr]) != 1))
1671 return (false);
1672 switch (argv[*arg_ptr][0])
1674 case 'b': /* block special */
1675 type_cell = S_IFBLK;
1676 break;
1677 case 'c': /* character special */
1678 type_cell = S_IFCHR;
1679 break;
1680 case 'd': /* directory */
1681 type_cell = S_IFDIR;
1682 break;
1683 case 'f': /* regular file */
1684 type_cell = S_IFREG;
1685 break;
1686 #ifdef S_IFLNK
1687 case 'l': /* symbolic link */
1688 type_cell = S_IFLNK;
1689 break;
1690 #endif
1691 #ifdef S_IFIFO
1692 case 'p': /* pipe */
1693 type_cell = S_IFIFO;
1694 break;
1695 #endif
1696 #ifdef S_IFSOCK
1697 case 's': /* socket */
1698 type_cell = S_IFSOCK;
1699 break;
1700 #endif
1701 #ifdef S_IFDOOR
1702 case 'D': /* Solaris door */
1703 type_cell = S_IFDOOR;
1704 break;
1705 #endif
1706 default: /* None of the above ... nuke 'em. */
1707 return (false);
1709 our_pred = insert_primary (which_pred);
1711 /* Figure out if we will need to stat the file, because if we don't
1712 * need to follow symlinks, we can avoid a stat call by using
1713 * struct dirent.d_type.
1715 if (which_pred == pred_xtype)
1717 our_pred->need_stat = true;
1718 our_pred->need_type = false;
1720 else
1722 our_pred->need_stat = false; /* struct dirent is enough */
1723 our_pred->need_type = true;
1725 our_pred->args.type = type_cell;
1726 (*arg_ptr)++; /* Move on to next argument. */
1727 return (true);
1730 /* If true, we've determined that the current fprintf predicate
1731 uses stat information. */
1732 static boolean fprintf_stat_needed;
1734 static boolean
1735 insert_fprintf (FILE *fp, boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
1737 char *format; /* Beginning of unprocessed format string. */
1738 register char *scan; /* Current address in scanning `format'. */
1739 register char *scan2; /* Address inside of element being scanned. */
1740 struct segment **segmentp; /* Address of current segment. */
1741 struct predicate *our_pred;
1743 format = argv[(*arg_ptr)++];
1745 fprintf_stat_needed = false; /* Might be overridden later. */
1746 our_pred = insert_primary (func);
1747 our_pred->side_effects = true;
1748 our_pred->no_default_print = true;
1749 our_pred->args.printf_vec.stream = fp;
1750 segmentp = &our_pred->args.printf_vec.segment;
1751 *segmentp = NULL;
1753 for (scan = format; *scan; scan++)
1755 if (*scan == '\\')
1757 scan2 = scan + 1;
1758 if (*scan2 >= '0' && *scan2 <= '7')
1760 register int n, i;
1762 for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
1763 i++, scan2++)
1764 n = 8 * n + *scan2 - '0';
1765 scan2--;
1766 *scan = n;
1768 else
1770 switch (*scan2)
1772 case 'a':
1773 *scan = 7;
1774 break;
1775 case 'b':
1776 *scan = '\b';
1777 break;
1778 case 'c':
1779 make_segment (segmentp, format, scan - format, KIND_STOP);
1780 our_pred->need_stat = fprintf_stat_needed;
1781 return (true);
1782 case 'f':
1783 *scan = '\f';
1784 break;
1785 case 'n':
1786 *scan = '\n';
1787 break;
1788 case 'r':
1789 *scan = '\r';
1790 break;
1791 case 't':
1792 *scan = '\t';
1793 break;
1794 case 'v':
1795 *scan = '\v';
1796 break;
1797 case '\\':
1798 /* *scan = '\\'; * it already is */
1799 break;
1800 default:
1801 error (0, 0,
1802 _("warning: unrecognized escape `\\%c'"), *scan2);
1803 scan++;
1804 continue;
1807 segmentp = make_segment (segmentp, format, scan - format + 1,
1808 KIND_PLAIN);
1809 format = scan2 + 1; /* Move past the escape. */
1810 scan = scan2; /* Incremented immediately by `for'. */
1812 else if (*scan == '%')
1814 if (scan[1] == '%')
1816 segmentp = make_segment (segmentp, format, scan - format + 1,
1817 KIND_PLAIN);
1818 scan++;
1819 format = scan + 1;
1820 continue;
1822 /* Scan past flags, width and precision, to verify kind. */
1823 for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
1824 /* Do nothing. */ ;
1825 while (ISDIGIT (*scan2))
1826 scan2++;
1827 if (*scan2 == '.')
1828 for (scan2++; ISDIGIT (*scan2); scan2++)
1829 /* Do nothing. */ ;
1830 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2))
1832 segmentp = make_segment (segmentp, format, scan2 - format,
1833 (int) *scan2);
1834 scan = scan2;
1835 format = scan + 1;
1837 else if (strchr ("ACT", *scan2) && scan2[1])
1839 segmentp = make_segment (segmentp, format, scan2 - format,
1840 *scan2 | (scan2[1] << 8));
1841 scan = scan2 + 1;
1842 format = scan + 1;
1843 continue;
1845 else
1847 /* An unrecognized % escape. Print the char after the %. */
1848 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1849 *scan2);
1850 segmentp = make_segment (segmentp, format, scan - format,
1851 KIND_PLAIN);
1852 format = scan + 1;
1853 continue;
1858 if (scan > format)
1859 make_segment (segmentp, format, scan - format, KIND_PLAIN);
1860 our_pred->need_type = false;
1861 our_pred->need_stat = fprintf_stat_needed;
1862 return (true);
1865 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1866 from the text in FORMAT, which has length LEN.
1867 Return the address of the `next' pointer of the new segment. */
1869 static struct segment **
1870 make_segment (struct segment **segment, char *format, int len, int kind)
1872 char *fmt;
1874 *segment = (struct segment *) xmalloc (sizeof (struct segment));
1876 (*segment)->kind = kind;
1877 (*segment)->next = NULL;
1878 (*segment)->text_len = len;
1880 fmt = (*segment)->text = xmalloc (len + sizeof "d");
1881 strncpy (fmt, format, len);
1882 fmt += len;
1884 switch (kind & 0xff)
1886 case KIND_PLAIN: /* Plain text string, no % conversion. */
1887 case KIND_STOP: /* Terminate argument, no newline. */
1888 break;
1890 case 'a': /* atime in `ctime' format */
1891 case 'A': /* atime in user-specified strftime format */
1892 case 'c': /* ctime in `ctime' format */
1893 case 'C': /* ctime in user-specified strftime format */
1894 case 'F': /* filesystem type */
1895 case 'g': /* group name */
1896 case 'i': /* inode number */
1897 case 'l': /* object of symlink */
1898 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1899 case 's': /* size in bytes */
1900 case 't': /* mtime in `ctime' format */
1901 case 'T': /* mtime in user-specified strftime format */
1902 case 'u': /* user name */
1903 case 'y': /* file type */
1904 case 'Y': /* symlink pointed file type */
1905 fprintf_stat_needed = true;
1906 /* FALLTHROUGH */
1907 case 'f': /* basename of path */
1908 case 'h': /* leading directories part of path */
1909 case 'H': /* ARGV element file was found under */
1910 case 'p': /* pathname */
1911 case 'P': /* pathname with ARGV element stripped */
1912 *fmt++ = 's';
1913 break;
1915 /* Numeric items that one might expect to honour
1916 * #, 0, + flags but which do not.
1918 case 'G': /* GID number */
1919 case 'U': /* UID number */
1920 case 'b': /* size in 512-byte blocks */
1921 case 'D': /* Filesystem device on which the file exits */
1922 case 'k': /* size in 1K blocks */
1923 case 'n': /* number of links */
1924 fprintf_stat_needed = true;
1925 *fmt++ = 's';
1926 break;
1928 /* Numeric items that DO honour #, 0, + flags.
1930 case 'd': /* depth in search tree (0 = ARGV element) */
1931 *fmt++ = 'd';
1932 break;
1934 case 'm': /* mode as octal number (perms only) */
1935 *fmt++ = 'o';
1936 fprintf_stat_needed = true;
1937 break;
1939 *fmt = '\0';
1941 return (&(*segment)->next);
1944 static void
1945 check_path_safety(const char *action)
1947 const char *path = getenv("PATH");
1948 char *s;
1949 s = next_element(path, 1);
1950 while ((s = next_element ((char *) NULL, 1)) != NULL)
1952 if (0 == strcmp(s, "."))
1954 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)"),
1955 action);
1961 /* handles both exec and ok predicate */
1962 #if defined(NEW_EXEC)
1963 /* handles both exec and ok predicate */
1964 static boolean
1965 new_insert_exec_ok (const char *action,
1966 boolean (*func) (/* ??? */),
1967 char **argv, int *arg_ptr)
1969 int start, end; /* Indexes in ARGV of start & end of cmd. */
1970 int i; /* Index into cmd args */
1971 int saw_braces; /* True if previous arg was '{}'. */
1972 boolean allow_plus; /* True if + is a valid terminator */
1973 int brace_count; /* Number of instances of {}. */
1975 struct predicate *our_pred;
1976 struct exec_val *execp; /* Pointer for efficiency. */
1978 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1979 return (false);
1981 our_pred = insert_primary (func);
1982 our_pred->side_effects = true;
1983 our_pred->no_default_print = true;
1984 execp = &our_pred->args.exec_vec;
1986 if ((func != pred_okdir) && (func != pred_ok))
1987 allow_plus = true;
1988 else
1989 allow_plus = false;
1991 if ((func == pred_execdir) || (func == pred_okdir))
1993 options.ignore_readdir_race = false;
1994 check_path_safety(action);
1995 execp->use_current_dir = true;
1997 else
1999 execp->use_current_dir = false;
2002 our_pred->args.exec_vec.multiple = 0;
2004 /* Count the number of args with path replacements, up until the ';'.
2005 * Also figure out if the command is terminated by ";" or by "+".
2007 start = *arg_ptr;
2008 for (end = start, saw_braces=0, brace_count=0;
2009 (argv[end] != NULL)
2010 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2011 end++)
2013 /* For -exec and -execdir, "{} +" can terminate the command. */
2014 if ( allow_plus
2015 && argv[end][0] == '+' && argv[end][1] == 0
2016 && saw_braces)
2018 our_pred->args.exec_vec.multiple = 1;
2019 break;
2022 saw_braces = 0;
2023 if (strstr (argv[end], "{}"))
2025 saw_braces = 1;
2026 ++brace_count;
2028 if (0 == end && (func == pred_execdir || func == pred_okdir))
2030 /* The POSIX standard says that {} replacement should
2031 * occur even in the utility name. This is insecure
2032 * since it means we will be executing a command whose
2033 * name is chosen according to whatever find finds in
2034 * the filesystem. That can be influenced by an
2035 * attacker. Hence for -execdir and -okdir this is not
2036 * allowed. We can specify this as those options are
2037 * not defined by POSIX.
2039 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2044 /* Fail if no command given or no semicolon found. */
2045 if ((end == start) || (argv[end] == NULL))
2047 *arg_ptr = end;
2048 free(our_pred);
2049 return false;
2052 if (our_pred->args.exec_vec.multiple && brace_count > 1)
2055 const char *suffix;
2056 if (func == pred_execdir)
2057 suffix = "dir";
2058 else
2059 suffix = "";
2061 error(1, 0,
2062 _("Only one instance of {} is supported with -exec%s ... +"),
2063 suffix);
2066 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2067 bc_init_controlinfo(&execp->ctl);
2068 execp->ctl.exec_callback = launch;
2070 if (our_pred->args.exec_vec.multiple)
2072 /* "+" terminator, so we can just append our arguments after the
2073 * command and initial arguments.
2075 execp->replace_vec = NULL;
2076 execp->ctl.replace_pat = NULL;
2077 execp->ctl.rplen = 0;
2078 execp->ctl.lines_per_exec = 0; /* no limit */
2079 execp->ctl.args_per_exec = 0; /* no limit */
2081 /* remember how many arguments there are */
2082 execp->ctl.initial_argc = (end-start) - 1;
2084 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2085 bc_init_state(&execp->ctl, &execp->state, execp);
2087 /* Gather the initial arguments. Skip the {}. */
2088 for (i=start; i<end-1; ++i)
2090 bc_push_arg(&execp->ctl, &execp->state,
2091 argv[i], strlen(argv[i])+1,
2092 NULL, 0,
2096 else
2098 /* Semicolon terminator - more than one {} is supported, so we
2099 * have to do brace-replacement.
2101 execp->num_args = end - start;
2103 execp->ctl.replace_pat = "{}";
2104 execp->ctl.rplen = strlen(execp->ctl.replace_pat);
2105 execp->ctl.lines_per_exec = 0; /* no limit */
2106 execp->ctl.args_per_exec = 0; /* no limit */
2107 execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
2110 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2111 bc_init_state(&execp->ctl, &execp->state, execp);
2113 /* Remember the (pre-replacement) arguments for later. */
2114 for (i=0; i<execp->num_args; ++i)
2116 execp->replace_vec[i] = argv[i+start];
2120 if (argv[end] == NULL)
2121 *arg_ptr = end;
2122 else
2123 *arg_ptr = end + 1;
2125 return true;
2127 #else
2128 /* handles both exec and ok predicate */
2129 static boolean
2130 old_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
2132 int start, end; /* Indexes in ARGV of start & end of cmd. */
2133 int num_paths; /* Number of args with path replacements. */
2134 int path_pos; /* Index in array of path replacements. */
2135 int vec_pos; /* Index in array of args. */
2136 struct predicate *our_pred;
2137 struct exec_val *execp; /* Pointer for efficiency. */
2139 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2140 return (false);
2142 /* Count the number of args with path replacements, up until the ';'. */
2143 start = *arg_ptr;
2144 for (end = start, num_paths = 0;
2145 (argv[end] != NULL)
2146 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2147 end++)
2148 if (strstr (argv[end], "{}"))
2149 num_paths++;
2150 /* Fail if no command given or no semicolon found. */
2151 if ((end == start) || (argv[end] == NULL))
2153 *arg_ptr = end;
2154 return (false);
2157 our_pred = insert_primary (func);
2158 our_pred->side_effects = true;
2159 our_pred->no_default_print = true;
2160 execp = &our_pred->args.exec_vec;
2161 execp->usercontext = our_pred;
2162 execp->use_current_dir = false;
2163 execp->paths =
2164 (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1));
2165 execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1));
2166 /* Record the positions of all args, and the args with path replacements. */
2167 for (end = start, path_pos = vec_pos = 0;
2168 (argv[end] != NULL)
2169 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2170 end++)
2172 register char *p;
2174 execp->paths[path_pos].count = 0;
2175 for (p = argv[end]; *p; ++p)
2176 if (p[0] == '{' && p[1] == '}')
2178 execp->paths[path_pos].count++;
2179 ++p;
2181 if (execp->paths[path_pos].count)
2183 execp->paths[path_pos].offset = vec_pos;
2184 execp->paths[path_pos].origarg = argv[end];
2185 path_pos++;
2187 execp->vec[vec_pos++] = argv[end];
2189 execp->paths[path_pos].offset = -1;
2190 execp->vec[vec_pos] = NULL;
2192 if (argv[end] == NULL)
2193 *arg_ptr = end;
2194 else
2195 *arg_ptr = end + 1;
2196 return (true);
2198 #endif
2202 static boolean
2203 insert_exec_ok (const char *action,
2204 boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
2206 #if defined(NEW_EXEC)
2207 return new_insert_exec_ok(action, func, argv, arg_ptr);
2208 #else
2209 return old_insert_exec_ok(func, argv, arg_ptr);
2210 #endif
2215 /* Get a number of days and comparison type.
2216 STR is the ASCII representation.
2217 Set *NUM_DAYS to the number of days, taken as being from
2218 the current moment (or possibly midnight). Thus the sense of the
2219 comparison type appears to be reversed.
2220 Set *COMP_TYPE to the kind of comparison that is requested.
2222 Return true if all okay, false if input error.
2224 Used by -atime, -ctime and -mtime (parsers) to
2225 get the appropriate information for a time predicate processor. */
2227 static boolean
2228 get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
2230 boolean r = get_num (str, num_days, comp_type);
2231 if (r)
2232 switch (*comp_type)
2234 case COMP_LT: *comp_type = COMP_GT; break;
2235 case COMP_GT: *comp_type = COMP_LT; break;
2236 default: break;
2238 return r;
2241 /* Insert a time predicate PRED.
2242 ARGV is a pointer to the argument array.
2243 ARG_PTR is a pointer to an index into the array, incremented if
2244 all went well.
2246 Return true if input is valid, false if not.
2248 A new predicate node is assigned, along with an argument node
2249 obtained with malloc.
2251 Used by -atime, -ctime, and -mtime parsers. */
2253 static boolean
2254 insert_time (char **argv, int *arg_ptr, PFB pred)
2256 struct predicate *our_pred;
2257 uintmax_t num_days;
2258 enum comparison_type c_type;
2259 time_t t;
2261 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2262 return (false);
2263 if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
2264 return (false);
2266 /* Figure out the timestamp value we are looking for. */
2267 t = ( options.cur_day_start - num_days * DAYSECS
2268 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2270 if (1)
2272 /* We introduce a scope in which 'val' can be declared, for the
2273 * benefit of compilers that are really C89 compilers
2274 * which support intmax_t because config.h #defines it
2276 intmax_t val = ( (intmax_t)options.cur_day_start - num_days * DAYSECS
2277 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2278 t = val;
2280 /* Check for possibility of an overflow */
2281 if ( (intmax_t)t != val )
2283 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv[*arg_ptr]);
2287 our_pred = insert_primary (pred);
2288 our_pred->args.info.kind = c_type;
2289 our_pred->args.info.negative = t < 0;
2290 our_pred->args.info.l_val = t;
2291 (*arg_ptr)++;
2292 #ifdef DEBUG
2293 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2294 fprintf (stderr, " type: %s %s ",
2295 (c_type == COMP_GT) ? "gt" :
2296 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2297 (c_type == COMP_GT) ? " >" :
2298 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
2299 t = our_pred->args.info.l_val;
2300 fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2301 if (c_type == COMP_EQ)
2303 t = our_pred->args.info.l_val += DAYSECS;
2304 fprintf (stderr, " < %ju %s",
2305 (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2306 our_pred->args.info.l_val -= DAYSECS;
2308 #endif /* DEBUG */
2309 return (true);
2312 /* Get a number with comparision information.
2313 The sense of the comparision information is 'normal'; that is,
2314 '+' looks for a count > than the number and '-' less than.
2316 STR is the ASCII representation of the number.
2317 Set *NUM to the number.
2318 Set *COMP_TYPE to the kind of comparison that is requested.
2320 Return true if all okay, false if input error. */
2322 static boolean
2323 get_num (char *str, uintmax_t *num, enum comparison_type *comp_type)
2325 if (str == NULL)
2326 return (false);
2327 switch (str[0])
2329 case '+':
2330 *comp_type = COMP_GT;
2331 str++;
2332 break;
2333 case '-':
2334 *comp_type = COMP_LT;
2335 str++;
2336 break;
2337 default:
2338 *comp_type = COMP_EQ;
2339 break;
2342 return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK;
2345 /* Insert a number predicate.
2346 ARGV is a pointer to the argument array.
2347 *ARG_PTR is an index into ARGV, incremented if all went well.
2348 *PRED is the predicate processor to insert.
2350 Return true if input is valid, false if error.
2352 A new predicate node is assigned, along with an argument node
2353 obtained with malloc.
2355 Used by -inum and -links parsers. */
2357 static boolean
2358 insert_num (char **argv, int *arg_ptr, PFB pred)
2360 struct predicate *our_pred;
2361 uintmax_t num;
2362 enum comparison_type c_type;
2364 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2365 return (false);
2366 if (!get_num (argv[*arg_ptr], &num, &c_type))
2367 return (false);
2368 our_pred = insert_primary (pred);
2369 our_pred->args.info.kind = c_type;
2370 our_pred->args.info.l_val = num;
2371 (*arg_ptr)++;
2372 #ifdef DEBUG
2373 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2374 fprintf (stderr, " type: %s %s ",
2375 (c_type == COMP_GT) ? "gt" :
2376 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2377 (c_type == COMP_GT) ? " >" :
2378 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
2379 fprintf (stderr, "%ju\n", our_pred->args.info.l_val);
2380 #endif /* DEBUG */
2381 return (true);
2384 static FILE *
2385 open_output_file (char *path)
2387 FILE *f;
2389 if (!strcmp (path, "/dev/stderr"))
2390 return (stderr);
2391 else if (!strcmp (path, "/dev/stdout"))
2392 return (stdout);
2393 f = fopen (path, "w");
2394 if (f == NULL)
2395 error (1, errno, "%s", path);
2396 return (f);