distcheck
[findutils.git] / find / parser.c
blob1790b03f6c233bc3fe4f107d7b3ac635896e4f90
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.
20 #include "defs.h"
21 #include <ctype.h>
22 #include <pwd.h>
23 #include <grp.h>
24 #include <fnmatch.h>
25 #include "../gnulib/lib/modechange.h"
26 #include "modetype.h"
27 #include "../gnulib/lib/xstrtol.h"
28 #include "../gnulib/lib/xalloc.h"
31 #if ENABLE_NLS
32 # include <libintl.h>
33 # define _(Text) gettext (Text)
34 #else
35 # define _(Text) Text
36 #endif
37 #ifdef gettext_noop
38 # define N_(String) gettext_noop (String)
39 #else
40 /* See locate.c for explanation as to why not use (String) */
41 # define N_(String) String
42 #endif
44 #if !defined (isascii) || defined (STDC_HEADERS)
45 #ifdef isascii
46 #undef isascii
47 #endif
48 #define isascii(c) 1
49 #endif
51 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
52 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
54 #ifndef HAVE_ENDGRENT
55 #define endgrent()
56 #endif
57 #ifndef HAVE_ENDPWENT
58 #define endpwent()
59 #endif
61 static boolean parse_amin PARAMS((char *argv[], int *arg_ptr));
62 static boolean parse_and PARAMS((char *argv[], int *arg_ptr));
63 static boolean parse_anewer PARAMS((char *argv[], int *arg_ptr));
64 static boolean parse_atime PARAMS((char *argv[], int *arg_ptr));
65 boolean parse_close PARAMS((char *argv[], int *arg_ptr));
66 static boolean parse_cmin PARAMS((char *argv[], int *arg_ptr));
67 static boolean parse_cnewer PARAMS((char *argv[], int *arg_ptr));
68 static boolean parse_comma PARAMS((char *argv[], int *arg_ptr));
69 static boolean parse_ctime PARAMS((char *argv[], int *arg_ptr));
70 static boolean parse_daystart PARAMS((char *argv[], int *arg_ptr));
71 static boolean parse_delete PARAMS((char *argv[], int *arg_ptr));
72 static boolean parse_d PARAMS((char *argv[], int *arg_ptr));
73 static boolean parse_depth PARAMS((char *argv[], int *arg_ptr));
74 static boolean parse_empty PARAMS((char *argv[], int *arg_ptr));
75 static boolean parse_exec PARAMS((char *argv[], int *arg_ptr));
76 static boolean parse_false PARAMS((char *argv[], int *arg_ptr));
77 static boolean parse_fls PARAMS((char *argv[], int *arg_ptr));
78 static boolean parse_fprintf PARAMS((char *argv[], int *arg_ptr));
79 static boolean parse_follow PARAMS((char *argv[], int *arg_ptr));
80 static boolean parse_fprint PARAMS((char *argv[], int *arg_ptr));
81 static boolean parse_fprint0 PARAMS((char *argv[], int *arg_ptr));
82 static boolean parse_fstype PARAMS((char *argv[], int *arg_ptr));
83 static boolean parse_gid PARAMS((char *argv[], int *arg_ptr));
84 static boolean parse_group PARAMS((char *argv[], int *arg_ptr));
85 static boolean parse_help PARAMS((char *argv[], int *arg_ptr));
86 static boolean parse_ilname PARAMS((char *argv[], int *arg_ptr));
87 static boolean parse_iname PARAMS((char *argv[], int *arg_ptr));
88 static boolean parse_inum PARAMS((char *argv[], int *arg_ptr));
89 static boolean parse_ipath PARAMS((char *argv[], int *arg_ptr));
90 static boolean parse_iregex PARAMS((char *argv[], int *arg_ptr));
91 static boolean parse_iwholename PARAMS((char *argv[], int *arg_ptr));
92 static boolean parse_links PARAMS((char *argv[], int *arg_ptr));
93 static boolean parse_lname PARAMS((char *argv[], int *arg_ptr));
94 static boolean parse_ls PARAMS((char *argv[], int *arg_ptr));
95 static boolean parse_maxdepth PARAMS((char *argv[], int *arg_ptr));
96 static boolean parse_mindepth PARAMS((char *argv[], int *arg_ptr));
97 static boolean parse_mmin PARAMS((char *argv[], int *arg_ptr));
98 static boolean parse_mtime PARAMS((char *argv[], int *arg_ptr));
99 static boolean parse_name PARAMS((char *argv[], int *arg_ptr));
100 static boolean parse_negate PARAMS((char *argv[], int *arg_ptr));
101 static boolean parse_newer PARAMS((char *argv[], int *arg_ptr));
102 static boolean parse_noleaf PARAMS((char *argv[], int *arg_ptr));
103 static boolean parse_nogroup PARAMS((char *argv[], int *arg_ptr));
104 static boolean parse_nouser PARAMS((char *argv[], int *arg_ptr));
105 static boolean parse_nowarn PARAMS((char *argv[], int *arg_ptr));
106 static boolean parse_ok PARAMS((char *argv[], int *arg_ptr));
107 boolean parse_open PARAMS((char *argv[], int *arg_ptr));
108 static boolean parse_or PARAMS((char *argv[], int *arg_ptr));
109 static boolean parse_path PARAMS((char *argv[], int *arg_ptr));
110 static boolean parse_perm PARAMS((char *argv[], int *arg_ptr));
111 boolean parse_print PARAMS((char *argv[], int *arg_ptr));
112 static boolean parse_print0 PARAMS((char *argv[], int *arg_ptr));
113 static boolean parse_printf PARAMS((char *argv[], int *arg_ptr));
114 static boolean parse_prune PARAMS((char *argv[], int *arg_ptr));
115 static boolean parse_regex PARAMS((char *argv[], int *arg_ptr));
116 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case));
117 static boolean parse_samefile PARAMS((char *argv[], int *arg_ptr));
118 static boolean parse_size PARAMS((char *argv[], int *arg_ptr));
119 static boolean parse_true PARAMS((char *argv[], int *arg_ptr));
120 static boolean parse_type PARAMS((char *argv[], int *arg_ptr));
121 static boolean parse_uid PARAMS((char *argv[], int *arg_ptr));
122 static boolean parse_used PARAMS((char *argv[], int *arg_ptr));
123 static boolean parse_user PARAMS((char *argv[], int *arg_ptr));
124 static boolean parse_version PARAMS((char *argv[], int *arg_ptr));
125 static boolean parse_wholename PARAMS((char *argv[], int *arg_ptr));
126 static boolean parse_xdev PARAMS((char *argv[], int *arg_ptr));
127 static boolean parse_ignore_race PARAMS((char *argv[], int *arg_ptr));
128 static boolean parse_noignore_race PARAMS((char *argv[], int *arg_ptr));
129 static boolean parse_warn PARAMS((char *argv[], int *arg_ptr));
130 static boolean parse_xtype PARAMS((char *argv[], int *arg_ptr));
131 static boolean parse_quit PARAMS((char *argv[], int *arg_ptr));
133 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case));
134 static boolean insert_type PARAMS((char *argv[], int *arg_ptr, boolean (*which_pred )()));
135 static boolean insert_fprintf PARAMS((FILE *fp, boolean (*func )(), char *argv[], int *arg_ptr));
136 static struct segment **make_segment PARAMS((struct segment **segment, char *format, int len, int kind));
137 static boolean insert_exec_ok PARAMS((boolean (*func )(), char *argv[], int *arg_ptr));
138 static boolean get_num_days PARAMS((char *str, uintmax_t *num_days, enum comparison_type *comp_type));
139 static boolean insert_time PARAMS((char *argv[], int *arg_ptr, PFB pred));
140 static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type));
141 static boolean insert_num PARAMS((char *argv[], int *arg_ptr, PFB pred));
142 static FILE *open_output_file PARAMS((char *path));
144 #ifdef DEBUG
145 char *find_pred_name PARAMS((PFB pred_func));
146 #endif /* DEBUG */
150 enum arg_type
152 ARG_OPTION, /* regular options like -maxdepth */
153 ARG_POSITIONAL_OPTION, /* options whose position is important (-follow) */
154 ARG_TEST, /* a like -name */
155 ARG_PUNCTUATION, /* like -o or ( */
156 ARG_ACTION /* like -print */
160 struct parser_table
162 enum arg_type type;
163 char *parser_name;
164 PFB parser_func;
167 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
168 If they are in some Unix versions of find, they are marked `Unix'. */
170 static struct parser_table const parse_table[] =
172 {ARG_PUNCTUATION, "!", parse_negate},
173 {ARG_PUNCTUATION, "not", parse_negate}, /* GNU */
174 {ARG_PUNCTUATION, "(", parse_open},
175 {ARG_PUNCTUATION, ")", parse_close},
176 {ARG_PUNCTUATION, ",", parse_comma}, /* GNU */
177 {ARG_PUNCTUATION, "a", parse_and},
178 {ARG_TEST, "amin", parse_amin}, /* GNU */
179 {ARG_PUNCTUATION, "and", parse_and}, /* GNU */
180 {ARG_TEST, "anewer", parse_anewer}, /* GNU */
181 {ARG_TEST, "atime", parse_atime},
182 {ARG_TEST, "cmin", parse_cmin}, /* GNU */
183 {ARG_TEST, "cnewer", parse_cnewer}, /* GNU */
184 #ifdef UNIMPLEMENTED_UNIX
185 /* It's pretty ugly for find to know about archive formats.
186 Plus what it could do with cpio archives is very limited.
187 Better to leave it out. */
188 {ARG_UNIMPLEMENTED, "cpio", parse_cpio}, /* Unix */
189 #endif
190 {ARG_TEST, "ctime", parse_ctime},
191 {ARG_POSITIONAL_OPTION, "daystart", parse_daystart}, /* GNU */
192 {ARG_ACTION, "delete", parse_delete}, /* GNU, Mac OS, FreeBSD */
193 {ARG_OPTION, "d", parse_d}, /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
194 {ARG_OPTION, "depth", parse_depth},
195 {ARG_TEST, "empty", parse_empty}, /* GNU */
196 {ARG_ACTION, "exec", parse_exec},
197 {ARG_TEST, "false", parse_false}, /* GNU */
198 {ARG_ACTION, "fls", parse_fls}, /* GNU */
199 {ARG_POSITIONAL_OPTION, "follow", parse_follow}, /* GNU, Unix */
200 {ARG_ACTION, "fprint", parse_fprint}, /* GNU */
201 {ARG_ACTION, "fprint0", parse_fprint0}, /* GNU */
202 {ARG_ACTION, "fprintf", parse_fprintf}, /* GNU */
203 {ARG_TEST, "fstype", parse_fstype}, /* GNU, Unix */
204 {ARG_TEST, "gid", parse_gid}, /* GNU */
205 {ARG_TEST, "group", parse_group},
206 {ARG_TEST, "help", parse_help}, /* GNU */
207 {ARG_TEST, "-help", parse_help}, /* GNU */
208 {ARG_OPTION, "ignore_readdir_race", parse_ignore_race}, /* GNU */
209 {ARG_TEST, "ilname", parse_ilname}, /* GNU */
210 {ARG_TEST, "iname", parse_iname}, /* GNU */
211 {ARG_TEST, "inum", parse_inum}, /* GNU, Unix */
212 {ARG_TEST, "ipath", parse_ipath}, /* GNU, deprecated in favour of iwholename */
213 {ARG_TEST, "iregex", parse_iregex}, /* GNU */
214 {ARG_TEST, "iwholename", parse_iwholename}, /* GNU */
215 {ARG_TEST, "links", parse_links},
216 {ARG_TEST, "lname", parse_lname}, /* GNU */
217 {ARG_ACTION, "ls", parse_ls}, /* GNU, Unix */
218 {ARG_OPTION, "maxdepth", parse_maxdepth}, /* GNU */
219 {ARG_OPTION, "mindepth", parse_mindepth}, /* GNU */
220 {ARG_TEST, "mmin", parse_mmin}, /* GNU */
221 {ARG_OPTION, "mount", parse_xdev}, /* Unix */
222 {ARG_TEST, "mtime", parse_mtime},
223 {ARG_TEST, "name", parse_name},
224 #ifdef UNIMPLEMENTED_UNIX
225 {ARG_UNIMPLEMENTED, "ncpio", parse_ncpio}, /* Unix */
226 #endif
227 {ARG_TEST, "newer", parse_newer},
228 {ARG_OPTION, "noleaf", parse_noleaf}, /* GNU */
229 {ARG_TEST, "nogroup", parse_nogroup},
230 {ARG_TEST, "nouser", parse_nouser},
231 {ARG_OPTION, "noignore_readdir_race", parse_noignore_race},/* GNU */
232 {ARG_OPTION, "nowarn", parse_nowarn}, /* GNU */
233 {ARG_PUNCTUATION, "o", parse_or},
234 {ARG_PUNCTUATION, "or", parse_or}, /* GNU */
235 {ARG_ACTION, "ok", parse_ok},
236 {ARG_TEST, "path", parse_path}, /* GNU, HP-UX, GNU prefers wholename */
237 {ARG_TEST, "perm", parse_perm},
238 {ARG_ACTION, "print", parse_print},
239 {ARG_ACTION, "print0", parse_print0}, /* GNU */
240 {ARG_ACTION, "printf", parse_printf}, /* GNU */
241 {ARG_TEST, "prune", parse_prune},
242 {ARG_ACTION, "quit", parse_quit}, /* GNU */
243 {ARG_TEST, "regex", parse_regex}, /* GNU */
244 {ARG_TEST, "samefile", parse_samefile}, /* GNU */
245 {ARG_TEST, "size", parse_size},
246 {ARG_TEST, "true", parse_true}, /* GNU */
247 {ARG_TEST, "type", parse_type},
248 {ARG_TEST, "uid", parse_uid}, /* GNU */
249 {ARG_TEST, "used", parse_used}, /* GNU */
250 {ARG_TEST, "user", parse_user},
251 {ARG_TEST, "version", parse_version}, /* GNU */
252 {ARG_TEST, "-version", parse_version}, /* GNU */
253 {ARG_OPTION, "warn", parse_warn}, /* GNU */
254 {ARG_TEST, "wholename", parse_wholename}, /* GNU, replaces -path */
255 {ARG_OPTION, "xdev", parse_xdev},
256 {ARG_TEST, "xtype", parse_xtype}, /* GNU */
257 {0, 0, 0}
261 static const char *first_nonoption_arg = NULL;
263 /* Return a pointer to the parser function to invoke for predicate
264 SEARCH_NAME.
265 Return NULL if SEARCH_NAME is not a valid predicate name. */
268 find_parser (char *search_name)
270 int i;
271 const char *original_arg = search_name;
273 if (*search_name == '-')
274 search_name++;
275 for (i = 0; parse_table[i].parser_name != 0; i++)
277 if (strcmp (parse_table[i].parser_name, search_name) == 0)
279 /* If this is an option, but we have already had a
280 * non-option argument, the user may be under the
281 * impression that the behaviour of the option
282 * argument is conditional on some preceding
283 * tests. This might typically be the case with,
284 * for example, -maxdepth.
286 * The options -daystart and -follow are exempt
287 * from this treatment, since their positioning
288 * in the command line does have an effect on
289 * subsequent tests but not previous ones. That
290 * might be intentional on the part of the user.
292 if (parse_table[i].type != ARG_POSITIONAL_OPTION)
294 /* Something other than -follow/-daystart.
295 * If this is an option, check if it followed
296 * a non-option and if so, issue a warning.
298 if (parse_table[i].type == ARG_OPTION)
300 if ((first_nonoption_arg != NULL)
301 && options.warnings )
303 /* option which folows a non-option */
304 error (0, 0,
305 _("warning: you have specified the %s "
306 "option after a non-option argument %s, "
307 "but options are not positional (%s affects "
308 "tests specified before it as well as those "
309 "specified after it). Please specify options "
310 "before other arguments.\n"),
311 original_arg,
312 first_nonoption_arg,
313 original_arg);
316 else
318 /* Not an option or a positional option,
319 * so remember we've seen it in order to
320 * use it in a possible future warning message.
322 if (first_nonoption_arg == NULL)
324 first_nonoption_arg = original_arg;
329 return (parse_table[i].parser_func);
332 return NULL;
335 /* The parsers are responsible to continue scanning ARGV for
336 their arguments. Each parser knows what is and isn't
337 allowed for itself.
339 ARGV is the argument array.
340 *ARG_PTR is the index to start at in ARGV,
341 updated to point beyond the last element consumed.
343 The predicate structure is updated with the new information. */
345 static boolean
346 parse_amin (char **argv, int *arg_ptr)
348 struct predicate *our_pred;
349 uintmax_t num;
350 enum comparison_type c_type;
351 time_t t;
353 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
354 return (false);
355 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
356 return (false);
357 t = options.cur_day_start + DAYSECS - num * 60;
358 our_pred = insert_primary (pred_amin);
359 our_pred->args.info.kind = c_type;
360 our_pred->args.info.negative = t < 0;
361 our_pred->args.info.l_val = t;
362 (*arg_ptr)++;
363 return (true);
366 static boolean
367 parse_and (char **argv, int *arg_ptr)
369 struct predicate *our_pred;
371 (void) argv;
372 (void) arg_ptr;
374 our_pred = get_new_pred ();
375 our_pred->pred_func = pred_and;
376 #ifdef DEBUG
377 our_pred->p_name = find_pred_name (pred_and);
378 #endif /* DEBUG */
379 our_pred->p_type = BI_OP;
380 our_pred->p_prec = AND_PREC;
381 our_pred->need_stat = false;
382 return (true);
385 static boolean
386 parse_anewer (char **argv, int *arg_ptr)
388 struct predicate *our_pred;
389 struct stat stat_newer;
391 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
392 return (false);
393 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
394 error (1, errno, "%s", argv[*arg_ptr]);
395 our_pred = insert_primary (pred_anewer);
396 our_pred->args.time = stat_newer.st_mtime;
397 (*arg_ptr)++;
398 return (true);
401 static boolean
402 parse_atime (char **argv, int *arg_ptr)
404 return (insert_time (argv, arg_ptr, pred_atime));
407 boolean
408 parse_close (char **argv, int *arg_ptr)
410 struct predicate *our_pred;
412 (void) argv;
413 (void) arg_ptr;
415 our_pred = get_new_pred ();
416 our_pred->pred_func = pred_close;
417 #ifdef DEBUG
418 our_pred->p_name = find_pred_name (pred_close);
419 #endif /* DEBUG */
420 our_pred->p_type = CLOSE_PAREN;
421 our_pred->p_prec = NO_PREC;
422 our_pred->need_stat = false;
423 return (true);
426 static boolean
427 parse_cmin (char **argv, int *arg_ptr)
429 struct predicate *our_pred;
430 uintmax_t num;
431 enum comparison_type c_type;
432 time_t t;
434 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
435 return (false);
436 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
437 return (false);
438 t = options.cur_day_start + DAYSECS - num * 60;
439 our_pred = insert_primary (pred_cmin);
440 our_pred->args.info.kind = c_type;
441 our_pred->args.info.negative = t < 0;
442 our_pred->args.info.l_val = t;
443 (*arg_ptr)++;
444 return (true);
447 static boolean
448 parse_cnewer (char **argv, int *arg_ptr)
450 struct predicate *our_pred;
451 struct stat stat_newer;
453 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
454 return (false);
455 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
456 error (1, errno, "%s", argv[*arg_ptr]);
457 our_pred = insert_primary (pred_cnewer);
458 our_pred->args.time = stat_newer.st_mtime;
459 (*arg_ptr)++;
460 return (true);
463 static boolean
464 parse_comma (char **argv, int *arg_ptr)
466 struct predicate *our_pred;
468 (void) argv;
469 (void) arg_ptr;
471 our_pred = get_new_pred ();
472 our_pred->pred_func = pred_comma;
473 #ifdef DEBUG
474 our_pred->p_name = find_pred_name (pred_comma);
475 #endif /* DEBUG */
476 our_pred->p_type = BI_OP;
477 our_pred->p_prec = COMMA_PREC;
478 our_pred->need_stat = false;
479 return (true);
482 static boolean
483 parse_ctime (char **argv, int *arg_ptr)
485 return (insert_time (argv, arg_ptr, pred_ctime));
488 static boolean
489 parse_daystart (char **argv, int *arg_ptr)
491 struct tm *local;
493 (void) argv;
494 (void) arg_ptr;
496 if (options.full_days == false)
498 options.cur_day_start += DAYSECS;
499 local = localtime (&options.cur_day_start);
500 options.cur_day_start -= (local
501 ? (local->tm_sec + local->tm_min * 60
502 + local->tm_hour * 3600)
503 : options.cur_day_start % DAYSECS);
504 options.full_days = true;
506 return true;
509 static boolean
510 parse_delete (argv, arg_ptr)
511 char *argv[];
512 int *arg_ptr;
514 struct predicate *our_pred;
515 (void) argv;
516 (void) arg_ptr;
518 our_pred = insert_primary (pred_delete);
519 our_pred->side_effects = true;
520 our_pred->no_default_print = true;
521 /* -delete implies -depth */
522 options.do_dir_first = false;
523 return (true);
526 static boolean
527 parse_depth (char **argv, int *arg_ptr)
529 (void) argv;
530 (void) arg_ptr;
532 options.do_dir_first = false;
533 return (true);
536 static boolean
537 parse_d (char **argv, int *arg_ptr)
539 (void) argv;
540 (void) arg_ptr;
542 if (options.warnings)
544 error (0, 0,
545 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
547 return parse_depth(argv, arg_ptr);
550 static boolean
551 parse_empty (char **argv, int *arg_ptr)
553 (void) argv;
554 (void) arg_ptr;
556 insert_primary (pred_empty);
557 return (true);
560 static boolean
561 parse_exec (char **argv, int *arg_ptr)
563 return (insert_exec_ok (pred_exec, argv, arg_ptr));
566 static boolean
567 parse_false (char **argv, int *arg_ptr)
569 struct predicate *our_pred;
571 (void) argv;
572 (void) arg_ptr;
574 our_pred = insert_primary (pred_false);
575 our_pred->need_stat = false;
576 return (true);
579 static boolean
580 parse_fls (char **argv, int *arg_ptr)
582 struct predicate *our_pred;
584 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
585 return (false);
586 our_pred = insert_primary (pred_fls);
587 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
588 our_pred->side_effects = true;
589 our_pred->no_default_print = true;
590 (*arg_ptr)++;
591 return (true);
594 static boolean
595 parse_fprintf (char **argv, int *arg_ptr)
597 FILE *fp;
599 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
600 return (false);
601 if (argv[*arg_ptr + 1] == NULL)
603 /* Ensure we get "missing arg" message, not "invalid arg". */
604 (*arg_ptr)++;
605 return (false);
607 fp = open_output_file (argv[*arg_ptr]);
608 (*arg_ptr)++;
609 return (insert_fprintf (fp, pred_fprintf, argv, arg_ptr));
612 static boolean
613 parse_follow (char **argv, int *arg_ptr)
615 (void) argv;
616 (void) arg_ptr;
618 set_follow_state(SYMLINK_ALWAYS_DEREF);
619 return true;
622 static boolean
623 parse_fprint (char **argv, int *arg_ptr)
625 struct predicate *our_pred;
627 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
628 return (false);
629 our_pred = insert_primary (pred_fprint);
630 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
631 our_pred->side_effects = true;
632 our_pred->no_default_print = true;
633 our_pred->need_stat = false;
634 (*arg_ptr)++;
635 return (true);
638 static boolean
639 parse_fprint0 (char **argv, int *arg_ptr)
641 struct predicate *our_pred;
643 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
644 return (false);
645 our_pred = insert_primary (pred_fprint0);
646 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
647 our_pred->side_effects = true;
648 our_pred->no_default_print = true;
649 our_pred->need_stat = false;
650 (*arg_ptr)++;
651 return (true);
654 static boolean
655 parse_fstype (char **argv, int *arg_ptr)
657 struct predicate *our_pred;
659 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
660 return (false);
661 our_pred = insert_primary (pred_fstype);
662 our_pred->args.str = argv[*arg_ptr];
663 (*arg_ptr)++;
664 return (true);
667 static boolean
668 parse_gid (char **argv, int *arg_ptr)
670 return (insert_num (argv, arg_ptr, pred_gid));
673 static boolean
674 parse_group (char **argv, int *arg_ptr)
676 struct group *cur_gr;
677 struct predicate *our_pred;
678 gid_t gid;
679 int gid_len;
681 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
682 return (false);
683 cur_gr = getgrnam (argv[*arg_ptr]);
684 endgrent ();
685 if (cur_gr != NULL)
686 gid = cur_gr->gr_gid;
687 else
689 gid_len = strspn (argv[*arg_ptr], "0123456789");
690 if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0'))
691 return (false);
692 gid = atoi (argv[*arg_ptr]);
694 our_pred = insert_primary (pred_group);
695 our_pred->args.gid = gid;
696 (*arg_ptr)++;
697 return (true);
700 static boolean
701 parse_help (char **argv, int *arg_ptr)
703 (void) argv;
704 (void) arg_ptr;
706 printf (_("\
707 Usage: %s [path...] [expression]\n"), program_name);
708 puts (_("\n\
709 default path is the current directory; default expression is -print\n\
710 expression may consist of: operators, options, tests, and actions:\n"));
711 puts (_("\
712 operators (decreasing precedence; -and is implicit where no others are given):\n\
713 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
714 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
715 puts (_("\
716 positional options (always true): -daystart -follow\n\
717 normal options (always true, specified before other expressions):\n\
718 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
719 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
720 puts (_("\
721 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
722 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
723 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
724 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
725 puts (_("\
726 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
727 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
728 -used N -user NAME -xtype [bcdpfls]\n"));
729 puts (_("\
730 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
731 -fls FILE -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls -delete\n\
732 -quit\n"));
733 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
734 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
735 email to <bug-findutils@gnu.org>."));
736 exit (0);
739 static boolean
740 parse_ilname (char **argv, int *arg_ptr)
742 struct predicate *our_pred;
744 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
745 return (false);
746 our_pred = insert_primary (pred_ilname);
747 our_pred->args.str = argv[*arg_ptr];
748 (*arg_ptr)++;
749 return (true);
753 /* sanity check the fnmatch() function to make sure
754 * it really is the GNU version.
756 static boolean
757 fnmatch_sanitycheck()
759 /* fprintf(stderr, "Performing find sanity check..."); */
760 if (0 != fnmatch("foo", "foo", 0)
761 || 0 == fnmatch("Foo", "foo", 0)
762 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
764 error (1, 0, _("sanity check of the fnmatch() library function failed."));
765 /* fprintf(stderr, "FAILED\n"); */
766 return false;
769 /* fprintf(stderr, "OK\n"); */
770 return true;
775 static boolean
776 parse_iname (char **argv, int *arg_ptr)
778 struct predicate *our_pred;
780 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
781 return (false);
783 fnmatch_sanitycheck();
785 our_pred = insert_primary (pred_iname);
786 our_pred->need_stat = false;
787 our_pred->args.str = argv[*arg_ptr];
788 (*arg_ptr)++;
789 return (true);
792 static boolean
793 parse_inum (char **argv, int *arg_ptr)
795 return (insert_num (argv, arg_ptr, pred_inum));
798 /* -ipath is deprecated (at RMS's request) in favour of
799 * -iwholename. See the node "GNU Manuals" in standards.texi
800 * for the rationale for this (basically, GNU prefers the use
801 * of the phrase "file name" to "path name"
803 static boolean
804 parse_ipath (char **argv, int *arg_ptr)
806 error (0, 0,
807 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
809 return parse_iwholename(argv, arg_ptr);
812 static boolean
813 parse_iwholename (char **argv, int *arg_ptr)
815 struct predicate *our_pred;
817 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
818 return (false);
820 fnmatch_sanitycheck();
822 our_pred = insert_primary (pred_ipath);
823 our_pred->need_stat = false;
824 our_pred->args.str = argv[*arg_ptr];
825 (*arg_ptr)++;
826 return (true);
829 static boolean
830 parse_iregex (char **argv, int *arg_ptr)
832 return insert_regex (argv, arg_ptr, true);
835 static boolean
836 parse_links (char **argv, int *arg_ptr)
838 return (insert_num (argv, arg_ptr, pred_links));
841 static boolean
842 parse_lname (char **argv, int *arg_ptr)
844 struct predicate *our_pred;
846 (void) argv;
847 (void) arg_ptr;
849 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
850 return (false);
852 fnmatch_sanitycheck();
854 our_pred = insert_primary (pred_lname);
855 our_pred->args.str = argv[*arg_ptr];
856 (*arg_ptr)++;
857 return (true);
860 static boolean
861 parse_ls (char **argv, int *arg_ptr)
863 struct predicate *our_pred;
865 (void) &argv;
866 (void) &arg_ptr;
868 our_pred = insert_primary (pred_ls);
869 our_pred->side_effects = true;
870 our_pred->no_default_print = true;
871 return (true);
874 static boolean
875 parse_maxdepth (char **argv, int *arg_ptr)
877 int depth_len;
879 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
880 return false;
881 depth_len = strspn (argv[*arg_ptr], "0123456789");
882 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
883 return false;
884 options.maxdepth = atoi (argv[*arg_ptr]);
885 if (options.maxdepth < 0)
886 return false;
887 (*arg_ptr)++;
888 return true;
891 static boolean
892 parse_mindepth (char **argv, int *arg_ptr)
894 int depth_len;
896 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
897 return false;
898 depth_len = strspn (argv[*arg_ptr], "0123456789");
899 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
900 return false;
901 options.mindepth = atoi (argv[*arg_ptr]);
902 if (options.mindepth < 0)
903 return false;
904 (*arg_ptr)++;
905 return true;
908 static boolean
909 parse_mmin (char **argv, int *arg_ptr)
911 struct predicate *our_pred;
912 uintmax_t num;
913 enum comparison_type c_type;
914 time_t t;
916 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
917 return (false);
918 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
919 return (false);
920 t = options.cur_day_start + DAYSECS - num * 60;
921 our_pred = insert_primary (pred_mmin);
922 our_pred->args.info.kind = c_type;
923 our_pred->args.info.negative = t < 0;
924 our_pred->args.info.l_val = t;
925 (*arg_ptr)++;
926 return (true);
929 static boolean
930 parse_mtime (char **argv, int *arg_ptr)
932 return (insert_time (argv, arg_ptr, pred_mtime));
935 static boolean
936 parse_name (char **argv, int *arg_ptr)
938 struct predicate *our_pred;
940 (void) argv;
941 (void) arg_ptr;
943 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
944 return (false);
945 our_pred = insert_primary (pred_name);
946 our_pred->need_stat = false;
947 our_pred->args.str = argv[*arg_ptr];
948 (*arg_ptr)++;
949 return (true);
952 static boolean
953 parse_negate (char **argv, int *arg_ptr)
955 struct predicate *our_pred;
957 (void) &argv;
958 (void) &arg_ptr;
960 our_pred = get_new_pred_chk_op ();
961 our_pred->pred_func = pred_negate;
962 #ifdef DEBUG
963 our_pred->p_name = find_pred_name (pred_negate);
964 #endif /* DEBUG */
965 our_pred->p_type = UNI_OP;
966 our_pred->p_prec = NEGATE_PREC;
967 our_pred->need_stat = false;
968 return (true);
971 static boolean
972 parse_newer (char **argv, int *arg_ptr)
974 struct predicate *our_pred;
975 struct stat stat_newer;
977 (void) argv;
978 (void) arg_ptr;
980 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
981 return (false);
982 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
983 error (1, errno, "%s", argv[*arg_ptr]);
984 our_pred = insert_primary (pred_newer);
985 our_pred->args.time = stat_newer.st_mtime;
986 (*arg_ptr)++;
987 return (true);
990 static boolean
991 parse_noleaf (char **argv, int *arg_ptr)
993 (void) &argv;
994 (void) &arg_ptr;
996 options.no_leaf_check = true;
997 return true;
1000 #ifdef CACHE_IDS
1001 /* Arbitrary amount by which to increase size
1002 of `uid_unused' and `gid_unused'. */
1003 #define ALLOC_STEP 2048
1005 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1006 char *uid_unused = NULL;
1008 /* Number of elements in `uid_unused'. */
1009 unsigned uid_allocated;
1011 /* Similar for GIDs and group entries. */
1012 char *gid_unused = NULL;
1013 unsigned gid_allocated;
1014 #endif
1016 static boolean
1017 parse_nogroup (char **argv, int *arg_ptr)
1019 struct predicate *our_pred;
1021 (void) &argv;
1022 (void) &arg_ptr;
1024 our_pred = insert_primary (pred_nogroup);
1025 #ifdef CACHE_IDS
1026 if (gid_unused == NULL)
1028 struct group *gr;
1030 gid_allocated = ALLOC_STEP;
1031 gid_unused = xmalloc (gid_allocated);
1032 memset (gid_unused, 1, gid_allocated);
1033 setgrent ();
1034 while ((gr = getgrent ()) != NULL)
1036 if ((unsigned) gr->gr_gid >= gid_allocated)
1038 unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
1039 gid_unused = xrealloc (gid_unused, new_allocated);
1040 memset (gid_unused + gid_allocated, 1,
1041 new_allocated - gid_allocated);
1042 gid_allocated = new_allocated;
1044 gid_unused[(unsigned) gr->gr_gid] = 0;
1046 endgrent ();
1048 #endif
1049 return (true);
1052 static boolean
1053 parse_nouser (char **argv, int *arg_ptr)
1055 struct predicate *our_pred;
1056 (void) argv;
1057 (void) arg_ptr;
1060 our_pred = insert_primary (pred_nouser);
1061 #ifdef CACHE_IDS
1062 if (uid_unused == NULL)
1064 struct passwd *pw;
1066 uid_allocated = ALLOC_STEP;
1067 uid_unused = xmalloc (uid_allocated);
1068 memset (uid_unused, 1, uid_allocated);
1069 setpwent ();
1070 while ((pw = getpwent ()) != NULL)
1072 if ((unsigned) pw->pw_uid >= uid_allocated)
1074 unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
1075 uid_unused = xrealloc (uid_unused, new_allocated);
1076 memset (uid_unused + uid_allocated, 1,
1077 new_allocated - uid_allocated);
1078 uid_allocated = new_allocated;
1080 uid_unused[(unsigned) pw->pw_uid] = 0;
1082 endpwent ();
1084 #endif
1085 return (true);
1088 static boolean
1089 parse_nowarn (char **argv, int *arg_ptr)
1091 (void) argv;
1092 (void) arg_ptr;
1094 options.warnings = false;
1095 return true;;
1098 static boolean
1099 parse_ok (char **argv, int *arg_ptr)
1101 return (insert_exec_ok (pred_ok, argv, arg_ptr));
1104 boolean
1105 parse_open (char **argv, int *arg_ptr)
1107 struct predicate *our_pred;
1109 (void) argv;
1110 (void) arg_ptr;
1112 our_pred = get_new_pred_chk_op ();
1113 our_pred->pred_func = pred_open;
1114 #ifdef DEBUG
1115 our_pred->p_name = find_pred_name (pred_open);
1116 #endif /* DEBUG */
1117 our_pred->p_type = OPEN_PAREN;
1118 our_pred->p_prec = NO_PREC;
1119 our_pred->need_stat = false;
1120 return (true);
1123 static boolean
1124 parse_or (char **argv, int *arg_ptr)
1126 struct predicate *our_pred;
1128 (void) argv;
1129 (void) arg_ptr;
1131 our_pred = get_new_pred ();
1132 our_pred->pred_func = pred_or;
1133 #ifdef DEBUG
1134 our_pred->p_name = find_pred_name (pred_or);
1135 #endif /* DEBUG */
1136 our_pred->p_type = BI_OP;
1137 our_pred->p_prec = OR_PREC;
1138 our_pred->need_stat = false;
1139 return (true);
1142 /* -path is deprecated (at RMS's request) in favour of
1143 * -iwholename. See the node "GNU Manuals" in standards.texi
1144 * for the rationale for this (basically, GNU prefers the use
1145 * of the phrase "file name" to "path name".
1147 * We do not issue a warning that this usage is deprecated
1148 * since HPUX find supports this predicate also.
1150 static boolean
1151 parse_path (char **argv, int *arg_ptr)
1153 return parse_wholename(argv, arg_ptr);
1156 static boolean
1157 parse_wholename (char **argv, int *arg_ptr)
1159 struct predicate *our_pred;
1161 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1162 return (false);
1163 our_pred = insert_primary (pred_path);
1164 our_pred->need_stat = false;
1165 our_pred->args.str = argv[*arg_ptr];
1166 (*arg_ptr)++;
1167 return (true);
1170 static boolean
1171 parse_perm (char **argv, int *arg_ptr)
1173 mode_t perm_val;
1174 int mode_start = 0;
1175 struct mode_change *change;
1176 struct predicate *our_pred;
1178 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1179 return (false);
1181 switch (argv[*arg_ptr][0])
1183 case '-':
1184 case '+':
1185 mode_start = 1;
1186 break;
1187 default:
1188 /* empty */
1189 break;
1192 change = mode_compile (argv[*arg_ptr] + mode_start, MODE_MASK_PLUS);
1193 if (change == MODE_INVALID)
1194 error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
1195 else if (change == MODE_MEMORY_EXHAUSTED)
1196 error (1, 0, _("virtual memory exhausted"));
1197 perm_val = mode_adjust (0, change);
1198 mode_free (change);
1200 our_pred = insert_primary (pred_perm);
1202 switch (argv[*arg_ptr][0])
1204 case '-':
1205 our_pred->args.perm.kind = PERM_AT_LEAST;
1206 break;
1207 case '+':
1208 our_pred->args.perm.kind = PERM_ANY;
1209 break;
1210 default:
1211 our_pred->args.perm.kind = PERM_EXACT;
1212 break;
1214 our_pred->args.perm.val = perm_val & MODE_ALL;
1215 (*arg_ptr)++;
1216 return (true);
1219 boolean
1220 parse_print (char **argv, int *arg_ptr)
1222 struct predicate *our_pred;
1224 (void) argv;
1225 (void) arg_ptr;
1227 our_pred = insert_primary (pred_print);
1228 /* -print has the side effect of printing. This prevents us
1229 from doing undesired multiple printing when the user has
1230 already specified -print. */
1231 our_pred->side_effects = true;
1232 our_pred->no_default_print = true;
1233 our_pred->need_stat = false;
1234 return (true);
1237 static boolean
1238 parse_print0 (char **argv, int *arg_ptr)
1240 struct predicate *our_pred;
1242 (void) argv;
1243 (void) arg_ptr;
1245 our_pred = insert_primary (pred_print0);
1246 /* -print0 has the side effect of printing. This prevents us
1247 from doing undesired multiple printing when the user has
1248 already specified -print0. */
1249 our_pred->side_effects = true;
1250 our_pred->no_default_print = true;
1251 our_pred->need_stat = false;
1252 return (true);
1255 static boolean
1256 parse_printf (char **argv, int *arg_ptr)
1258 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1259 return (false);
1260 return (insert_fprintf (stdout, pred_fprintf, argv, arg_ptr));
1263 static boolean
1264 parse_prune (char **argv, int *arg_ptr)
1266 struct predicate *our_pred;
1268 (void) argv;
1269 (void) arg_ptr;
1271 our_pred = insert_primary (pred_prune);
1272 our_pred->need_stat = false;
1273 /* -prune has a side effect that it does not descend into
1274 the current directory. */
1275 our_pred->side_effects = true;
1276 return (true);
1279 static boolean
1280 parse_quit (char **argv, int *arg_ptr)
1282 struct predicate *our_pred = insert_primary (pred_quit);
1283 (void) argv;
1284 (void) arg_ptr;
1285 our_pred->need_stat = false;
1286 return true;
1290 static boolean
1291 parse_regex (char **argv, int *arg_ptr)
1293 return insert_regex (argv, arg_ptr, false);
1296 static boolean
1297 insert_regex (char **argv, int *arg_ptr, boolean ignore_case)
1299 struct predicate *our_pred;
1300 struct re_pattern_buffer *re;
1301 const char *error_message;
1303 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1304 return (false);
1305 our_pred = insert_primary (pred_regex);
1306 our_pred->need_stat = false;
1307 re = (struct re_pattern_buffer *)
1308 xmalloc (sizeof (struct re_pattern_buffer));
1309 our_pred->args.regex = re;
1310 re->allocated = 100;
1311 re->buffer = (unsigned char *) xmalloc (re->allocated);
1312 re->fastmap = NULL;
1314 if (ignore_case)
1316 re_syntax_options |= RE_ICASE;
1318 else
1320 re_syntax_options &= ~RE_ICASE;
1322 re->translate = NULL;
1324 error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
1325 re);
1326 if (error_message)
1327 error (1, 0, "%s", error_message);
1328 (*arg_ptr)++;
1329 return (true);
1332 static boolean
1333 parse_size (char **argv, int *arg_ptr)
1335 struct predicate *our_pred;
1336 uintmax_t num;
1337 enum comparison_type c_type;
1338 int blksize = 512;
1339 int len;
1341 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1342 return (false);
1343 len = strlen (argv[*arg_ptr]);
1344 if (len == 0)
1345 error (1, 0, _("invalid null argument to -size"));
1346 switch (argv[*arg_ptr][len - 1])
1348 case 'b':
1349 blksize = 512;
1350 argv[*arg_ptr][len - 1] = '\0';
1351 break;
1353 case 'c':
1354 blksize = 1;
1355 argv[*arg_ptr][len - 1] = '\0';
1356 break;
1358 case 'k':
1359 blksize = 1024;
1360 argv[*arg_ptr][len - 1] = '\0';
1361 break;
1363 case 'M': /* Megabytes */
1364 blksize = 1024*1024;
1365 argv[*arg_ptr][len - 1] = '\0';
1366 break;
1368 case 'G': /* Gigabytes */
1369 blksize = 1024*1024*1024;
1370 argv[*arg_ptr][len - 1] = '\0';
1371 break;
1373 case 'w':
1374 blksize = 2;
1375 argv[*arg_ptr][len - 1] = '\0';
1376 break;
1378 case '0':
1379 case '1':
1380 case '2':
1381 case '3':
1382 case '4':
1383 case '5':
1384 case '6':
1385 case '7':
1386 case '8':
1387 case '9':
1388 break;
1390 default:
1391 error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
1393 if (!get_num (argv[*arg_ptr], &num, &c_type))
1394 return (false);
1395 our_pred = insert_primary (pred_size);
1396 our_pred->args.size.kind = c_type;
1397 our_pred->args.size.blocksize = blksize;
1398 our_pred->args.size.size = num;
1399 (*arg_ptr)++;
1400 return (true);
1404 static boolean
1405 parse_samefile (char **argv, int *arg_ptr)
1407 struct predicate *our_pred;
1408 struct stat st;
1410 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1411 return (false);
1412 if ((*options.xstat) (argv[*arg_ptr], &st))
1413 error (1, errno, "%s", argv[*arg_ptr]);
1415 our_pred = insert_primary (pred_samefile);
1416 our_pred->args.fileid.ino = st.st_ino;
1417 our_pred->args.fileid.dev = st.st_dev;
1418 our_pred->need_stat = true;
1419 (*arg_ptr)++;
1420 return (true);
1424 static boolean
1425 parse_true (char **argv, int *arg_ptr)
1427 struct predicate *our_pred;
1429 (void) argv;
1430 (void) arg_ptr;
1432 our_pred = insert_primary (pred_true);
1433 our_pred->need_stat = false;
1434 return (true);
1437 static boolean
1438 parse_type (char **argv, int *arg_ptr)
1440 return insert_type (argv, arg_ptr, pred_type);
1443 static boolean
1444 parse_uid (char **argv, int *arg_ptr)
1446 return (insert_num (argv, arg_ptr, pred_uid));
1449 static boolean
1450 parse_used (char **argv, int *arg_ptr)
1452 struct predicate *our_pred;
1453 uintmax_t num_days;
1454 enum comparison_type c_type;
1455 time_t t;
1457 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1458 return (false);
1459 if (!get_num (argv[*arg_ptr], &num_days, &c_type))
1460 return (false);
1461 t = num_days * DAYSECS;
1462 our_pred = insert_primary (pred_used);
1463 our_pred->args.info.kind = c_type;
1464 our_pred->args.info.negative = t < 0;
1465 our_pred->args.info.l_val = t;
1466 (*arg_ptr)++;
1467 return (true);
1470 static boolean
1471 parse_user (char **argv, int *arg_ptr)
1473 struct passwd *cur_pwd;
1474 struct predicate *our_pred;
1475 uid_t uid;
1476 int uid_len;
1478 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1479 return (false);
1480 cur_pwd = getpwnam (argv[*arg_ptr]);
1481 endpwent ();
1482 if (cur_pwd != NULL)
1483 uid = cur_pwd->pw_uid;
1484 else
1486 uid_len = strspn (argv[*arg_ptr], "0123456789");
1487 if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
1488 return (false);
1489 uid = atoi (argv[*arg_ptr]);
1491 our_pred = insert_primary (pred_user);
1492 our_pred->args.uid = uid;
1493 (*arg_ptr)++;
1494 return (true);
1497 static boolean
1498 parse_version (char **argv, int *arg_ptr)
1500 extern char *version_string;
1501 (void) argv;
1502 (void) arg_ptr;
1504 fflush (stderr);
1505 printf (_("GNU find version %s\n"), version_string);
1506 exit (0);
1509 static boolean
1510 parse_xdev (char **argv, int *arg_ptr)
1512 (void) argv;
1513 (void) arg_ptr;
1514 options.stay_on_filesystem = true;
1515 return true;
1518 static boolean
1519 parse_ignore_race (char **argv, int *arg_ptr)
1521 (void) argv;
1522 (void) arg_ptr;
1523 options.ignore_readdir_race = true;
1524 return true;
1527 static boolean
1528 parse_noignore_race (char **argv, int *arg_ptr)
1530 (void) argv;
1531 (void) arg_ptr;
1532 options.ignore_readdir_race = false;
1533 return true;
1536 static boolean
1537 parse_warn (char **argv, int *arg_ptr)
1539 (void) argv;
1540 (void) arg_ptr;
1541 options.warnings = true;
1542 return true;
1545 static boolean
1546 parse_xtype (char **argv, int *arg_ptr)
1548 (void) argv;
1549 (void) arg_ptr;
1550 return insert_type (argv, arg_ptr, pred_xtype);
1553 static boolean
1554 insert_type (char **argv, int *arg_ptr, boolean (*which_pred) (/* ??? */))
1556 mode_t type_cell;
1557 struct predicate *our_pred;
1559 if ((argv == NULL) || (argv[*arg_ptr] == NULL)
1560 || (strlen (argv[*arg_ptr]) != 1))
1561 return (false);
1562 switch (argv[*arg_ptr][0])
1564 case 'b': /* block special */
1565 type_cell = S_IFBLK;
1566 break;
1567 case 'c': /* character special */
1568 type_cell = S_IFCHR;
1569 break;
1570 case 'd': /* directory */
1571 type_cell = S_IFDIR;
1572 break;
1573 case 'f': /* regular file */
1574 type_cell = S_IFREG;
1575 break;
1576 #ifdef S_IFLNK
1577 case 'l': /* symbolic link */
1578 type_cell = S_IFLNK;
1579 break;
1580 #endif
1581 #ifdef S_IFIFO
1582 case 'p': /* pipe */
1583 type_cell = S_IFIFO;
1584 break;
1585 #endif
1586 #ifdef S_IFSOCK
1587 case 's': /* socket */
1588 type_cell = S_IFSOCK;
1589 break;
1590 #endif
1591 #ifdef S_IFDOOR
1592 case 'D': /* Solaris door */
1593 type_cell = S_IFDOOR;
1594 break;
1595 #endif
1596 default: /* None of the above ... nuke 'em. */
1597 return (false);
1599 our_pred = insert_primary (which_pred);
1600 our_pred->args.type = type_cell;
1601 (*arg_ptr)++; /* Move on to next argument. */
1602 return (true);
1605 /* If true, we've determined that the current fprintf predicate
1606 uses stat information. */
1607 static boolean fprintf_stat_needed;
1609 static boolean
1610 insert_fprintf (FILE *fp, boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
1612 char *format; /* Beginning of unprocessed format string. */
1613 register char *scan; /* Current address in scanning `format'. */
1614 register char *scan2; /* Address inside of element being scanned. */
1615 struct segment **segmentp; /* Address of current segment. */
1616 struct predicate *our_pred;
1618 format = argv[(*arg_ptr)++];
1620 fprintf_stat_needed = false; /* Might be overridden later. */
1621 our_pred = insert_primary (func);
1622 our_pred->side_effects = true;
1623 our_pred->no_default_print = true;
1624 our_pred->args.printf_vec.stream = fp;
1625 segmentp = &our_pred->args.printf_vec.segment;
1626 *segmentp = NULL;
1628 for (scan = format; *scan; scan++)
1630 if (*scan == '\\')
1632 scan2 = scan + 1;
1633 if (*scan2 >= '0' && *scan2 <= '7')
1635 register int n, i;
1637 for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
1638 i++, scan2++)
1639 n = 8 * n + *scan2 - '0';
1640 scan2--;
1641 *scan = n;
1643 else
1645 switch (*scan2)
1647 case 'a':
1648 *scan = 7;
1649 break;
1650 case 'b':
1651 *scan = '\b';
1652 break;
1653 case 'c':
1654 make_segment (segmentp, format, scan - format, KIND_STOP);
1655 our_pred->need_stat = fprintf_stat_needed;
1656 return (true);
1657 case 'f':
1658 *scan = '\f';
1659 break;
1660 case 'n':
1661 *scan = '\n';
1662 break;
1663 case 'r':
1664 *scan = '\r';
1665 break;
1666 case 't':
1667 *scan = '\t';
1668 break;
1669 case 'v':
1670 *scan = '\v';
1671 break;
1672 case '\\':
1673 /* *scan = '\\'; * it already is */
1674 break;
1675 default:
1676 error (0, 0,
1677 _("warning: unrecognized escape `\\%c'"), *scan2);
1678 scan++;
1679 continue;
1682 segmentp = make_segment (segmentp, format, scan - format + 1,
1683 KIND_PLAIN);
1684 format = scan2 + 1; /* Move past the escape. */
1685 scan = scan2; /* Incremented immediately by `for'. */
1687 else if (*scan == '%')
1689 if (scan[1] == '%')
1691 segmentp = make_segment (segmentp, format, scan - format + 1,
1692 KIND_PLAIN);
1693 scan++;
1694 format = scan + 1;
1695 continue;
1697 /* Scan past flags, width and precision, to verify kind. */
1698 for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
1699 /* Do nothing. */ ;
1700 while (ISDIGIT (*scan2))
1701 scan2++;
1702 if (*scan2 == '.')
1703 for (scan2++; ISDIGIT (*scan2); scan2++)
1704 /* Do nothing. */ ;
1705 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2))
1707 segmentp = make_segment (segmentp, format, scan2 - format,
1708 (int) *scan2);
1709 scan = scan2;
1710 format = scan + 1;
1712 else if (strchr ("ACT", *scan2) && scan2[1])
1714 segmentp = make_segment (segmentp, format, scan2 - format,
1715 *scan2 | (scan2[1] << 8));
1716 scan = scan2 + 1;
1717 format = scan + 1;
1718 continue;
1720 else
1722 /* An unrecognized % escape. Print the char after the %. */
1723 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1724 *scan2);
1725 segmentp = make_segment (segmentp, format, scan - format,
1726 KIND_PLAIN);
1727 format = scan + 1;
1728 continue;
1733 if (scan > format)
1734 make_segment (segmentp, format, scan - format, KIND_PLAIN);
1735 our_pred->need_stat = fprintf_stat_needed;
1736 return (true);
1739 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1740 from the text in FORMAT, which has length LEN.
1741 Return the address of the `next' pointer of the new segment. */
1743 static struct segment **
1744 make_segment (struct segment **segment, char *format, int len, int kind)
1746 char *fmt;
1748 *segment = (struct segment *) xmalloc (sizeof (struct segment));
1750 (*segment)->kind = kind;
1751 (*segment)->next = NULL;
1752 (*segment)->text_len = len;
1754 fmt = (*segment)->text = xmalloc (len + sizeof "d");
1755 strncpy (fmt, format, len);
1756 fmt += len;
1758 switch (kind & 0xff)
1760 case KIND_PLAIN: /* Plain text string, no % conversion. */
1761 case KIND_STOP: /* Terminate argument, no newline. */
1762 break;
1764 case 'a': /* atime in `ctime' format */
1765 case 'A': /* atime in user-specified strftime format */
1766 case 'c': /* ctime in `ctime' format */
1767 case 'C': /* ctime in user-specified strftime format */
1768 case 'F': /* filesystem type */
1769 case 'g': /* group name */
1770 case 'i': /* inode number */
1771 case 'l': /* object of symlink */
1772 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1773 case 's': /* size in bytes */
1774 case 't': /* mtime in `ctime' format */
1775 case 'T': /* mtime in user-specified strftime format */
1776 case 'u': /* user name */
1777 case 'y': /* file type */
1778 case 'Y': /* symlink pointed file type */
1779 fprintf_stat_needed = true;
1780 /* FALLTHROUGH */
1781 case 'f': /* basename of path */
1782 case 'h': /* leading directories part of path */
1783 case 'H': /* ARGV element file was found under */
1784 case 'p': /* pathname */
1785 case 'P': /* pathname with ARGV element stripped */
1786 *fmt++ = 's';
1787 break;
1789 /* Numeric items that one might expect to honour
1790 * #, 0, + flags but which do not.
1792 case 'G': /* GID number */
1793 case 'U': /* UID number */
1794 case 'b': /* size in 512-byte blocks */
1795 case 'D': /* Filesystem device on which the file exits */
1796 case 'k': /* size in 1K blocks */
1797 case 'n': /* number of links */
1798 fprintf_stat_needed = true;
1799 *fmt++ = 's';
1801 /* Numeric items that DO honour #, 0, + flags.
1803 case 'd': /* depth in search tree (0 = ARGV element) */
1804 *fmt++ = 'd';
1805 break;
1807 case 'm': /* mode as octal number (perms only) */
1808 *fmt++ = 'o';
1809 fprintf_stat_needed = true;
1810 break;
1812 *fmt = '\0';
1814 return (&(*segment)->next);
1817 /* handles both exec and ok predicate */
1818 static boolean
1819 insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
1821 int start, end; /* Indexes in ARGV of start & end of cmd. */
1822 int num_paths; /* Number of args with path replacements. */
1823 int path_pos; /* Index in array of path replacements. */
1824 int vec_pos; /* Index in array of args. */
1825 struct predicate *our_pred;
1826 struct exec_val *execp; /* Pointer for efficiency. */
1828 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1829 return (false);
1831 /* Count the number of args with path replacements, up until the ';'. */
1832 start = *arg_ptr;
1833 for (end = start, num_paths = 0;
1834 (argv[end] != NULL)
1835 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
1836 end++)
1837 if (strstr (argv[end], "{}"))
1838 num_paths++;
1839 /* Fail if no command given or no semicolon found. */
1840 if ((end == start) || (argv[end] == NULL))
1842 *arg_ptr = end;
1843 return (false);
1846 our_pred = insert_primary (func);
1847 our_pred->side_effects = true;
1848 our_pred->no_default_print = true;
1849 execp = &our_pred->args.exec_vec;
1850 execp->paths =
1851 (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1));
1852 execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1));
1853 /* Record the positions of all args, and the args with path replacements. */
1854 for (end = start, path_pos = vec_pos = 0;
1855 (argv[end] != NULL)
1856 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
1857 end++)
1859 register char *p;
1861 execp->paths[path_pos].count = 0;
1862 for (p = argv[end]; *p; ++p)
1863 if (p[0] == '{' && p[1] == '}')
1865 execp->paths[path_pos].count++;
1866 ++p;
1868 if (execp->paths[path_pos].count)
1870 execp->paths[path_pos].offset = vec_pos;
1871 execp->paths[path_pos].origarg = argv[end];
1872 path_pos++;
1874 execp->vec[vec_pos++] = argv[end];
1876 execp->paths[path_pos].offset = -1;
1877 execp->vec[vec_pos] = NULL;
1879 if (argv[end] == NULL)
1880 *arg_ptr = end;
1881 else
1882 *arg_ptr = end + 1;
1883 return (true);
1886 /* Get a number of days and comparison type.
1887 STR is the ASCII representation.
1888 Set *NUM_DAYS to the number of days, taken as being from
1889 the current moment (or possibly midnight). Thus the sense of the
1890 comparison type appears to be reversed.
1891 Set *COMP_TYPE to the kind of comparison that is requested.
1893 Return true if all okay, false if input error.
1895 Used by -atime, -ctime and -mtime (parsers) to
1896 get the appropriate information for a time predicate processor. */
1898 static boolean
1899 get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
1901 boolean r = get_num (str, num_days, comp_type);
1902 if (r)
1903 switch (*comp_type)
1905 case COMP_LT: *comp_type = COMP_GT; break;
1906 case COMP_GT: *comp_type = COMP_LT; break;
1907 default: break;
1909 return r;
1912 /* Insert a time predicate PRED.
1913 ARGV is a pointer to the argument array.
1914 ARG_PTR is a pointer to an index into the array, incremented if
1915 all went well.
1917 Return true if input is valid, false if not.
1919 A new predicate node is assigned, along with an argument node
1920 obtained with malloc.
1922 Used by -atime, -ctime, and -mtime parsers. */
1924 static boolean
1925 insert_time (char **argv, int *arg_ptr, PFB pred)
1927 struct predicate *our_pred;
1928 uintmax_t num_days;
1929 enum comparison_type c_type;
1930 time_t t;
1932 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1933 return (false);
1934 if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
1935 return (false);
1937 /* Figure out the timestamp value we are looking for. */
1938 t = ( options.cur_day_start - num_days * DAYSECS
1939 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
1941 if (1)
1943 /* We introduce a scope in which 'val' can be declared, for the
1944 * benefit of compilers that are really C89 compilers
1945 * which support intmax_t because config.h #defines it
1947 intmax_t val = ( (intmax_t)options.cur_day_start - num_days * DAYSECS
1948 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
1949 t = val;
1951 /* Check for possibility of an overflow */
1952 if ( (intmax_t)t != val )
1954 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv[*arg_ptr]);
1958 our_pred = insert_primary (pred);
1959 our_pred->args.info.kind = c_type;
1960 our_pred->args.info.negative = t < 0;
1961 our_pred->args.info.l_val = t;
1962 (*arg_ptr)++;
1963 #ifdef DEBUG
1964 fprintf (stderr, _("inserting %s\n"), our_pred->p_name);
1965 fprintf (stderr, _(" type: %s %s "),
1966 (c_type == COMP_GT) ? "gt" :
1967 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
1968 (c_type == COMP_GT) ? " >" :
1969 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
1970 t = our_pred->args.info.l_val;
1971 fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
1972 if (c_type == COMP_EQ)
1974 t = our_pred->args.info.l_val += DAYSECS;
1975 fprintf (stderr,
1976 " < %ju %s",
1977 (uintmax_t) our_pred->args.info.l_val, ctime (&t));
1978 our_pred->args.info.l_val -= DAYSECS;
1980 #endif /* DEBUG */
1981 return (true);
1984 /* Get a number with comparision information.
1985 The sense of the comparision information is 'normal'; that is,
1986 '+' looks for a count > than the number and '-' less than.
1988 STR is the ASCII representation of the number.
1989 Set *NUM to the number.
1990 Set *COMP_TYPE to the kind of comparison that is requested.
1992 Return true if all okay, false if input error. */
1994 static boolean
1995 get_num (char *str, uintmax_t *num, enum comparison_type *comp_type)
1997 if (str == NULL)
1998 return (false);
1999 switch (str[0])
2001 case '+':
2002 *comp_type = COMP_GT;
2003 str++;
2004 break;
2005 case '-':
2006 *comp_type = COMP_LT;
2007 str++;
2008 break;
2009 default:
2010 *comp_type = COMP_EQ;
2011 break;
2014 return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK;
2017 /* Insert a number predicate.
2018 ARGV is a pointer to the argument array.
2019 *ARG_PTR is an index into ARGV, incremented if all went well.
2020 *PRED is the predicate processor to insert.
2022 Return true if input is valid, false if error.
2024 A new predicate node is assigned, along with an argument node
2025 obtained with malloc.
2027 Used by -inum and -links parsers. */
2029 static boolean
2030 insert_num (char **argv, int *arg_ptr, PFB pred)
2032 struct predicate *our_pred;
2033 uintmax_t num;
2034 enum comparison_type c_type;
2036 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2037 return (false);
2038 if (!get_num (argv[*arg_ptr], &num, &c_type))
2039 return (false);
2040 our_pred = insert_primary (pred);
2041 our_pred->args.info.kind = c_type;
2042 our_pred->args.info.l_val = num;
2043 (*arg_ptr)++;
2044 #ifdef DEBUG
2045 fprintf (stderr, _("inserting %s\n"), our_pred->p_name);
2046 fprintf (stderr, _(" type: %s %s "),
2047 (c_type == COMP_GT) ? "gt" :
2048 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2049 (c_type == COMP_GT) ? " >" :
2050 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
2051 fprintf (stderr, "%ju\n", our_pred->args.info.l_val);
2052 #endif /* DEBUG */
2053 return (true);
2056 static FILE *
2057 open_output_file (char *path)
2059 FILE *f;
2061 if (!strcmp (path, "/dev/stderr"))
2062 return (stderr);
2063 else if (!strcmp (path, "/dev/stdout"))
2064 return (stdout);
2065 f = fopen (path, "w");
2066 if (f == NULL)
2067 error (1, errno, "%s", path);
2068 return (f);