Added a new "NON-BUGS" section pointing out things that look like bugs
[findutils.git] / find / parser.c
blob3c8bab3431c88be684d7f71436dcf5b4199b63c9
1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2001, 2003,
3 2004, 2005 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 USA.
22 #include "defs.h"
23 #include <ctype.h>
24 #include <pwd.h>
25 #include <grp.h>
26 #include <fnmatch.h>
27 #include "modechange.h"
28 #include "modetype.h"
29 #include "xstrtol.h"
30 #include "xalloc.h"
31 #include "quote.h"
32 #include "quotearg.h"
33 #include "buildcmd.h"
34 #include "nextelem.h"
36 #ifdef HAVE_FCNTL_H
37 #include <fcntl.h>
38 #else
39 #include <sys/file.h>
40 #endif
42 #ifdef HAVE_UNISTD_H
43 /* We need <unistd.h> for isatty(). */
44 #include <unistd.h>
45 #endif
48 #if ENABLE_NLS
49 # include <libintl.h>
50 # define _(Text) gettext (Text)
51 #else
52 # define _(Text) Text
53 #endif
54 #ifdef gettext_noop
55 # define N_(String) gettext_noop (String)
56 #else
57 /* See locate.c for explanation as to why not use (String) */
58 # define N_(String) String
59 #endif
61 #if !defined (isascii) || defined (STDC_HEADERS)
62 #ifdef isascii
63 #undef isascii
64 #endif
65 #define isascii(c) 1
66 #endif
68 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
69 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
71 #ifndef HAVE_ENDGRENT
72 #define endgrent()
73 #endif
74 #ifndef HAVE_ENDPWENT
75 #define endpwent()
76 #endif
78 static boolean parse_amin PARAMS((char *argv[], int *arg_ptr));
79 static boolean parse_and PARAMS((char *argv[], int *arg_ptr));
80 static boolean parse_anewer PARAMS((char *argv[], int *arg_ptr));
81 static boolean parse_atime PARAMS((char *argv[], int *arg_ptr));
82 boolean parse_close PARAMS((char *argv[], int *arg_ptr));
83 static boolean parse_cmin PARAMS((char *argv[], int *arg_ptr));
84 static boolean parse_cnewer PARAMS((char *argv[], int *arg_ptr));
85 static boolean parse_comma PARAMS((char *argv[], int *arg_ptr));
86 static boolean parse_ctime PARAMS((char *argv[], int *arg_ptr));
87 static boolean parse_daystart PARAMS((char *argv[], int *arg_ptr));
88 static boolean parse_delete PARAMS((char *argv[], int *arg_ptr));
89 static boolean parse_d PARAMS((char *argv[], int *arg_ptr));
90 static boolean parse_depth PARAMS((char *argv[], int *arg_ptr));
91 static boolean parse_empty PARAMS((char *argv[], int *arg_ptr));
92 static boolean parse_exec PARAMS((char *argv[], int *arg_ptr));
93 static boolean parse_execdir PARAMS((char *argv[], int *arg_ptr));
94 static boolean parse_false PARAMS((char *argv[], int *arg_ptr));
95 static boolean parse_fls PARAMS((char *argv[], int *arg_ptr));
96 static boolean parse_fprintf PARAMS((char *argv[], int *arg_ptr));
97 static boolean parse_follow PARAMS((char *argv[], int *arg_ptr));
98 static boolean parse_fprint PARAMS((char *argv[], int *arg_ptr));
99 static boolean parse_fprint0 PARAMS((char *argv[], int *arg_ptr));
100 static boolean parse_fstype PARAMS((char *argv[], int *arg_ptr));
101 static boolean parse_gid PARAMS((char *argv[], int *arg_ptr));
102 static boolean parse_group PARAMS((char *argv[], int *arg_ptr));
103 static boolean parse_help PARAMS((char *argv[], int *arg_ptr));
104 static boolean parse_ilname PARAMS((char *argv[], int *arg_ptr));
105 static boolean parse_iname PARAMS((char *argv[], int *arg_ptr));
106 static boolean parse_inum PARAMS((char *argv[], int *arg_ptr));
107 static boolean parse_ipath PARAMS((char *argv[], int *arg_ptr));
108 static boolean parse_iregex PARAMS((char *argv[], int *arg_ptr));
109 static boolean parse_iwholename PARAMS((char *argv[], int *arg_ptr));
110 static boolean parse_links PARAMS((char *argv[], int *arg_ptr));
111 static boolean parse_lname PARAMS((char *argv[], int *arg_ptr));
112 static boolean parse_ls PARAMS((char *argv[], int *arg_ptr));
113 static boolean parse_maxdepth PARAMS((char *argv[], int *arg_ptr));
114 static boolean parse_mindepth PARAMS((char *argv[], int *arg_ptr));
115 static boolean parse_mmin PARAMS((char *argv[], int *arg_ptr));
116 static boolean parse_mtime PARAMS((char *argv[], int *arg_ptr));
117 static boolean parse_name PARAMS((char *argv[], int *arg_ptr));
118 static boolean parse_negate PARAMS((char *argv[], int *arg_ptr));
119 static boolean parse_newer PARAMS((char *argv[], int *arg_ptr));
120 static boolean parse_noleaf PARAMS((char *argv[], int *arg_ptr));
121 static boolean parse_nogroup PARAMS((char *argv[], int *arg_ptr));
122 static boolean parse_nouser PARAMS((char *argv[], int *arg_ptr));
123 static boolean parse_nowarn PARAMS((char *argv[], int *arg_ptr));
124 static boolean parse_ok PARAMS((char *argv[], int *arg_ptr));
125 static boolean parse_okdir PARAMS((char *argv[], int *arg_ptr));
126 boolean parse_open PARAMS((char *argv[], int *arg_ptr));
127 static boolean parse_or PARAMS((char *argv[], int *arg_ptr));
128 static boolean parse_path PARAMS((char *argv[], int *arg_ptr));
129 static boolean parse_perm PARAMS((char *argv[], int *arg_ptr));
130 boolean parse_print PARAMS((char *argv[], int *arg_ptr));
131 static boolean parse_print0 PARAMS((char *argv[], int *arg_ptr));
132 static boolean parse_printf PARAMS((char *argv[], int *arg_ptr));
133 static boolean parse_prune PARAMS((char *argv[], int *arg_ptr));
134 static boolean parse_regex PARAMS((char *argv[], int *arg_ptr));
135 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case));
136 static boolean parse_samefile PARAMS((char *argv[], int *arg_ptr));
137 static boolean parse_size PARAMS((char *argv[], int *arg_ptr));
138 static boolean parse_true PARAMS((char *argv[], int *arg_ptr));
139 static boolean parse_type PARAMS((char *argv[], int *arg_ptr));
140 static boolean parse_uid PARAMS((char *argv[], int *arg_ptr));
141 static boolean parse_used PARAMS((char *argv[], int *arg_ptr));
142 static boolean parse_user PARAMS((char *argv[], int *arg_ptr));
143 static boolean parse_version PARAMS((char *argv[], int *arg_ptr));
144 static boolean parse_wholename PARAMS((char *argv[], int *arg_ptr));
145 static boolean parse_xdev PARAMS((char *argv[], int *arg_ptr));
146 static boolean parse_ignore_race PARAMS((char *argv[], int *arg_ptr));
147 static boolean parse_noignore_race PARAMS((char *argv[], int *arg_ptr));
148 static boolean parse_warn PARAMS((char *argv[], int *arg_ptr));
149 static boolean parse_xtype PARAMS((char *argv[], int *arg_ptr));
150 static boolean parse_quit PARAMS((char *argv[], int *arg_ptr));
152 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case));
153 static boolean insert_type PARAMS((char *argv[], int *arg_ptr, PRED_FUNC which_pred));
154 static boolean insert_fprintf PARAMS((FILE *fp, PRED_FUNC func, char *argv[], int *arg_ptr));
155 static struct segment **make_segment PARAMS((struct segment **segment, char *format, int len, int kind));
156 static boolean insert_exec_ok PARAMS((const char *action, PRED_FUNC func, char *argv[], int *arg_ptr));
157 static boolean get_num_days PARAMS((char *str, uintmax_t *num_days, enum comparison_type *comp_type));
158 static boolean insert_time PARAMS((char *argv[], int *arg_ptr, PRED_FUNC pred));
159 static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type));
160 static boolean insert_num PARAMS((char *argv[], int *arg_ptr, PRED_FUNC pred));
161 static FILE *open_output_file PARAMS((char *path));
162 static boolean stream_is_tty(FILE *fp);
164 #ifdef DEBUG
165 char *find_pred_name PARAMS((PRED_FUNC pred_func));
166 #endif /* DEBUG */
170 enum arg_type
172 ARG_OPTION, /* regular options like -maxdepth */
173 ARG_POSITIONAL_OPTION, /* options whose position is important (-follow) */
174 ARG_TEST, /* a like -name */
175 ARG_PUNCTUATION, /* like -o or ( */
176 ARG_ACTION /* like -print */
180 struct parser_table
182 enum arg_type type;
183 char *parser_name;
184 PARSE_FUNC parser_func;
187 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
188 If they are in some Unix versions of find, they are marked `Unix'. */
190 static struct parser_table const parse_table[] =
192 {ARG_PUNCTUATION, "!", parse_negate},
193 {ARG_PUNCTUATION, "not", parse_negate}, /* GNU */
194 {ARG_PUNCTUATION, "(", parse_open},
195 {ARG_PUNCTUATION, ")", parse_close},
196 {ARG_PUNCTUATION, ",", parse_comma}, /* GNU */
197 {ARG_PUNCTUATION, "a", parse_and},
198 {ARG_TEST, "amin", parse_amin}, /* GNU */
199 {ARG_PUNCTUATION, "and", parse_and}, /* GNU */
200 {ARG_TEST, "anewer", parse_anewer}, /* GNU */
201 {ARG_TEST, "atime", parse_atime},
202 {ARG_TEST, "cmin", parse_cmin}, /* GNU */
203 {ARG_TEST, "cnewer", parse_cnewer}, /* GNU */
204 #ifdef UNIMPLEMENTED_UNIX
205 /* It's pretty ugly for find to know about archive formats.
206 Plus what it could do with cpio archives is very limited.
207 Better to leave it out. */
208 {ARG_UNIMPLEMENTED, "cpio", parse_cpio}, /* Unix */
209 #endif
210 {ARG_TEST, "ctime", parse_ctime},
211 {ARG_POSITIONAL_OPTION, "daystart", parse_daystart}, /* GNU */
212 {ARG_ACTION, "delete", parse_delete}, /* GNU, Mac OS, FreeBSD */
213 {ARG_OPTION, "d", parse_d}, /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
214 {ARG_OPTION, "depth", parse_depth},
215 {ARG_TEST, "empty", parse_empty}, /* GNU */
216 {ARG_ACTION, "exec", parse_exec},
217 {ARG_ACTION, "execdir", parse_execdir}, /* *BSD, GNU */
218 {ARG_TEST, "false", parse_false}, /* GNU */
219 {ARG_ACTION, "fls", parse_fls}, /* GNU */
220 {ARG_POSITIONAL_OPTION, "follow", parse_follow}, /* GNU, Unix */
221 {ARG_ACTION, "fprint", parse_fprint}, /* GNU */
222 {ARG_ACTION, "fprint0", parse_fprint0}, /* GNU */
223 {ARG_ACTION, "fprintf", parse_fprintf}, /* GNU */
224 {ARG_TEST, "fstype", parse_fstype}, /* GNU, Unix */
225 {ARG_TEST, "gid", parse_gid}, /* GNU */
226 {ARG_TEST, "group", parse_group},
227 {ARG_TEST, "help", parse_help}, /* GNU */
228 {ARG_TEST, "-help", parse_help}, /* GNU */
229 {ARG_OPTION, "ignore_readdir_race", parse_ignore_race}, /* GNU */
230 {ARG_TEST, "ilname", parse_ilname}, /* GNU */
231 {ARG_TEST, "iname", parse_iname}, /* GNU */
232 {ARG_TEST, "inum", parse_inum}, /* GNU, Unix */
233 {ARG_TEST, "ipath", parse_ipath}, /* GNU, deprecated in favour of iwholename */
234 {ARG_TEST, "iregex", parse_iregex}, /* GNU */
235 {ARG_TEST, "iwholename", parse_iwholename}, /* GNU */
236 {ARG_TEST, "links", parse_links},
237 {ARG_TEST, "lname", parse_lname}, /* GNU */
238 {ARG_ACTION, "ls", parse_ls}, /* GNU, Unix */
239 {ARG_OPTION, "maxdepth", parse_maxdepth}, /* GNU */
240 {ARG_OPTION, "mindepth", parse_mindepth}, /* GNU */
241 {ARG_TEST, "mmin", parse_mmin}, /* GNU */
242 {ARG_OPTION, "mount", parse_xdev}, /* Unix */
243 {ARG_TEST, "mtime", parse_mtime},
244 {ARG_TEST, "name", parse_name},
245 #ifdef UNIMPLEMENTED_UNIX
246 {ARG_UNIMPLEMENTED, "ncpio", parse_ncpio}, /* Unix */
247 #endif
248 {ARG_TEST, "newer", parse_newer},
249 {ARG_OPTION, "noleaf", parse_noleaf}, /* GNU */
250 {ARG_TEST, "nogroup", parse_nogroup},
251 {ARG_TEST, "nouser", parse_nouser},
252 {ARG_OPTION, "noignore_readdir_race", parse_noignore_race},/* GNU */
253 {ARG_OPTION, "nowarn", parse_nowarn}, /* GNU */
254 {ARG_PUNCTUATION, "o", parse_or},
255 {ARG_PUNCTUATION, "or", parse_or}, /* GNU */
256 {ARG_ACTION, "ok", parse_ok},
257 {ARG_ACTION, "okdir", parse_okdir}, /* GNU (-execdir is BSD) */
258 {ARG_TEST, "path", parse_path}, /* GNU, HP-UX, GNU prefers wholename */
259 {ARG_TEST, "perm", parse_perm},
260 {ARG_ACTION, "print", parse_print},
261 {ARG_ACTION, "print0", parse_print0}, /* GNU */
262 {ARG_ACTION, "printf", parse_printf}, /* GNU */
263 {ARG_TEST, "prune", parse_prune},
264 {ARG_ACTION, "quit", parse_quit}, /* GNU */
265 {ARG_TEST, "regex", parse_regex}, /* GNU */
266 {ARG_TEST, "samefile", parse_samefile}, /* GNU */
267 {ARG_TEST, "size", parse_size},
268 {ARG_TEST, "true", parse_true}, /* GNU */
269 {ARG_TEST, "type", parse_type},
270 {ARG_TEST, "uid", parse_uid}, /* GNU */
271 {ARG_TEST, "used", parse_used}, /* GNU */
272 {ARG_TEST, "user", parse_user},
273 {ARG_TEST, "version", parse_version}, /* GNU */
274 {ARG_TEST, "-version", parse_version}, /* GNU */
275 {ARG_OPTION, "warn", parse_warn}, /* GNU */
276 {ARG_TEST, "wholename", parse_wholename}, /* GNU, replaces -path */
277 {ARG_OPTION, "xdev", parse_xdev},
278 {ARG_TEST, "xtype", parse_xtype}, /* GNU */
279 {0, 0, 0}
283 static const char *first_nonoption_arg = NULL;
285 /* Return a pointer to the parser function to invoke for predicate
286 SEARCH_NAME.
287 Return NULL if SEARCH_NAME is not a valid predicate name. */
289 PARSE_FUNC
290 find_parser (char *search_name)
292 int i;
293 const char *original_arg = search_name;
295 if (*search_name == '-')
296 search_name++;
297 for (i = 0; parse_table[i].parser_name != 0; i++)
299 if (strcmp (parse_table[i].parser_name, search_name) == 0)
301 /* If this is an option, but we have already had a
302 * non-option argument, the user may be under the
303 * impression that the behaviour of the option
304 * argument is conditional on some preceding
305 * tests. This might typically be the case with,
306 * for example, -maxdepth.
308 * The options -daystart and -follow are exempt
309 * from this treatment, since their positioning
310 * in the command line does have an effect on
311 * subsequent tests but not previous ones. That
312 * might be intentional on the part of the user.
314 if (parse_table[i].type != ARG_POSITIONAL_OPTION)
316 /* Something other than -follow/-daystart.
317 * If this is an option, check if it followed
318 * a non-option and if so, issue a warning.
320 if (parse_table[i].type == ARG_OPTION)
322 if ((first_nonoption_arg != NULL)
323 && options.warnings )
325 /* option which folows a non-option */
326 error (0, 0,
327 _("warning: you have specified the %s "
328 "option after a non-option argument %s, "
329 "but options are not positional (%s affects "
330 "tests specified before it as well as those "
331 "specified after it). Please specify options "
332 "before other arguments.\n"),
333 original_arg,
334 first_nonoption_arg,
335 original_arg);
338 else
340 /* Not an option or a positional option,
341 * so remember we've seen it in order to
342 * use it in a possible future warning message.
344 if (first_nonoption_arg == NULL)
346 first_nonoption_arg = original_arg;
351 return (parse_table[i].parser_func);
354 return NULL;
357 /* The parsers are responsible to continue scanning ARGV for
358 their arguments. Each parser knows what is and isn't
359 allowed for itself.
361 ARGV is the argument array.
362 *ARG_PTR is the index to start at in ARGV,
363 updated to point beyond the last element consumed.
365 The predicate structure is updated with the new information. */
367 static boolean
368 parse_amin (char **argv, int *arg_ptr)
370 struct predicate *our_pred;
371 uintmax_t num;
372 enum comparison_type c_type;
373 time_t t;
375 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
376 return (false);
377 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
378 return (false);
379 t = options.cur_day_start + DAYSECS - num * 60;
380 our_pred = insert_primary (pred_amin);
381 our_pred->args.info.kind = c_type;
382 our_pred->args.info.negative = t < 0;
383 our_pred->args.info.l_val = t;
384 (*arg_ptr)++;
385 return (true);
388 static boolean
389 parse_and (char **argv, int *arg_ptr)
391 struct predicate *our_pred;
393 (void) argv;
394 (void) arg_ptr;
396 our_pred = get_new_pred ();
397 our_pred->pred_func = pred_and;
398 #ifdef DEBUG
399 our_pred->p_name = find_pred_name (pred_and);
400 #endif /* DEBUG */
401 our_pred->p_type = BI_OP;
402 our_pred->p_prec = AND_PREC;
403 our_pred->need_stat = our_pred->need_type = false;
404 return (true);
407 static boolean
408 parse_anewer (char **argv, int *arg_ptr)
410 struct predicate *our_pred;
411 struct stat stat_newer;
413 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
414 return (false);
415 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
416 error (1, errno, "%s", argv[*arg_ptr]);
417 our_pred = insert_primary (pred_anewer);
418 our_pred->args.time = stat_newer.st_mtime;
419 (*arg_ptr)++;
420 return (true);
423 static boolean
424 parse_atime (char **argv, int *arg_ptr)
426 return (insert_time (argv, arg_ptr, pred_atime));
429 boolean
430 parse_close (char **argv, int *arg_ptr)
432 struct predicate *our_pred;
434 (void) argv;
435 (void) arg_ptr;
437 our_pred = get_new_pred ();
438 our_pred->pred_func = pred_close;
439 #ifdef DEBUG
440 our_pred->p_name = find_pred_name (pred_close);
441 #endif /* DEBUG */
442 our_pred->p_type = CLOSE_PAREN;
443 our_pred->p_prec = NO_PREC;
444 our_pred->need_stat = our_pred->need_type = false;
445 return (true);
448 static boolean
449 parse_cmin (char **argv, int *arg_ptr)
451 struct predicate *our_pred;
452 uintmax_t num;
453 enum comparison_type c_type;
454 time_t t;
456 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
457 return (false);
458 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
459 return (false);
460 t = options.cur_day_start + DAYSECS - num * 60;
461 our_pred = insert_primary (pred_cmin);
462 our_pred->args.info.kind = c_type;
463 our_pred->args.info.negative = t < 0;
464 our_pred->args.info.l_val = t;
465 (*arg_ptr)++;
466 return (true);
469 static boolean
470 parse_cnewer (char **argv, int *arg_ptr)
472 struct predicate *our_pred;
473 struct stat stat_newer;
475 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
476 return (false);
477 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
478 error (1, errno, "%s", argv[*arg_ptr]);
479 our_pred = insert_primary (pred_cnewer);
480 our_pred->args.time = stat_newer.st_mtime;
481 (*arg_ptr)++;
482 return (true);
485 static boolean
486 parse_comma (char **argv, int *arg_ptr)
488 struct predicate *our_pred;
490 (void) argv;
491 (void) arg_ptr;
493 our_pred = get_new_pred ();
494 our_pred->pred_func = pred_comma;
495 #ifdef DEBUG
496 our_pred->p_name = find_pred_name (pred_comma);
497 #endif /* DEBUG */
498 our_pred->p_type = BI_OP;
499 our_pred->p_prec = COMMA_PREC;
500 our_pred->need_stat = our_pred->need_type = false;
501 return (true);
504 static boolean
505 parse_ctime (char **argv, int *arg_ptr)
507 return (insert_time (argv, arg_ptr, pred_ctime));
510 static boolean
511 parse_daystart (char **argv, int *arg_ptr)
513 struct tm *local;
515 (void) argv;
516 (void) arg_ptr;
518 if (options.full_days == false)
520 options.cur_day_start += DAYSECS;
521 local = localtime (&options.cur_day_start);
522 options.cur_day_start -= (local
523 ? (local->tm_sec + local->tm_min * 60
524 + local->tm_hour * 3600)
525 : options.cur_day_start % DAYSECS);
526 options.full_days = true;
528 return true;
531 static boolean
532 parse_delete ( char *argv[], int *arg_ptr)
534 struct predicate *our_pred;
535 (void) argv;
536 (void) arg_ptr;
538 our_pred = insert_primary (pred_delete);
539 our_pred->side_effects = true;
540 our_pred->no_default_print = true;
541 /* -delete implies -depth */
542 options.do_dir_first = false;
543 return (true);
546 static boolean
547 parse_depth (char **argv, int *arg_ptr)
549 (void) argv;
550 (void) arg_ptr;
552 options.do_dir_first = false;
553 return (true);
556 static boolean
557 parse_d (char **argv, int *arg_ptr)
559 (void) argv;
560 (void) arg_ptr;
562 if (options.warnings)
564 error (0, 0,
565 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
567 return parse_depth(argv, arg_ptr);
570 static boolean
571 parse_empty (char **argv, int *arg_ptr)
573 (void) argv;
574 (void) arg_ptr;
576 insert_primary (pred_empty);
577 return (true);
580 static boolean
581 parse_exec (char **argv, int *arg_ptr)
583 return (insert_exec_ok ("-exec", pred_exec, argv, arg_ptr));
586 static boolean
587 parse_execdir (char **argv, int *arg_ptr)
589 return (insert_exec_ok ("-execdir", pred_execdir, argv, arg_ptr));
592 static boolean
593 parse_false (char **argv, int *arg_ptr)
595 struct predicate *our_pred;
597 (void) argv;
598 (void) arg_ptr;
600 our_pred = insert_primary (pred_false);
601 our_pred->need_stat = our_pred->need_type = false;
602 return (true);
605 static boolean
606 parse_fls (char **argv, int *arg_ptr)
608 struct predicate *our_pred;
610 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
611 return (false);
612 our_pred = insert_primary (pred_fls);
613 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
614 our_pred->side_effects = true;
615 our_pred->no_default_print = true;
616 (*arg_ptr)++;
617 return (true);
620 static boolean
621 parse_fprintf (char **argv, int *arg_ptr)
623 FILE *fp;
625 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
626 return (false);
627 if (argv[*arg_ptr + 1] == NULL)
629 /* Ensure we get "missing arg" message, not "invalid arg". */
630 (*arg_ptr)++;
631 return (false);
633 fp = open_output_file (argv[*arg_ptr]);
634 (*arg_ptr)++;
635 return (insert_fprintf (fp, pred_fprintf, argv, arg_ptr));
638 static boolean
639 parse_follow (char **argv, int *arg_ptr)
641 (void) argv;
642 (void) arg_ptr;
644 set_follow_state(SYMLINK_ALWAYS_DEREF);
645 return true;
648 static boolean
649 parse_fprint (char **argv, int *arg_ptr)
651 struct predicate *our_pred;
653 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
654 return false;
655 our_pred = insert_primary (pred_fprint);
656 our_pred->args.printf_vec.segment = NULL;
657 our_pred->args.printf_vec.stream = open_output_file (argv[*arg_ptr]);
658 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(our_pred->args.printf_vec.stream);
659 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
660 our_pred->side_effects = true;
661 our_pred->no_default_print = true;
662 our_pred->need_stat = our_pred->need_type = false;
663 (*arg_ptr)++;
664 return true;
667 static boolean
668 parse_fprint0 (char **argv, int *arg_ptr)
670 struct predicate *our_pred;
672 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
673 return (false);
674 our_pred = insert_primary (pred_fprint0);
675 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
676 our_pred->side_effects = true;
677 our_pred->no_default_print = true;
678 our_pred->need_stat = our_pred->need_type = false;
679 (*arg_ptr)++;
680 return (true);
683 static boolean
684 parse_fstype (char **argv, int *arg_ptr)
686 struct predicate *our_pred;
688 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
689 return (false);
690 our_pred = insert_primary (pred_fstype);
691 our_pred->args.str = argv[*arg_ptr];
692 (*arg_ptr)++;
693 return (true);
696 static boolean
697 parse_gid (char **argv, int *arg_ptr)
699 return (insert_num (argv, arg_ptr, pred_gid));
702 static boolean
703 parse_group (char **argv, int *arg_ptr)
705 struct group *cur_gr;
706 struct predicate *our_pred;
707 gid_t gid;
708 int gid_len;
710 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
711 return (false);
712 cur_gr = getgrnam (argv[*arg_ptr]);
713 endgrent ();
714 if (cur_gr != NULL)
715 gid = cur_gr->gr_gid;
716 else
718 gid_len = strspn (argv[*arg_ptr], "0123456789");
719 if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0'))
720 return (false);
721 gid = atoi (argv[*arg_ptr]);
723 our_pred = insert_primary (pred_group);
724 our_pred->args.gid = gid;
725 (*arg_ptr)++;
726 return (true);
729 static boolean
730 parse_help (char **argv, int *arg_ptr)
732 (void) argv;
733 (void) arg_ptr;
735 printf (_("\
736 Usage: %s [path...] [expression]\n"), program_name);
737 puts (_("\n\
738 default path is the current directory; default expression is -print\n\
739 expression may consist of: operators, options, tests, and actions:\n"));
740 puts (_("\
741 operators (decreasing precedence; -and is implicit where no others are given):\n\
742 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
743 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
744 puts (_("\
745 positional options (always true): -daystart -follow\n\
746 normal options (always true, specified before other expressions):\n\
747 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
748 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
749 puts (_("\
750 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
751 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
752 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
753 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
754 puts (_("\
755 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
756 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
757 -used N -user NAME -xtype [bcdpfls]\n"));
758 puts (_("\
759 actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\
760 -fls FILE -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls -delete\n\
761 -quit\n"));
762 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
763 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
764 email to <bug-findutils@gnu.org>."));
765 exit (0);
768 static boolean
769 parse_ilname (char **argv, int *arg_ptr)
771 struct predicate *our_pred;
773 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
774 return (false);
775 our_pred = insert_primary (pred_ilname);
776 our_pred->args.str = argv[*arg_ptr];
777 (*arg_ptr)++;
778 return (true);
782 /* sanity check the fnmatch() function to make sure
783 * it really is the GNU version.
785 static boolean
786 fnmatch_sanitycheck(void)
788 /* fprintf(stderr, "Performing find sanity check..."); */
789 if (0 != fnmatch("foo", "foo", 0)
790 || 0 == fnmatch("Foo", "foo", 0)
791 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
793 error (1, 0, _("sanity check of the fnmatch() library function failed."));
794 /* fprintf(stderr, "FAILED\n"); */
795 return false;
798 /* fprintf(stderr, "OK\n"); */
799 return true;
803 static boolean
804 check_name_arg(const char *pred, const char *arg)
806 if (strchr(arg, '/'))
808 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'."),
809 pred, arg, arg);
811 return true; /* allow it anyway */
816 static boolean
817 parse_iname (char **argv, int *arg_ptr)
819 struct predicate *our_pred;
821 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
822 return (false);
823 if (!check_name_arg("-iname", argv[*arg_ptr]))
824 return false;
826 fnmatch_sanitycheck();
828 our_pred = insert_primary (pred_iname);
829 our_pred->need_stat = our_pred->need_type = false;
830 our_pred->args.str = argv[*arg_ptr];
831 (*arg_ptr)++;
832 return (true);
835 static boolean
836 parse_inum (char **argv, int *arg_ptr)
838 return (insert_num (argv, arg_ptr, pred_inum));
841 /* -ipath is deprecated (at RMS's request) in favour of
842 * -iwholename. See the node "GNU Manuals" in standards.texi
843 * for the rationale for this (basically, GNU prefers the use
844 * of the phrase "file name" to "path name"
846 static boolean
847 parse_ipath (char **argv, int *arg_ptr)
849 error (0, 0,
850 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
852 return parse_iwholename(argv, arg_ptr);
855 static boolean
856 parse_iwholename (char **argv, int *arg_ptr)
858 struct predicate *our_pred;
860 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
861 return (false);
863 fnmatch_sanitycheck();
865 our_pred = insert_primary (pred_ipath);
866 our_pred->need_stat = our_pred->need_type = false;
867 our_pred->args.str = argv[*arg_ptr];
868 (*arg_ptr)++;
869 return (true);
872 static boolean
873 parse_iregex (char **argv, int *arg_ptr)
875 return insert_regex (argv, arg_ptr, true);
878 static boolean
879 parse_links (char **argv, int *arg_ptr)
881 return (insert_num (argv, arg_ptr, pred_links));
884 static boolean
885 parse_lname (char **argv, int *arg_ptr)
887 struct predicate *our_pred;
889 (void) argv;
890 (void) arg_ptr;
892 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
893 return (false);
895 fnmatch_sanitycheck();
897 our_pred = insert_primary (pred_lname);
898 our_pred->args.str = argv[*arg_ptr];
899 (*arg_ptr)++;
900 return (true);
903 static boolean
904 parse_ls (char **argv, int *arg_ptr)
906 struct predicate *our_pred;
908 (void) &argv;
909 (void) &arg_ptr;
911 our_pred = insert_primary (pred_ls);
912 our_pred->side_effects = true;
913 our_pred->no_default_print = true;
914 return (true);
917 static boolean
918 parse_maxdepth (char **argv, int *arg_ptr)
920 int depth_len;
922 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
923 return false;
924 depth_len = strspn (argv[*arg_ptr], "0123456789");
925 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
926 return false;
927 options.maxdepth = atoi (argv[*arg_ptr]);
928 if (options.maxdepth < 0)
929 return false;
930 (*arg_ptr)++;
931 return true;
934 static boolean
935 parse_mindepth (char **argv, int *arg_ptr)
937 int depth_len;
939 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
940 return false;
941 depth_len = strspn (argv[*arg_ptr], "0123456789");
942 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
943 return false;
944 options.mindepth = atoi (argv[*arg_ptr]);
945 if (options.mindepth < 0)
946 return false;
947 (*arg_ptr)++;
948 return true;
951 static boolean
952 parse_mmin (char **argv, int *arg_ptr)
954 struct predicate *our_pred;
955 uintmax_t num;
956 enum comparison_type c_type;
957 time_t t;
959 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
960 return (false);
961 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
962 return (false);
963 t = options.cur_day_start + DAYSECS - num * 60;
964 our_pred = insert_primary (pred_mmin);
965 our_pred->args.info.kind = c_type;
966 our_pred->args.info.negative = t < 0;
967 our_pred->args.info.l_val = t;
968 (*arg_ptr)++;
969 return (true);
972 static boolean
973 parse_mtime (char **argv, int *arg_ptr)
975 return (insert_time (argv, arg_ptr, pred_mtime));
978 static boolean
979 parse_name (char **argv, int *arg_ptr)
981 struct predicate *our_pred;
983 (void) argv;
984 (void) arg_ptr;
986 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
987 return (false);
988 if (!check_name_arg("-name", argv[*arg_ptr]))
989 return false;
990 fnmatch_sanitycheck();
992 our_pred = insert_primary (pred_name);
993 our_pred->need_stat = our_pred->need_type = false;
994 our_pred->args.str = argv[*arg_ptr];
995 (*arg_ptr)++;
996 return (true);
999 static boolean
1000 parse_negate (char **argv, int *arg_ptr)
1002 struct predicate *our_pred;
1004 (void) &argv;
1005 (void) &arg_ptr;
1007 our_pred = get_new_pred_chk_op ();
1008 our_pred->pred_func = pred_negate;
1009 #ifdef DEBUG
1010 our_pred->p_name = find_pred_name (pred_negate);
1011 #endif /* DEBUG */
1012 our_pred->p_type = UNI_OP;
1013 our_pred->p_prec = NEGATE_PREC;
1014 our_pred->need_stat = our_pred->need_type = false;
1015 return (true);
1018 static boolean
1019 parse_newer (char **argv, int *arg_ptr)
1021 struct predicate *our_pred;
1022 struct stat stat_newer;
1024 (void) argv;
1025 (void) arg_ptr;
1027 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1028 return (false);
1029 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
1030 error (1, errno, "%s", argv[*arg_ptr]);
1031 our_pred = insert_primary (pred_newer);
1032 our_pred->args.time = stat_newer.st_mtime;
1033 (*arg_ptr)++;
1034 return (true);
1037 static boolean
1038 parse_noleaf (char **argv, int *arg_ptr)
1040 (void) &argv;
1041 (void) &arg_ptr;
1043 options.no_leaf_check = true;
1044 return true;
1047 #ifdef CACHE_IDS
1048 /* Arbitrary amount by which to increase size
1049 of `uid_unused' and `gid_unused'. */
1050 #define ALLOC_STEP 2048
1052 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1053 char *uid_unused = NULL;
1055 /* Number of elements in `uid_unused'. */
1056 unsigned uid_allocated;
1058 /* Similar for GIDs and group entries. */
1059 char *gid_unused = NULL;
1060 unsigned gid_allocated;
1061 #endif
1063 static boolean
1064 parse_nogroup (char **argv, int *arg_ptr)
1066 struct predicate *our_pred;
1068 (void) &argv;
1069 (void) &arg_ptr;
1071 our_pred = insert_primary (pred_nogroup);
1072 #ifdef CACHE_IDS
1073 if (gid_unused == NULL)
1075 struct group *gr;
1077 gid_allocated = ALLOC_STEP;
1078 gid_unused = xmalloc (gid_allocated);
1079 memset (gid_unused, 1, gid_allocated);
1080 setgrent ();
1081 while ((gr = getgrent ()) != NULL)
1083 if ((unsigned) gr->gr_gid >= gid_allocated)
1085 unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
1086 gid_unused = xrealloc (gid_unused, new_allocated);
1087 memset (gid_unused + gid_allocated, 1,
1088 new_allocated - gid_allocated);
1089 gid_allocated = new_allocated;
1091 gid_unused[(unsigned) gr->gr_gid] = 0;
1093 endgrent ();
1095 #endif
1096 return (true);
1099 static boolean
1100 parse_nouser (char **argv, int *arg_ptr)
1102 struct predicate *our_pred;
1103 (void) argv;
1104 (void) arg_ptr;
1107 our_pred = insert_primary (pred_nouser);
1108 #ifdef CACHE_IDS
1109 if (uid_unused == NULL)
1111 struct passwd *pw;
1113 uid_allocated = ALLOC_STEP;
1114 uid_unused = xmalloc (uid_allocated);
1115 memset (uid_unused, 1, uid_allocated);
1116 setpwent ();
1117 while ((pw = getpwent ()) != NULL)
1119 if ((unsigned) pw->pw_uid >= uid_allocated)
1121 unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
1122 uid_unused = xrealloc (uid_unused, new_allocated);
1123 memset (uid_unused + uid_allocated, 1,
1124 new_allocated - uid_allocated);
1125 uid_allocated = new_allocated;
1127 uid_unused[(unsigned) pw->pw_uid] = 0;
1129 endpwent ();
1131 #endif
1132 return (true);
1135 static boolean
1136 parse_nowarn (char **argv, int *arg_ptr)
1138 (void) argv;
1139 (void) arg_ptr;
1141 options.warnings = false;
1142 return true;;
1145 static boolean
1146 parse_ok (char **argv, int *arg_ptr)
1148 return (insert_exec_ok ("-ok", pred_ok, argv, arg_ptr));
1151 static boolean
1152 parse_okdir (char **argv, int *arg_ptr)
1154 return (insert_exec_ok ("-okdir", pred_okdir, argv, arg_ptr));
1157 boolean
1158 parse_open (char **argv, int *arg_ptr)
1160 struct predicate *our_pred;
1162 (void) argv;
1163 (void) arg_ptr;
1165 our_pred = get_new_pred_chk_op ();
1166 our_pred->pred_func = pred_open;
1167 #ifdef DEBUG
1168 our_pred->p_name = find_pred_name (pred_open);
1169 #endif /* DEBUG */
1170 our_pred->p_type = OPEN_PAREN;
1171 our_pred->p_prec = NO_PREC;
1172 our_pred->need_stat = our_pred->need_type = false;
1173 return (true);
1176 static boolean
1177 parse_or (char **argv, int *arg_ptr)
1179 struct predicate *our_pred;
1181 (void) argv;
1182 (void) arg_ptr;
1184 our_pred = get_new_pred ();
1185 our_pred->pred_func = pred_or;
1186 #ifdef DEBUG
1187 our_pred->p_name = find_pred_name (pred_or);
1188 #endif /* DEBUG */
1189 our_pred->p_type = BI_OP;
1190 our_pred->p_prec = OR_PREC;
1191 our_pred->need_stat = our_pred->need_type = false;
1192 return (true);
1195 /* -path is deprecated (at RMS's request) in favour of
1196 * -iwholename. See the node "GNU Manuals" in standards.texi
1197 * for the rationale for this (basically, GNU prefers the use
1198 * of the phrase "file name" to "path name".
1200 * We do not issue a warning that this usage is deprecated
1201 * since HPUX find supports this predicate also.
1203 static boolean
1204 parse_path (char **argv, int *arg_ptr)
1206 return parse_wholename(argv, arg_ptr);
1209 static boolean
1210 parse_wholename (char **argv, int *arg_ptr)
1212 struct predicate *our_pred;
1214 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1215 return (false);
1216 our_pred = insert_primary (pred_path);
1217 our_pred->need_stat = our_pred->need_type = false;
1218 our_pred->args.str = argv[*arg_ptr];
1219 (*arg_ptr)++;
1220 return (true);
1223 static boolean
1224 parse_perm (char **argv, int *arg_ptr)
1226 mode_t perm_val;
1227 int mode_start = 0;
1228 enum permissions_type kind = PERM_EXACT;
1229 struct mode_change *change = NULL;
1230 struct predicate *our_pred;
1232 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1233 return (false);
1235 switch (argv[*arg_ptr][0])
1237 case '-':
1238 mode_start = 1;
1239 kind = PERM_AT_LEAST;
1240 break;
1242 case '+':
1243 change = mode_compile (argv[*arg_ptr]);
1244 if (NULL == change)
1246 /* Most likely the caller is an old script that is still
1247 * using the obsolete GNU syntax '-perm +MODE'. This old
1248 * syntax was withdrawn in favor of '-perm /MODE' because
1249 * it is incompatible with POSIX in some cases, but we
1250 * still support uses of it that are not incompatible with
1251 * POSIX.
1253 mode_start = 1;
1254 kind = PERM_ANY;
1256 else
1258 /* This is a POSIX-compatible usage */
1259 mode_start = 0;
1260 kind = PERM_EXACT;
1262 break;
1264 case '/': /* GNU extension */
1265 mode_start = 1;
1266 kind = PERM_ANY;
1267 break;
1269 default:
1270 /* For example, '-perm 0644', which is valid and matches
1271 * only files whose mode is exactly 0644.
1273 * We do nothing here, because mode_start and kind are already
1274 * correctly set.
1276 break;
1279 if (NULL == change)
1281 change = mode_compile (argv[*arg_ptr] + mode_start);
1282 if (NULL == change)
1283 error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
1285 perm_val = mode_adjust (0, change, 0);
1286 free (change);
1288 our_pred = insert_primary (pred_perm);
1290 switch (argv[*arg_ptr][0])
1292 case '-':
1293 our_pred->args.perm.kind = PERM_AT_LEAST;
1294 break;
1295 case '+':
1296 our_pred->args.perm.kind = PERM_ANY;
1297 break;
1298 default:
1299 our_pred->args.perm.kind = PERM_EXACT;
1300 break;
1302 our_pred->args.perm.val = perm_val & MODE_ALL;
1303 (*arg_ptr)++;
1304 return (true);
1307 boolean
1308 parse_print (char **argv, int *arg_ptr)
1310 struct predicate *our_pred;
1312 (void) argv;
1313 (void) arg_ptr;
1315 our_pred = insert_primary (pred_print);
1316 /* -print has the side effect of printing. This prevents us
1317 from doing undesired multiple printing when the user has
1318 already specified -print. */
1319 our_pred->side_effects = true;
1320 our_pred->no_default_print = true;
1321 our_pred->need_stat = our_pred->need_type = false;
1322 our_pred->args.printf_vec.segment = NULL;
1323 our_pred->args.printf_vec.stream = stdout;
1324 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(stdout);
1325 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
1327 return (true);
1330 static boolean
1331 parse_print0 (char **argv, int *arg_ptr)
1333 struct predicate *our_pred;
1335 (void) argv;
1336 (void) arg_ptr;
1338 our_pred = insert_primary (pred_print0);
1339 /* -print0 has the side effect of printing. This prevents us
1340 from doing undesired multiple printing when the user has
1341 already specified -print0. */
1342 our_pred->side_effects = true;
1343 our_pred->no_default_print = true;
1344 our_pred->need_stat = our_pred->need_type = false;
1345 return (true);
1348 static boolean
1349 parse_printf (char **argv, int *arg_ptr)
1351 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1352 return (false);
1353 return (insert_fprintf (stdout, pred_fprintf, argv, arg_ptr));
1356 static boolean
1357 parse_prune (char **argv, int *arg_ptr)
1359 struct predicate *our_pred;
1361 (void) argv;
1362 (void) arg_ptr;
1364 our_pred = insert_primary (pred_prune);
1365 our_pred->need_stat = our_pred->need_type = false;
1366 /* -prune has a side effect that it does not descend into
1367 the current directory. */
1368 our_pred->side_effects = true;
1369 return (true);
1372 static boolean
1373 parse_quit (char **argv, int *arg_ptr)
1375 struct predicate *our_pred = insert_primary (pred_quit);
1376 (void) argv;
1377 (void) arg_ptr;
1378 our_pred->need_stat = our_pred->need_type = false;
1379 return true;
1383 static boolean
1384 parse_regex (char **argv, int *arg_ptr)
1386 return insert_regex (argv, arg_ptr, false);
1389 static boolean
1390 insert_regex (char **argv, int *arg_ptr, boolean ignore_case)
1392 struct predicate *our_pred;
1393 struct re_pattern_buffer *re;
1394 const char *error_message;
1395 int opt = RE_SYNTAX_POSIX_BASIC;
1397 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1398 return (false);
1399 our_pred = insert_primary (pred_regex);
1400 our_pred->need_stat = our_pred->need_type = false;
1401 re = (struct re_pattern_buffer *)
1402 xmalloc (sizeof (struct re_pattern_buffer));
1403 our_pred->args.regex = re;
1404 re->allocated = 100;
1405 re->buffer = (unsigned char *) xmalloc (re->allocated);
1406 re->fastmap = NULL;
1408 if (ignore_case)
1409 opt |= RE_ICASE;
1411 re_set_syntax(opt);
1412 re->syntax = opt;
1413 re->translate = NULL;
1415 error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
1416 re);
1417 if (error_message)
1418 error (1, 0, "%s", error_message);
1419 (*arg_ptr)++;
1420 return (true);
1423 static boolean
1424 parse_size (char **argv, int *arg_ptr)
1426 struct predicate *our_pred;
1427 uintmax_t num;
1428 enum comparison_type c_type;
1429 int blksize = 512;
1430 int len;
1432 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1433 return (false);
1434 len = strlen (argv[*arg_ptr]);
1435 if (len == 0)
1436 error (1, 0, _("invalid null argument to -size"));
1437 switch (argv[*arg_ptr][len - 1])
1439 case 'b':
1440 blksize = 512;
1441 argv[*arg_ptr][len - 1] = '\0';
1442 break;
1444 case 'c':
1445 blksize = 1;
1446 argv[*arg_ptr][len - 1] = '\0';
1447 break;
1449 case 'k':
1450 blksize = 1024;
1451 argv[*arg_ptr][len - 1] = '\0';
1452 break;
1454 case 'M': /* Megabytes */
1455 blksize = 1024*1024;
1456 argv[*arg_ptr][len - 1] = '\0';
1457 break;
1459 case 'G': /* Gigabytes */
1460 blksize = 1024*1024*1024;
1461 argv[*arg_ptr][len - 1] = '\0';
1462 break;
1464 case 'w':
1465 blksize = 2;
1466 argv[*arg_ptr][len - 1] = '\0';
1467 break;
1469 case '0':
1470 case '1':
1471 case '2':
1472 case '3':
1473 case '4':
1474 case '5':
1475 case '6':
1476 case '7':
1477 case '8':
1478 case '9':
1479 break;
1481 default:
1482 error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
1484 if (!get_num (argv[*arg_ptr], &num, &c_type))
1485 return (false);
1486 our_pred = insert_primary (pred_size);
1487 our_pred->args.size.kind = c_type;
1488 our_pred->args.size.blocksize = blksize;
1489 our_pred->args.size.size = num;
1490 (*arg_ptr)++;
1491 return (true);
1495 static boolean
1496 parse_samefile (char **argv, int *arg_ptr)
1498 struct predicate *our_pred;
1499 struct stat st;
1501 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1502 return (false);
1503 if ((*options.xstat) (argv[*arg_ptr], &st))
1504 error (1, errno, "%s", argv[*arg_ptr]);
1506 our_pred = insert_primary (pred_samefile);
1507 our_pred->args.fileid.ino = st.st_ino;
1508 our_pred->args.fileid.dev = st.st_dev;
1509 our_pred->need_type = false;
1510 our_pred->need_stat = true;
1511 (*arg_ptr)++;
1512 return (true);
1516 static boolean
1517 parse_true (char **argv, int *arg_ptr)
1519 struct predicate *our_pred;
1521 (void) argv;
1522 (void) arg_ptr;
1524 our_pred = insert_primary (pred_true);
1525 our_pred->need_stat = our_pred->need_type = false;
1526 return (true);
1529 static boolean
1530 parse_type (char **argv, int *arg_ptr)
1532 return insert_type (argv, arg_ptr, pred_type);
1535 static boolean
1536 parse_uid (char **argv, int *arg_ptr)
1538 return (insert_num (argv, arg_ptr, pred_uid));
1541 static boolean
1542 parse_used (char **argv, int *arg_ptr)
1544 struct predicate *our_pred;
1545 uintmax_t num_days;
1546 enum comparison_type c_type;
1547 time_t t;
1549 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1550 return (false);
1551 if (!get_num (argv[*arg_ptr], &num_days, &c_type))
1552 return (false);
1553 t = num_days * DAYSECS;
1554 our_pred = insert_primary (pred_used);
1555 our_pred->args.info.kind = c_type;
1556 our_pred->args.info.negative = t < 0;
1557 our_pred->args.info.l_val = t;
1558 (*arg_ptr)++;
1559 return (true);
1562 static boolean
1563 parse_user (char **argv, int *arg_ptr)
1565 struct passwd *cur_pwd;
1566 struct predicate *our_pred;
1567 uid_t uid;
1568 int uid_len;
1570 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1571 return (false);
1572 cur_pwd = getpwnam (argv[*arg_ptr]);
1573 endpwent ();
1574 if (cur_pwd != NULL)
1575 uid = cur_pwd->pw_uid;
1576 else
1578 uid_len = strspn (argv[*arg_ptr], "0123456789");
1579 if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
1580 return (false);
1581 uid = atoi (argv[*arg_ptr]);
1583 our_pred = insert_primary (pred_user);
1584 our_pred->args.uid = uid;
1585 (*arg_ptr)++;
1586 return (true);
1589 static boolean
1590 parse_version (char **argv, int *arg_ptr)
1592 extern char *version_string;
1593 int features = 0;
1595 (void) argv;
1596 (void) arg_ptr;
1598 fflush (stderr);
1599 printf (_("GNU find version %s\n"), version_string);
1600 printf (_("Features enabled: "));
1602 #if CACHE_IDS
1603 printf("CACHE_IDS ");
1604 ++features;
1605 #endif
1606 #if DEBUG
1607 printf("DEBUG ");
1608 ++features;
1609 #endif
1610 #if DEBUG_STAT
1611 printf("DEBUG_STAT ");
1612 ++features;
1613 #endif
1614 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1615 printf("D_TYPE ");
1616 ++features;
1617 #endif
1618 #if defined(O_NOFOLLOW)
1619 printf("O_NOFOLLOW(%s) ",
1620 (options.open_nofollow_available ? "enabled" : "disabled"));
1621 ++features;
1622 #endif
1624 if (0 == features)
1626 /* For the moment, leave this as English in case someone wants
1627 to parse these strings. */
1628 printf("none");
1630 printf("\n");
1632 exit (0);
1635 static boolean
1636 parse_xdev (char **argv, int *arg_ptr)
1638 (void) argv;
1639 (void) arg_ptr;
1640 options.stay_on_filesystem = true;
1641 return true;
1644 static boolean
1645 parse_ignore_race (char **argv, int *arg_ptr)
1647 (void) argv;
1648 (void) arg_ptr;
1649 options.ignore_readdir_race = true;
1650 return true;
1653 static boolean
1654 parse_noignore_race (char **argv, int *arg_ptr)
1656 (void) argv;
1657 (void) arg_ptr;
1658 options.ignore_readdir_race = false;
1659 return true;
1662 static boolean
1663 parse_warn (char **argv, int *arg_ptr)
1665 (void) argv;
1666 (void) arg_ptr;
1667 options.warnings = true;
1668 return true;
1671 static boolean
1672 parse_xtype (char **argv, int *arg_ptr)
1674 (void) argv;
1675 (void) arg_ptr;
1676 return insert_type (argv, arg_ptr, pred_xtype);
1679 static boolean
1680 insert_type (char **argv, int *arg_ptr, PRED_FUNC which_pred)
1682 mode_t type_cell;
1683 struct predicate *our_pred;
1685 if ((argv == NULL) || (argv[*arg_ptr] == NULL)
1686 || (strlen (argv[*arg_ptr]) != 1))
1687 return (false);
1688 switch (argv[*arg_ptr][0])
1690 case 'b': /* block special */
1691 type_cell = S_IFBLK;
1692 break;
1693 case 'c': /* character special */
1694 type_cell = S_IFCHR;
1695 break;
1696 case 'd': /* directory */
1697 type_cell = S_IFDIR;
1698 break;
1699 case 'f': /* regular file */
1700 type_cell = S_IFREG;
1701 break;
1702 #ifdef S_IFLNK
1703 case 'l': /* symbolic link */
1704 type_cell = S_IFLNK;
1705 break;
1706 #endif
1707 #ifdef S_IFIFO
1708 case 'p': /* pipe */
1709 type_cell = S_IFIFO;
1710 break;
1711 #endif
1712 #ifdef S_IFSOCK
1713 case 's': /* socket */
1714 type_cell = S_IFSOCK;
1715 break;
1716 #endif
1717 #ifdef S_IFDOOR
1718 case 'D': /* Solaris door */
1719 type_cell = S_IFDOOR;
1720 break;
1721 #endif
1722 default: /* None of the above ... nuke 'em. */
1723 return (false);
1725 our_pred = insert_primary (which_pred);
1727 /* Figure out if we will need to stat the file, because if we don't
1728 * need to follow symlinks, we can avoid a stat call by using
1729 * struct dirent.d_type.
1731 if (which_pred == pred_xtype)
1733 our_pred->need_stat = true;
1734 our_pred->need_type = false;
1736 else
1738 our_pred->need_stat = false; /* struct dirent is enough */
1739 our_pred->need_type = true;
1741 our_pred->args.type = type_cell;
1742 (*arg_ptr)++; /* Move on to next argument. */
1743 return (true);
1747 /* Return true if the file accessed via FP is a terminal.
1749 static boolean
1750 stream_is_tty(FILE *fp)
1752 int fd = fileno(fp);
1753 if (-1 == fd)
1755 return false; /* not a valid stream */
1757 else
1759 return isatty(fd) ? true : false;
1766 /* If true, we've determined that the current fprintf predicate
1767 uses stat information. */
1768 static boolean fprintf_stat_needed;
1770 static boolean
1771 insert_fprintf (FILE *fp, PRED_FUNC func, char **argv, int *arg_ptr)
1773 char *format; /* Beginning of unprocessed format string. */
1774 register char *scan; /* Current address in scanning `format'. */
1775 register char *scan2; /* Address inside of element being scanned. */
1776 struct segment **segmentp; /* Address of current segment. */
1777 struct predicate *our_pred;
1779 format = argv[(*arg_ptr)++];
1781 fprintf_stat_needed = false; /* Might be overridden later. */
1782 our_pred = insert_primary (func);
1783 our_pred->side_effects = true;
1784 our_pred->no_default_print = true;
1785 our_pred->args.printf_vec.stream = fp;
1786 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(fp);
1787 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
1788 segmentp = &our_pred->args.printf_vec.segment;
1789 *segmentp = NULL;
1791 for (scan = format; *scan; scan++)
1793 if (*scan == '\\')
1795 scan2 = scan + 1;
1796 if (*scan2 >= '0' && *scan2 <= '7')
1798 register int n, i;
1800 for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
1801 i++, scan2++)
1802 n = 8 * n + *scan2 - '0';
1803 scan2--;
1804 *scan = n;
1806 else
1808 switch (*scan2)
1810 case 'a':
1811 *scan = 7;
1812 break;
1813 case 'b':
1814 *scan = '\b';
1815 break;
1816 case 'c':
1817 make_segment (segmentp, format, scan - format, KIND_STOP);
1818 our_pred->need_stat = fprintf_stat_needed;
1819 return (true);
1820 case 'f':
1821 *scan = '\f';
1822 break;
1823 case 'n':
1824 *scan = '\n';
1825 break;
1826 case 'r':
1827 *scan = '\r';
1828 break;
1829 case 't':
1830 *scan = '\t';
1831 break;
1832 case 'v':
1833 *scan = '\v';
1834 break;
1835 case '\\':
1836 /* *scan = '\\'; * it already is */
1837 break;
1838 default:
1839 error (0, 0,
1840 _("warning: unrecognized escape `\\%c'"), *scan2);
1841 scan++;
1842 continue;
1845 segmentp = make_segment (segmentp, format, scan - format + 1,
1846 KIND_PLAIN);
1847 format = scan2 + 1; /* Move past the escape. */
1848 scan = scan2; /* Incremented immediately by `for'. */
1850 else if (*scan == '%')
1852 if (scan[1] == '%')
1854 segmentp = make_segment (segmentp, format, scan - format + 1,
1855 KIND_PLAIN);
1856 scan++;
1857 format = scan + 1;
1858 continue;
1860 /* Scan past flags, width and precision, to verify kind. */
1861 for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
1862 /* Do nothing. */ ;
1863 while (ISDIGIT (*scan2))
1864 scan2++;
1865 if (*scan2 == '.')
1866 for (scan2++; ISDIGIT (*scan2); scan2++)
1867 /* Do nothing. */ ;
1868 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2))
1870 segmentp = make_segment (segmentp, format, scan2 - format,
1871 (int) *scan2);
1872 scan = scan2;
1873 format = scan + 1;
1875 else if (strchr ("ACT", *scan2) && scan2[1])
1877 segmentp = make_segment (segmentp, format, scan2 - format,
1878 *scan2 | (scan2[1] << 8));
1879 scan = scan2 + 1;
1880 format = scan + 1;
1881 continue;
1883 else
1885 /* An unrecognized % escape. Print the char after the %. */
1886 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1887 *scan2);
1888 segmentp = make_segment (segmentp, format, scan - format,
1889 KIND_PLAIN);
1890 format = scan + 1;
1891 continue;
1896 if (scan > format)
1897 make_segment (segmentp, format, scan - format, KIND_PLAIN);
1898 our_pred->need_type = false;
1899 our_pred->need_stat = fprintf_stat_needed;
1900 return (true);
1903 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1904 from the text in FORMAT, which has length LEN.
1905 Return the address of the `next' pointer of the new segment. */
1907 static struct segment **
1908 make_segment (struct segment **segment, char *format, int len, int kind)
1910 char *fmt;
1912 *segment = (struct segment *) xmalloc (sizeof (struct segment));
1914 (*segment)->kind = kind;
1915 (*segment)->next = NULL;
1916 (*segment)->text_len = len;
1918 fmt = (*segment)->text = xmalloc (len + sizeof "d");
1919 strncpy (fmt, format, len);
1920 fmt += len;
1922 switch (kind & 0xff)
1924 case KIND_PLAIN: /* Plain text string, no % conversion. */
1925 case KIND_STOP: /* Terminate argument, no newline. */
1926 break;
1928 case 'a': /* atime in `ctime' format */
1929 case 'A': /* atime in user-specified strftime format */
1930 case 'c': /* ctime in `ctime' format */
1931 case 'C': /* ctime in user-specified strftime format */
1932 case 'F': /* filesystem type */
1933 case 'g': /* group name */
1934 case 'i': /* inode number */
1935 case 'l': /* object of symlink */
1936 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1937 case 's': /* size in bytes */
1938 case 't': /* mtime in `ctime' format */
1939 case 'T': /* mtime in user-specified strftime format */
1940 case 'u': /* user name */
1941 case 'y': /* file type */
1942 case 'Y': /* symlink pointed file type */
1943 fprintf_stat_needed = true;
1944 /* FALLTHROUGH */
1945 case 'f': /* basename of path */
1946 case 'h': /* leading directories part of path */
1947 case 'H': /* ARGV element file was found under */
1948 case 'p': /* pathname */
1949 case 'P': /* pathname with ARGV element stripped */
1950 *fmt++ = 's';
1951 break;
1953 /* Numeric items that one might expect to honour
1954 * #, 0, + flags but which do not.
1956 case 'G': /* GID number */
1957 case 'U': /* UID number */
1958 case 'b': /* size in 512-byte blocks */
1959 case 'D': /* Filesystem device on which the file exits */
1960 case 'k': /* size in 1K blocks */
1961 case 'n': /* number of links */
1962 fprintf_stat_needed = true;
1963 *fmt++ = 's';
1964 break;
1966 /* Numeric items that DO honour #, 0, + flags.
1968 case 'd': /* depth in search tree (0 = ARGV element) */
1969 *fmt++ = 'd';
1970 break;
1972 case 'm': /* mode as octal number (perms only) */
1973 *fmt++ = 'o';
1974 fprintf_stat_needed = true;
1975 break;
1977 *fmt = '\0';
1979 return (&(*segment)->next);
1982 static void
1983 check_path_safety(const char *action)
1985 const char *path = getenv("PATH");
1986 char *s;
1987 s = next_element(path, 1);
1988 while ((s = next_element ((char *) NULL, 1)) != NULL)
1990 if (0 == strcmp(s, "."))
1992 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)"),
1993 action);
1999 /* handles both exec and ok predicate */
2000 #if defined(NEW_EXEC)
2001 /* handles both exec and ok predicate */
2002 static boolean
2003 new_insert_exec_ok (const char *action,
2004 PRED_FUNC func,
2005 char **argv,
2006 int *arg_ptr)
2008 int start, end; /* Indexes in ARGV of start & end of cmd. */
2009 int i; /* Index into cmd args */
2010 int saw_braces; /* True if previous arg was '{}'. */
2011 boolean allow_plus; /* True if + is a valid terminator */
2012 int brace_count; /* Number of instances of {}. */
2014 struct predicate *our_pred;
2015 struct exec_val *execp; /* Pointer for efficiency. */
2017 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2018 return (false);
2020 our_pred = insert_primary (func);
2021 our_pred->side_effects = true;
2022 our_pred->no_default_print = true;
2023 execp = &our_pred->args.exec_vec;
2025 if ((func != pred_okdir) && (func != pred_ok))
2026 allow_plus = true;
2027 else
2028 allow_plus = false;
2030 if ((func == pred_execdir) || (func == pred_okdir))
2032 options.ignore_readdir_race = false;
2033 check_path_safety(action);
2034 execp->use_current_dir = true;
2036 else
2038 execp->use_current_dir = false;
2041 our_pred->args.exec_vec.multiple = 0;
2043 /* Count the number of args with path replacements, up until the ';'.
2044 * Also figure out if the command is terminated by ";" or by "+".
2046 start = *arg_ptr;
2047 for (end = start, saw_braces=0, brace_count=0;
2048 (argv[end] != NULL)
2049 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2050 end++)
2052 /* For -exec and -execdir, "{} +" can terminate the command. */
2053 if ( allow_plus
2054 && argv[end][0] == '+' && argv[end][1] == 0
2055 && saw_braces)
2057 our_pred->args.exec_vec.multiple = 1;
2058 break;
2061 saw_braces = 0;
2062 if (strstr (argv[end], "{}"))
2064 saw_braces = 1;
2065 ++brace_count;
2067 if (0 == end && (func == pred_execdir || func == pred_okdir))
2069 /* The POSIX standard says that {} replacement should
2070 * occur even in the utility name. This is insecure
2071 * since it means we will be executing a command whose
2072 * name is chosen according to whatever find finds in
2073 * the filesystem. That can be influenced by an
2074 * attacker. Hence for -execdir and -okdir this is not
2075 * allowed. We can specify this as those options are
2076 * not defined by POSIX.
2078 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2083 /* Fail if no command given or no semicolon found. */
2084 if ((end == start) || (argv[end] == NULL))
2086 *arg_ptr = end;
2087 free(our_pred);
2088 return false;
2091 if (our_pred->args.exec_vec.multiple && brace_count > 1)
2094 const char *suffix;
2095 if (func == pred_execdir)
2096 suffix = "dir";
2097 else
2098 suffix = "";
2100 error(1, 0,
2101 _("Only one instance of {} is supported with -exec%s ... +"),
2102 suffix);
2105 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2106 bc_init_controlinfo(&execp->ctl);
2107 execp->ctl.exec_callback = launch;
2109 if (our_pred->args.exec_vec.multiple)
2111 /* "+" terminator, so we can just append our arguments after the
2112 * command and initial arguments.
2114 execp->replace_vec = NULL;
2115 execp->ctl.replace_pat = NULL;
2116 execp->ctl.rplen = 0;
2117 execp->ctl.lines_per_exec = 0; /* no limit */
2118 execp->ctl.args_per_exec = 0; /* no limit */
2120 /* remember how many arguments there are */
2121 execp->ctl.initial_argc = (end-start) - 1;
2123 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2124 bc_init_state(&execp->ctl, &execp->state, execp);
2126 /* Gather the initial arguments. Skip the {}. */
2127 for (i=start; i<end-1; ++i)
2129 bc_push_arg(&execp->ctl, &execp->state,
2130 argv[i], strlen(argv[i])+1,
2131 NULL, 0,
2135 else
2137 /* Semicolon terminator - more than one {} is supported, so we
2138 * have to do brace-replacement.
2140 execp->num_args = end - start;
2142 execp->ctl.replace_pat = "{}";
2143 execp->ctl.rplen = strlen(execp->ctl.replace_pat);
2144 execp->ctl.lines_per_exec = 0; /* no limit */
2145 execp->ctl.args_per_exec = 0; /* no limit */
2146 execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
2149 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2150 bc_init_state(&execp->ctl, &execp->state, execp);
2152 /* Remember the (pre-replacement) arguments for later. */
2153 for (i=0; i<execp->num_args; ++i)
2155 execp->replace_vec[i] = argv[i+start];
2159 if (argv[end] == NULL)
2160 *arg_ptr = end;
2161 else
2162 *arg_ptr = end + 1;
2164 return true;
2166 #else
2167 /* handles both exec and ok predicate */
2168 static boolean
2169 old_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
2171 int start, end; /* Indexes in ARGV of start & end of cmd. */
2172 int num_paths; /* Number of args with path replacements. */
2173 int path_pos; /* Index in array of path replacements. */
2174 int vec_pos; /* Index in array of args. */
2175 struct predicate *our_pred;
2176 struct exec_val *execp; /* Pointer for efficiency. */
2178 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2179 return (false);
2181 /* Count the number of args with path replacements, up until the ';'. */
2182 start = *arg_ptr;
2183 for (end = start, num_paths = 0;
2184 (argv[end] != NULL)
2185 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2186 end++)
2187 if (strstr (argv[end], "{}"))
2188 num_paths++;
2189 /* Fail if no command given or no semicolon found. */
2190 if ((end == start) || (argv[end] == NULL))
2192 *arg_ptr = end;
2193 return (false);
2196 our_pred = insert_primary (func);
2197 our_pred->side_effects = true;
2198 our_pred->no_default_print = true;
2199 execp = &our_pred->args.exec_vec;
2200 execp->usercontext = our_pred;
2201 execp->use_current_dir = false;
2202 execp->paths =
2203 (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1));
2204 execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1));
2205 /* Record the positions of all args, and the args with path replacements. */
2206 for (end = start, path_pos = vec_pos = 0;
2207 (argv[end] != NULL)
2208 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2209 end++)
2211 register char *p;
2213 execp->paths[path_pos].count = 0;
2214 for (p = argv[end]; *p; ++p)
2215 if (p[0] == '{' && p[1] == '}')
2217 execp->paths[path_pos].count++;
2218 ++p;
2220 if (execp->paths[path_pos].count)
2222 execp->paths[path_pos].offset = vec_pos;
2223 execp->paths[path_pos].origarg = argv[end];
2224 path_pos++;
2226 execp->vec[vec_pos++] = argv[end];
2228 execp->paths[path_pos].offset = -1;
2229 execp->vec[vec_pos] = NULL;
2231 if (argv[end] == NULL)
2232 *arg_ptr = end;
2233 else
2234 *arg_ptr = end + 1;
2235 return (true);
2237 #endif
2241 static boolean
2242 insert_exec_ok (const char *action, PRED_FUNC func, char **argv, int *arg_ptr)
2244 #if defined(NEW_EXEC)
2245 return new_insert_exec_ok(action, func, argv, arg_ptr);
2246 #else
2247 return old_insert_exec_ok(func, argv, arg_ptr);
2248 #endif
2253 /* Get a number of days and comparison type.
2254 STR is the ASCII representation.
2255 Set *NUM_DAYS to the number of days, taken as being from
2256 the current moment (or possibly midnight). Thus the sense of the
2257 comparison type appears to be reversed.
2258 Set *COMP_TYPE to the kind of comparison that is requested.
2260 Return true if all okay, false if input error.
2262 Used by -atime, -ctime and -mtime (parsers) to
2263 get the appropriate information for a time predicate processor. */
2265 static boolean
2266 get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
2268 boolean r = get_num (str, num_days, comp_type);
2269 if (r)
2270 switch (*comp_type)
2272 case COMP_LT: *comp_type = COMP_GT; break;
2273 case COMP_GT: *comp_type = COMP_LT; break;
2274 default: break;
2276 return r;
2279 /* Insert a time predicate PRED.
2280 ARGV is a pointer to the argument array.
2281 ARG_PTR is a pointer to an index into the array, incremented if
2282 all went well.
2284 Return true if input is valid, false if not.
2286 A new predicate node is assigned, along with an argument node
2287 obtained with malloc.
2289 Used by -atime, -ctime, and -mtime parsers. */
2291 static boolean
2292 insert_time (char **argv, int *arg_ptr, PRED_FUNC pred)
2294 struct predicate *our_pred;
2295 uintmax_t num_days;
2296 enum comparison_type c_type;
2297 time_t t;
2299 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2300 return (false);
2301 if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
2302 return (false);
2304 /* Figure out the timestamp value we are looking for. */
2305 t = ( options.cur_day_start - num_days * DAYSECS
2306 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2308 if (1)
2310 /* We introduce a scope in which 'val' can be declared, for the
2311 * benefit of compilers that are really C89 compilers
2312 * which support intmax_t because config.h #defines it
2314 intmax_t val = ( (intmax_t)options.cur_day_start - num_days * DAYSECS
2315 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2316 t = val;
2318 /* Check for possibility of an overflow */
2319 if ( (intmax_t)t != val )
2321 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv[*arg_ptr]);
2325 our_pred = insert_primary (pred);
2326 our_pred->args.info.kind = c_type;
2327 our_pred->args.info.negative = t < 0;
2328 our_pred->args.info.l_val = t;
2329 (*arg_ptr)++;
2330 #ifdef DEBUG
2331 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2332 fprintf (stderr, " type: %s %s ",
2333 (c_type == COMP_GT) ? "gt" :
2334 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2335 (c_type == COMP_GT) ? " >" :
2336 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
2337 t = our_pred->args.info.l_val;
2338 fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2339 if (c_type == COMP_EQ)
2341 t = our_pred->args.info.l_val += DAYSECS;
2342 fprintf (stderr, " < %ju %s",
2343 (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2344 our_pred->args.info.l_val -= DAYSECS;
2346 #endif /* DEBUG */
2347 return (true);
2350 /* Get a number with comparision information.
2351 The sense of the comparision information is 'normal'; that is,
2352 '+' looks for a count > than the number and '-' less than.
2354 STR is the ASCII representation of the number.
2355 Set *NUM to the number.
2356 Set *COMP_TYPE to the kind of comparison that is requested.
2358 Return true if all okay, false if input error. */
2360 static boolean
2361 get_num (char *str, uintmax_t *num, enum comparison_type *comp_type)
2363 if (str == NULL)
2364 return (false);
2365 switch (str[0])
2367 case '+':
2368 *comp_type = COMP_GT;
2369 str++;
2370 break;
2371 case '-':
2372 *comp_type = COMP_LT;
2373 str++;
2374 break;
2375 default:
2376 *comp_type = COMP_EQ;
2377 break;
2380 return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK;
2383 /* Insert a number predicate.
2384 ARGV is a pointer to the argument array.
2385 *ARG_PTR is an index into ARGV, incremented if all went well.
2386 *PRED is the predicate processor to insert.
2388 Return true if input is valid, false if error.
2390 A new predicate node is assigned, along with an argument node
2391 obtained with malloc.
2393 Used by -inum and -links parsers. */
2395 static boolean
2396 insert_num (char **argv, int *arg_ptr, PRED_FUNC pred)
2398 struct predicate *our_pred;
2399 uintmax_t num;
2400 enum comparison_type c_type;
2402 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2403 return (false);
2404 if (!get_num (argv[*arg_ptr], &num, &c_type))
2405 return (false);
2406 our_pred = insert_primary (pred);
2407 our_pred->args.info.kind = c_type;
2408 our_pred->args.info.l_val = num;
2409 (*arg_ptr)++;
2410 #ifdef DEBUG
2411 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2412 fprintf (stderr, " type: %s %s ",
2413 (c_type == COMP_GT) ? "gt" :
2414 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2415 (c_type == COMP_GT) ? " >" :
2416 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
2417 fprintf (stderr, "%ju\n", our_pred->args.info.l_val);
2418 #endif /* DEBUG */
2419 return (true);
2422 static FILE *
2423 open_output_file (char *path)
2425 FILE *f;
2427 if (!strcmp (path, "/dev/stderr"))
2428 return (stderr);
2429 else if (!strcmp (path, "/dev/stdout"))
2430 return (stdout);
2431 f = fopen (path, "w");
2432 if (f == NULL)
2433 error (1, errno, "%s", path);
2434 return (f);