Findutils 4.3.x defaults to using the the FTS implementation of find.
[findutils.git] / find / parser.c
blob889d4370402dd2c7d2d959fa9512225b7a533517
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"
35 #include "stdio-safer.h"
36 #include "regextype.h"
38 #ifdef HAVE_FCNTL_H
39 #include <fcntl.h>
40 #else
41 #include <sys/file.h>
42 #endif
44 /* The presence of unistd.h is assumed by gnulib these days, so we
45 * might as well assume it too.
47 /* We need <unistd.h> for isatty(). */
48 #include <unistd.h>
50 #if ENABLE_NLS
51 # include <libintl.h>
52 # define _(Text) gettext (Text)
53 #else
54 # define _(Text) Text
55 #endif
56 #ifdef gettext_noop
57 # define N_(String) gettext_noop (String)
58 #else
59 /* See locate.c for explanation as to why not use (String) */
60 # define N_(String) String
61 #endif
63 #if !defined (isascii) || defined (STDC_HEADERS)
64 #ifdef isascii
65 #undef isascii
66 #endif
67 #define isascii(c) 1
68 #endif
70 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
71 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
73 #ifndef HAVE_ENDGRENT
74 #define endgrent()
75 #endif
76 #ifndef HAVE_ENDPWENT
77 #define endpwent()
78 #endif
80 static boolean parse_amin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
81 static boolean parse_and PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
82 static boolean parse_anewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
83 static boolean parse_atime PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
84 static boolean parse_cmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
85 static boolean parse_cnewer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
86 static boolean parse_comma PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
87 static boolean parse_ctime PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
88 static boolean parse_daystart PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
89 static boolean parse_delete PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
90 static boolean parse_d PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
91 static boolean parse_depth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
92 static boolean parse_empty PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
93 static boolean parse_exec PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
94 static boolean parse_execdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
95 static boolean parse_false PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
96 static boolean parse_fls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
97 static boolean parse_fprintf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
98 static boolean parse_follow PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
99 static boolean parse_fprint PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
100 static boolean parse_fprint0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
101 static boolean parse_fstype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
102 static boolean parse_gid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
103 static boolean parse_group PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
104 static boolean parse_help PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
105 static boolean parse_ilname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
106 static boolean parse_iname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
107 static boolean parse_inum PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
108 static boolean parse_ipath PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
109 static boolean parse_iregex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
110 static boolean parse_iwholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
111 static boolean parse_links PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
112 static boolean parse_lname PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
113 static boolean parse_ls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
114 static boolean parse_maxdepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
115 static boolean parse_mindepth PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
116 static boolean parse_mmin PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
117 static boolean parse_mtime PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
118 static boolean parse_name PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
119 static boolean parse_negate PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
120 static boolean parse_newer PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
121 static boolean parse_noleaf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
122 static boolean parse_nogroup PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
123 static boolean parse_nouser PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
124 static boolean parse_nowarn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
125 static boolean parse_ok PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
126 static boolean parse_okdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
127 static boolean parse_or PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
128 static boolean parse_path PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
129 static boolean parse_perm PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
130 static boolean parse_print0 PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
131 static boolean parse_printf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
132 static boolean parse_prune PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
133 static boolean parse_regex PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
134 static boolean parse_regextype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
135 static boolean parse_samefile PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
136 static boolean parse_size PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
137 static boolean parse_true PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
138 static boolean parse_type PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
139 static boolean parse_uid PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
140 static boolean parse_used PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
141 static boolean parse_user PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
142 static boolean parse_version PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
143 static boolean parse_wholename PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
144 static boolean parse_xdev PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
145 static boolean parse_ignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
146 static boolean parse_noignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
147 static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
148 static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
149 static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
153 boolean parse_print PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
154 boolean parse_open PARAMS((const struct parser_table* entry, char *argv[], int *arg_ptr));
155 boolean parse_close PARAMS((const struct parser_table* entry, char *argv[], int *arg_ptr));
159 static boolean insert_type PARAMS((char **argv, int *arg_ptr, const struct parser_table *entry, PRED_FUNC which_pred));
160 static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, const struct parser_table *entry, int regex_options));
161 static boolean insert_fprintf PARAMS((FILE *fp, const struct parser_table *entry, PRED_FUNC func, char *argv[], int *arg_ptr));
163 static struct segment **make_segment PARAMS((struct segment **segment, char *format, int len, int kind));
164 static boolean insert_exec_ok PARAMS((const char *action, const struct parser_table *entry, char *argv[], int *arg_ptr));
165 static boolean get_num_days PARAMS((char *str, uintmax_t *num_days, enum comparison_type *comp_type));
166 static boolean insert_time PARAMS((char **argv, int *arg_ptr, const struct parser_table* entry, PRED_FUNC pred));
167 static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type));
168 static boolean insert_num PARAMS((char *argv[], int *arg_ptr, const struct parser_table *entry));
169 static FILE *open_output_file PARAMS((char *path));
170 static boolean stream_is_tty(FILE *fp);
172 #ifdef DEBUG
173 char *find_pred_name PARAMS((PRED_FUNC pred_func));
174 #endif /* DEBUG */
177 #define PASTE(x,y) x##y
178 #define STRINGIFY(s) #s
180 #define PARSE_OPTION(what,suffix) \
181 { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL }
183 #define PARSE_POSOPT(what,suffix) \
184 { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL }
186 #define PARSE_TEST(what,suffix) \
187 { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
189 #define PARSE_TEST_NP(what,suffix) \
190 { (ARG_TEST), (what), PASTE(parse_,suffix), NULL }
192 #define PARSE_ACTION(what,suffix) \
193 { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
195 #define PARSE_ACTION_NP(what,suffix) \
196 { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL }
198 #define PARSE_PUNCTUATION(what,suffix) \
199 { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
202 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
203 If they are in some Unix versions of find, they are marked `Unix'. */
205 static struct parser_table const parse_table[] =
207 PARSE_PUNCTUATION("!", negate),
208 PARSE_PUNCTUATION("not", negate), /* GNU */
209 PARSE_PUNCTUATION("(", open),
210 PARSE_PUNCTUATION(")", close),
211 PARSE_PUNCTUATION(",", comma), /* GNU */
212 PARSE_PUNCTUATION("a", and),
213 PARSE_TEST ("amin", amin), /* GNU */
214 PARSE_PUNCTUATION("and", and), /* GNU */
215 PARSE_TEST ("anewer", anewer), /* GNU */
216 PARSE_TEST ("atime", atime),
217 PARSE_TEST ("cmin", cmin), /* GNU */
218 PARSE_TEST ("cnewer", cnewer), /* GNU */
219 PARSE_TEST ("ctime", ctime),
220 PARSE_POSOPT ("daystart", daystart), /* GNU */
221 PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
222 PARSE_OPTION ("d", d), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
223 PARSE_OPTION ("depth", depth),
224 PARSE_TEST ("empty", empty), /* GNU */
225 PARSE_ACTION ("exec", exec),
226 PARSE_ACTION ("execdir", execdir), /* *BSD, GNU */
227 PARSE_ACTION ("fls", fls), /* GNU */
228 PARSE_POSOPT ("follow", follow), /* GNU, Unix */
229 PARSE_ACTION ("fprint", fprint), /* GNU */
230 PARSE_ACTION ("fprint0", fprint0), /* GNU */
231 PARSE_ACTION ("fprintf", fprintf), /* GNU */
232 PARSE_TEST ("fstype", fstype), /* GNU, Unix */
233 PARSE_TEST ("gid", gid), /* GNU */
234 PARSE_TEST ("group", group),
235 PARSE_OPTION ("ignore_readdir_race", ignore_race), /* GNU */
236 PARSE_TEST ("ilname", ilname), /* GNU */
237 PARSE_TEST ("iname", iname), /* GNU */
238 PARSE_TEST ("inum", inum), /* GNU, Unix */
239 PARSE_TEST ("ipath", ipath), /* GNU, deprecated in favour of iwholename */
240 PARSE_TEST_NP ("iregex", iregex), /* GNU */
241 PARSE_TEST_NP ("iwholename", iwholename), /* GNU */
242 PARSE_TEST ("links", links),
243 PARSE_TEST ("lname", lname), /* GNU */
244 PARSE_ACTION ("ls", ls), /* GNU, Unix */
245 PARSE_OPTION ("maxdepth", maxdepth), /* GNU */
246 PARSE_OPTION ("mindepth", mindepth), /* GNU */
247 PARSE_TEST ("mmin", mmin), /* GNU */
248 PARSE_OPTION ("mount", xdev), /* Unix */
249 PARSE_TEST ("mtime", mtime),
250 PARSE_TEST ("name", name),
251 #ifdef UNIMPLEMENTED_UNIX
252 PARSE(ARG_UNIMPLEMENTED, "ncpio", ncpio), /* Unix */
253 #endif
254 PARSE_TEST ("newer", newer),
255 PARSE_OPTION ("noleaf", noleaf), /* GNU */
256 PARSE_TEST ("nogroup", nogroup),
257 PARSE_TEST ("nouser", nouser),
258 PARSE_OPTION ("noignore_readdir_race", noignore_race), /* GNU */
259 PARSE_OPTION ("nowarn", nowarn), /* GNU */
260 PARSE_PUNCTUATION("o", or),
261 PARSE_PUNCTUATION("or", or), /* GNU */
262 PARSE_ACTION ("ok", ok),
263 PARSE_ACTION ("okdir", okdir), /* GNU (-execdir is BSD) */
264 PARSE_TEST ("path", path), /* GNU, HP-UX, GNU prefers wholename */
265 PARSE_TEST ("perm", perm),
266 PARSE_ACTION ("print", print),
267 PARSE_ACTION ("print0", print0), /* GNU */
268 PARSE_ACTION_NP ("printf", printf), /* GNU */
269 PARSE_ACTION ("prune", prune),
270 PARSE_ACTION ("quit", quit), /* GNU */
271 PARSE_TEST ("regex", regex), /* GNU */
272 PARSE_OPTION ("regextype", regextype), /* GNU */
273 PARSE_TEST ("samefile", samefile), /* GNU */
274 PARSE_TEST ("size", size),
275 PARSE_TEST ("type", type),
276 PARSE_TEST ("uid", uid), /* GNU */
277 PARSE_TEST ("used", used), /* GNU */
278 PARSE_TEST ("user", user),
279 PARSE_OPTION ("warn", warn), /* GNU */
280 PARSE_TEST_NP ("wholename", wholename), /* GNU, replaces -path */
281 PARSE_OPTION ("xdev", xdev),
282 PARSE_TEST ("xtype", xtype), /* GNU */
283 #ifdef UNIMPLEMENTED_UNIX
284 /* It's pretty ugly for find to know about archive formats.
285 Plus what it could do with cpio archives is very limited.
286 Better to leave it out. */
287 PARSE(ARG_UNIMPLEMENTED, "cpio", cpio), /* Unix */
288 #endif
289 /* gnulib's stdbool.h might have made true and false into macros,
290 * so we can't leave named 'true' and 'false' tokens, so we have
291 * to expeant the relevant entries longhand.
293 {ARG_TEST, "false", parse_false, pred_false}, /* GNU */
294 {ARG_TEST, "true", parse_true, pred_true }, /* GNU */
296 /* Various other cases that don't fit neatly into our macro scheme. */
297 {ARG_TEST, "help", parse_help, NULL}, /* GNU */
298 {ARG_TEST, "-help", parse_help, NULL}, /* GNU */
299 {ARG_TEST, "version", parse_version, NULL}, /* GNU */
300 {ARG_TEST, "-version", parse_version, NULL}, /* GNU */
301 {0, 0, 0, 0}
305 static const char *first_nonoption_arg = NULL;
310 void
311 set_follow_state(enum SymlinkOption opt)
313 switch (opt)
315 case SYMLINK_ALWAYS_DEREF: /* -L */
316 options.xstat = optionl_stat;
317 options.no_leaf_check = true;
318 break;
320 case SYMLINK_NEVER_DEREF: /* -P (default) */
321 options.xstat = optionp_stat;
322 /* Can't turn no_leaf_check off because the user might have specified
323 * -noleaf anyway
325 break;
327 case SYMLINK_DEREF_ARGSONLY: /* -H */
328 options.xstat = optionh_stat;
329 options.no_leaf_check = true;
332 options.symlink_handling = opt;
334 /* For DEBUG_STAT, the choice is made at runtime within debug_stat()
335 * by checking the contents of the symlink_handling variable.
337 #if defined(DEBUG_STAT)
338 options.xstat = debug_stat;
339 #endif /* !DEBUG_STAT */
343 void
344 parse_begin_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates)
346 (void) args;
347 (void) argno;
348 (void) last;
349 (void) predicates;
350 first_nonoption_arg = NULL;
353 void
354 parse_end_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates)
356 /* does nothing */
357 (void) args;
358 (void) argno;
359 (void) last;
360 (void) predicates;
366 /* Return a pointer to the parser function to invoke for predicate
367 SEARCH_NAME.
368 Return NULL if SEARCH_NAME is not a valid predicate name. */
370 const struct parser_table*
371 find_parser (char *search_name)
373 int i;
374 const char *original_arg = search_name;
376 if (*search_name == '-')
377 search_name++;
378 for (i = 0; parse_table[i].parser_name != 0; i++)
380 if (strcmp (parse_table[i].parser_name, search_name) == 0)
382 /* If this is an option, but we have already had a
383 * non-option argument, the user may be under the
384 * impression that the behaviour of the option
385 * argument is conditional on some preceding
386 * tests. This might typically be the case with,
387 * for example, -maxdepth.
389 * The options -daystart and -follow are exempt
390 * from this treatment, since their positioning
391 * in the command line does have an effect on
392 * subsequent tests but not previous ones. That
393 * might be intentional on the part of the user.
395 if (parse_table[i].type != ARG_POSITIONAL_OPTION)
397 /* Something other than -follow/-daystart.
398 * If this is an option, check if it followed
399 * a non-option and if so, issue a warning.
401 if (parse_table[i].type == ARG_OPTION)
403 if ((first_nonoption_arg != NULL)
404 && options.warnings )
406 /* option which follows a non-option */
407 error (0, 0,
408 _("warning: you have specified the %s "
409 "option after a non-option argument %s, "
410 "but options are not positional (%s affects "
411 "tests specified before it as well as those "
412 "specified after it). Please specify options "
413 "before other arguments.\n"),
414 original_arg,
415 first_nonoption_arg,
416 original_arg);
419 else
421 /* Not an option or a positional option,
422 * so remember we've seen it in order to
423 * use it in a possible future warning message.
425 if (first_nonoption_arg == NULL)
427 first_nonoption_arg = original_arg;
432 return &parse_table[i];
435 return NULL;
438 /* The parsers are responsible to continue scanning ARGV for
439 their arguments. Each parser knows what is and isn't
440 allowed for itself.
442 ARGV is the argument array.
443 *ARG_PTR is the index to start at in ARGV,
444 updated to point beyond the last element consumed.
446 The predicate structure is updated with the new information. */
448 static boolean
449 parse_amin (const struct parser_table* entry, 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 (entry);
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_and (const struct parser_table* entry, char **argv, int *arg_ptr)
472 struct predicate *our_pred;
474 (void) argv;
475 (void) arg_ptr;
477 our_pred = get_new_pred (entry);
478 our_pred->pred_func = pred_and;
479 #ifdef DEBUG
480 our_pred->p_name = find_pred_name (pred_and);
481 #endif /* DEBUG */
482 our_pred->p_type = BI_OP;
483 our_pred->p_prec = AND_PREC;
484 our_pred->need_stat = our_pred->need_type = false;
485 return true;
488 static boolean
489 parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr)
491 struct predicate *our_pred;
492 struct stat stat_newer;
494 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
495 return false;
496 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
497 error (1, errno, "%s", argv[*arg_ptr]);
498 our_pred = insert_primary (entry);
499 our_pred->args.time = stat_newer.st_mtime;
500 (*arg_ptr)++;
501 return true;
504 static boolean
505 parse_atime (const struct parser_table* entry, char **argv, int *arg_ptr)
507 return insert_time (argv, arg_ptr, entry, pred_atime);
510 boolean
511 parse_close (const struct parser_table* entry, char **argv, int *arg_ptr)
513 struct predicate *our_pred;
515 (void) argv;
516 (void) arg_ptr;
518 our_pred = get_new_pred (entry);
519 our_pred->pred_func = pred_close;
520 #ifdef DEBUG
521 our_pred->p_name = find_pred_name (pred_close);
522 #endif /* DEBUG */
523 our_pred->p_type = CLOSE_PAREN;
524 our_pred->p_prec = NO_PREC;
525 our_pred->need_stat = our_pred->need_type = false;
526 return true;
529 static boolean
530 parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr)
532 struct predicate *our_pred;
533 uintmax_t num;
534 enum comparison_type c_type;
535 time_t t;
537 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
538 return false;
539 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
540 return false;
541 t = options.cur_day_start + DAYSECS - num * 60;
542 our_pred = insert_primary (entry);
543 our_pred->args.info.kind = c_type;
544 our_pred->args.info.negative = t < 0;
545 our_pred->args.info.l_val = t;
546 (*arg_ptr)++;
547 return true;
550 static boolean
551 parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr)
553 struct predicate *our_pred;
554 struct stat stat_newer;
556 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
557 return false;
558 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
559 error (1, errno, "%s", argv[*arg_ptr]);
560 our_pred = insert_primary (entry);
561 our_pred->args.time = stat_newer.st_mtime;
562 (*arg_ptr)++;
563 return true;
566 static boolean
567 parse_comma (const struct parser_table* entry, char **argv, int *arg_ptr)
569 struct predicate *our_pred;
571 (void) argv;
572 (void) arg_ptr;
574 our_pred = get_new_pred (entry);
575 our_pred->pred_func = pred_comma;
576 #ifdef DEBUG
577 our_pred->p_name = find_pred_name (pred_comma);
578 #endif /* DEBUG */
579 our_pred->p_type = BI_OP;
580 our_pred->p_prec = COMMA_PREC;
581 our_pred->need_stat = our_pred->need_type = false;
582 return true;
585 static boolean
586 parse_ctime (const struct parser_table* entry, char **argv, int *arg_ptr)
588 return insert_time (argv, arg_ptr, entry, pred_ctime);
591 static boolean
592 parse_daystart (const struct parser_table* entry, char **argv, int *arg_ptr)
594 struct tm *local;
596 (void) entry;
597 (void) argv;
598 (void) arg_ptr;
600 if (options.full_days == false)
602 options.cur_day_start += DAYSECS;
603 local = localtime (&options.cur_day_start);
604 options.cur_day_start -= (local
605 ? (local->tm_sec + local->tm_min * 60
606 + local->tm_hour * 3600)
607 : options.cur_day_start % DAYSECS);
608 options.full_days = true;
610 return true;
613 static boolean
614 parse_delete (const struct parser_table* entry, char *argv[], int *arg_ptr)
616 struct predicate *our_pred;
617 (void) argv;
618 (void) arg_ptr;
620 our_pred = insert_primary (entry);
621 our_pred->side_effects = our_pred->no_default_print = true;
622 /* -delete implies -depth */
623 options.do_dir_first = false;
624 return true;
627 static boolean
628 parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr)
630 (void) entry;
631 (void) argv;
632 (void) arg_ptr;
634 options.do_dir_first = false;
635 return true;
638 static boolean
639 parse_d (const struct parser_table* entry, char **argv, int *arg_ptr)
641 (void) argv;
642 (void) arg_ptr;
644 if (options.warnings)
646 error (0, 0,
647 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
649 return parse_depth(entry, argv, arg_ptr);
652 static boolean
653 parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr)
655 (void) argv;
656 (void) arg_ptr;
658 insert_primary (entry);
659 return true;
662 static boolean
663 parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr)
665 return insert_exec_ok ("-exec", entry, argv, arg_ptr);
668 static boolean
669 parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr)
671 return insert_exec_ok ("-execdir", entry, argv, arg_ptr);
674 static boolean
675 parse_false (const struct parser_table* entry, char **argv, int *arg_ptr)
677 struct predicate *our_pred;
679 (void) argv;
680 (void) arg_ptr;
682 our_pred = insert_primary (entry);
683 our_pred->need_stat = our_pred->need_type = false;
684 our_pred->side_effects = our_pred->no_default_print = false;
685 return true;
688 static boolean
689 parse_fls (const struct parser_table* entry, char **argv, int *arg_ptr)
691 struct predicate *our_pred;
693 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
694 return false;
695 our_pred = insert_primary (entry);
696 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
697 our_pred->side_effects = our_pred->no_default_print = true;
698 (*arg_ptr)++;
699 return true;
702 static boolean
703 parse_fprintf (const struct parser_table* entry, char **argv, int *arg_ptr)
705 FILE *fp;
707 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
708 return false;
709 if (argv[*arg_ptr + 1] == NULL)
711 /* Ensure we get "missing arg" message, not "invalid arg". */
712 (*arg_ptr)++;
713 return false;
715 fp = open_output_file (argv[*arg_ptr]);
716 (*arg_ptr)++;
717 return insert_fprintf (fp, entry, pred_fprintf, argv, arg_ptr);
720 static boolean
721 parse_follow (const struct parser_table* entry, char **argv, int *arg_ptr)
723 (void) entry;
724 (void) argv;
725 (void) arg_ptr;
727 set_follow_state(SYMLINK_ALWAYS_DEREF);
728 return true;
731 static boolean
732 parse_fprint (const struct parser_table* entry, char **argv, int *arg_ptr)
734 struct predicate *our_pred;
736 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
737 return false;
738 our_pred = insert_primary (entry);
739 our_pred->args.printf_vec.segment = NULL;
740 our_pred->args.printf_vec.stream = open_output_file (argv[*arg_ptr]);
741 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(our_pred->args.printf_vec.stream);
742 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
743 our_pred->side_effects = our_pred->no_default_print = true;
744 our_pred->need_stat = our_pred->need_type = false;
745 (*arg_ptr)++;
746 return true;
749 static boolean
750 parse_fprint0 (const struct parser_table* entry, char **argv, int *arg_ptr)
752 struct predicate *our_pred;
754 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
755 return false;
756 our_pred = insert_primary (entry);
757 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
758 our_pred->side_effects = our_pred->no_default_print = true;
759 our_pred->need_stat = our_pred->need_type = false;
760 (*arg_ptr)++;
761 return true;
764 static boolean
765 parse_fstype (const struct parser_table* entry, char **argv, int *arg_ptr)
767 struct predicate *our_pred;
769 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
770 return false;
771 our_pred = insert_primary (entry);
772 our_pred->args.str = argv[*arg_ptr];
773 (*arg_ptr)++;
774 return true;
777 static boolean
778 parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr)
780 return insert_num (argv, arg_ptr, entry);
783 static boolean
784 parse_group (const struct parser_table* entry, char **argv, int *arg_ptr)
786 struct group *cur_gr;
787 struct predicate *our_pred;
788 gid_t gid;
789 int gid_len;
791 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
792 return false;
793 cur_gr = getgrnam (argv[*arg_ptr]);
794 endgrent ();
795 if (cur_gr != NULL)
796 gid = cur_gr->gr_gid;
797 else
799 gid_len = strspn (argv[*arg_ptr], "0123456789");
800 if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0'))
801 return false;
802 gid = atoi (argv[*arg_ptr]);
804 our_pred = insert_primary (entry);
805 our_pred->args.gid = gid;
806 (*arg_ptr)++;
807 return true;
810 static boolean
811 parse_help (const struct parser_table* entry, char **argv, int *arg_ptr)
813 (void) entry;
814 (void) argv;
815 (void) arg_ptr;
817 printf (_("\
818 Usage: %s [path...] [expression]\n"), program_name);
819 puts (_("\n\
820 default path is the current directory; default expression is -print\n\
821 expression may consist of: operators, options, tests, and actions:\n"));
822 puts (_("\
823 operators (decreasing precedence; -and is implicit where no others are given):\n\
824 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
825 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
826 puts (_("\
827 positional options (always true): -daystart -follow -regextype\n\n\
828 normal options (always true, specified before other expressions):\n\
829 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
830 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
831 puts (_("\
832 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
833 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
834 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
835 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
836 puts (_("\
837 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
838 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
839 -used N -user NAME -xtype [bcdpfls]\n"));
840 puts (_("\
841 actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
842 -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
843 -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
844 -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
845 "));
846 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
847 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
848 email to <bug-findutils@gnu.org>."));
849 exit (0);
852 static boolean
853 parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr)
855 struct predicate *our_pred;
857 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
858 return false;
859 our_pred = insert_primary (entry);
860 our_pred->args.str = argv[*arg_ptr];
861 (*arg_ptr)++;
862 return true;
866 /* sanity check the fnmatch() function to make sure
867 * it really is the GNU version.
869 static boolean
870 fnmatch_sanitycheck(void)
872 /* fprintf(stderr, "Performing find sanity check..."); */
873 if (0 != fnmatch("foo", "foo", 0)
874 || 0 == fnmatch("Foo", "foo", 0)
875 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
877 error (1, 0, _("sanity check of the fnmatch() library function failed."));
878 /* fprintf(stderr, "FAILED\n"); */
879 return false;
882 /* fprintf(stderr, "OK\n"); */
883 return true;
887 static boolean
888 check_name_arg(const char *pred, const char *arg)
890 if (strchr(arg, '/'))
892 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'."),
893 pred, arg, arg);
895 return true; /* allow it anyway */
900 static boolean
901 parse_iname (const struct parser_table* entry, char **argv, int *arg_ptr)
903 struct predicate *our_pred;
905 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
906 return false;
907 if (!check_name_arg("-iname", argv[*arg_ptr]))
908 return false;
910 fnmatch_sanitycheck();
912 our_pred = insert_primary (entry);
913 our_pred->need_stat = our_pred->need_type = false;
914 our_pred->args.str = argv[*arg_ptr];
915 (*arg_ptr)++;
916 return true;
919 static boolean
920 parse_inum (const struct parser_table* entry, char **argv, int *arg_ptr)
922 return insert_num (argv, arg_ptr, entry);
925 /* -ipath is deprecated (at RMS's request) in favour of
926 * -iwholename. See the node "GNU Manuals" in standards.texi
927 * for the rationale for this (basically, GNU prefers the use
928 * of the phrase "file name" to "path name"
930 static boolean
931 parse_ipath (const struct parser_table* entry, char **argv, int *arg_ptr)
933 error (0, 0,
934 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
936 return parse_iwholename(entry, argv, arg_ptr);
939 static boolean
940 parse_iwholename (const struct parser_table* entry, char **argv, int *arg_ptr)
942 struct predicate *our_pred;
944 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
945 return false;
947 fnmatch_sanitycheck();
949 our_pred = insert_primary_withpred (entry, pred_ipath);
950 our_pred->need_stat = our_pred->need_type = false;
951 our_pred->args.str = argv[*arg_ptr];
952 (*arg_ptr)++;
953 return true;
956 static boolean
957 parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr)
959 return insert_regex (argv, arg_ptr, entry, RE_ICASE|options.regex_options);
962 static boolean
963 parse_links (const struct parser_table* entry, char **argv, int *arg_ptr)
965 return insert_num (argv, arg_ptr, entry);
968 static boolean
969 parse_lname (const struct parser_table* entry, char **argv, int *arg_ptr)
971 struct predicate *our_pred;
973 (void) argv;
974 (void) arg_ptr;
976 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
977 return false;
979 fnmatch_sanitycheck();
981 our_pred = insert_primary (entry);
982 our_pred->args.str = argv[*arg_ptr];
983 (*arg_ptr)++;
984 return true;
987 static boolean
988 parse_ls (const struct parser_table* entry, char **argv, int *arg_ptr)
990 struct predicate *our_pred;
992 (void) &argv;
993 (void) &arg_ptr;
995 our_pred = insert_primary (entry);
996 our_pred->side_effects = our_pred->no_default_print = true;
997 return true;
1000 static boolean
1001 parse_maxdepth (const struct parser_table* entry, char **argv, int *arg_ptr)
1003 int depth_len;
1004 (void) entry;
1006 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1007 return false;
1008 depth_len = strspn (argv[*arg_ptr], "0123456789");
1009 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
1010 return false;
1011 options.maxdepth = atoi (argv[*arg_ptr]);
1012 if (options.maxdepth < 0)
1013 return false;
1014 (*arg_ptr)++;
1015 return true;
1018 static boolean
1019 parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr)
1021 int depth_len;
1022 (void) entry;
1024 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1025 return false;
1026 depth_len = strspn (argv[*arg_ptr], "0123456789");
1027 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
1028 return false;
1029 options.mindepth = atoi (argv[*arg_ptr]);
1030 if (options.mindepth < 0)
1031 return false;
1032 (*arg_ptr)++;
1033 return true;
1036 static boolean
1037 parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr)
1039 struct predicate *our_pred;
1040 uintmax_t num;
1041 enum comparison_type c_type;
1042 time_t t;
1044 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1045 return false;
1046 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
1047 return false;
1048 t = options.cur_day_start + DAYSECS - num * 60;
1049 our_pred = insert_primary (entry);
1050 our_pred->args.info.kind = c_type;
1051 our_pred->args.info.negative = t < 0;
1052 our_pred->args.info.l_val = t;
1053 (*arg_ptr)++;
1054 return true;
1057 static boolean
1058 parse_mtime (const struct parser_table* entry, char **argv, int *arg_ptr)
1060 return insert_time (argv, arg_ptr, entry, pred_mtime);
1063 static boolean
1064 parse_name (const struct parser_table* entry, char **argv, int *arg_ptr)
1066 struct predicate *our_pred;
1068 (void) argv;
1069 (void) arg_ptr;
1071 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1072 return false;
1073 if (!check_name_arg("-name", argv[*arg_ptr]))
1074 return false;
1075 fnmatch_sanitycheck();
1077 our_pred = insert_primary (entry);
1078 our_pred->need_stat = our_pred->need_type = false;
1079 our_pred->args.str = argv[*arg_ptr];
1080 (*arg_ptr)++;
1081 return true;
1084 static boolean
1085 parse_negate (const struct parser_table* entry, char **argv, int *arg_ptr)
1087 struct predicate *our_pred;
1089 (void) &argv;
1090 (void) &arg_ptr;
1092 our_pred = get_new_pred_chk_op (entry);
1093 our_pred->pred_func = pred_negate;
1094 #ifdef DEBUG
1095 our_pred->p_name = find_pred_name (pred_negate);
1096 #endif /* DEBUG */
1097 our_pred->p_type = UNI_OP;
1098 our_pred->p_prec = NEGATE_PREC;
1099 our_pred->need_stat = our_pred->need_type = false;
1100 return true;
1103 static boolean
1104 parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr)
1106 struct predicate *our_pred;
1107 struct stat stat_newer;
1109 (void) argv;
1110 (void) arg_ptr;
1112 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1113 return false;
1114 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
1115 error (1, errno, "%s", argv[*arg_ptr]);
1116 our_pred = insert_primary (entry);
1117 our_pred->args.time = stat_newer.st_mtime;
1118 (*arg_ptr)++;
1119 return true;
1122 static boolean
1123 parse_noleaf (const struct parser_table* entry, char **argv, int *arg_ptr)
1125 (void) &argv;
1126 (void) &arg_ptr;
1127 (void) entry;
1129 options.no_leaf_check = true;
1130 return true;
1133 #ifdef CACHE_IDS
1134 /* Arbitrary amount by which to increase size
1135 of `uid_unused' and `gid_unused'. */
1136 #define ALLOC_STEP 2048
1138 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1139 char *uid_unused = NULL;
1141 /* Number of elements in `uid_unused'. */
1142 unsigned uid_allocated;
1144 /* Similar for GIDs and group entries. */
1145 char *gid_unused = NULL;
1146 unsigned gid_allocated;
1147 #endif
1149 static boolean
1150 parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr)
1152 struct predicate *our_pred;
1154 (void) &argv;
1155 (void) &arg_ptr;
1157 our_pred = insert_primary (entry);
1158 #ifdef CACHE_IDS
1159 if (gid_unused == NULL)
1161 struct group *gr;
1163 gid_allocated = ALLOC_STEP;
1164 gid_unused = xmalloc (gid_allocated);
1165 memset (gid_unused, 1, gid_allocated);
1166 setgrent ();
1167 while ((gr = getgrent ()) != NULL)
1169 if ((unsigned) gr->gr_gid >= gid_allocated)
1171 unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
1172 gid_unused = xrealloc (gid_unused, new_allocated);
1173 memset (gid_unused + gid_allocated, 1,
1174 new_allocated - gid_allocated);
1175 gid_allocated = new_allocated;
1177 gid_unused[(unsigned) gr->gr_gid] = 0;
1179 endgrent ();
1181 #endif
1182 return true;
1185 static boolean
1186 parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr)
1188 struct predicate *our_pred;
1189 (void) argv;
1190 (void) arg_ptr;
1193 our_pred = insert_primary (entry);
1194 #ifdef CACHE_IDS
1195 if (uid_unused == NULL)
1197 struct passwd *pw;
1199 uid_allocated = ALLOC_STEP;
1200 uid_unused = xmalloc (uid_allocated);
1201 memset (uid_unused, 1, uid_allocated);
1202 setpwent ();
1203 while ((pw = getpwent ()) != NULL)
1205 if ((unsigned) pw->pw_uid >= uid_allocated)
1207 unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
1208 uid_unused = xrealloc (uid_unused, new_allocated);
1209 memset (uid_unused + uid_allocated, 1,
1210 new_allocated - uid_allocated);
1211 uid_allocated = new_allocated;
1213 uid_unused[(unsigned) pw->pw_uid] = 0;
1215 endpwent ();
1217 #endif
1218 return true;
1221 static boolean
1222 parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr)
1224 (void) argv;
1225 (void) arg_ptr;
1226 (void) entry;
1228 options.warnings = false;
1229 return true;;
1232 static boolean
1233 parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr)
1235 return insert_exec_ok ("-ok", entry, argv, arg_ptr);
1238 static boolean
1239 parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr)
1241 return insert_exec_ok ("-okdir", entry, argv, arg_ptr);
1244 boolean
1245 parse_open (const struct parser_table* entry, char **argv, int *arg_ptr)
1247 struct predicate *our_pred;
1249 (void) argv;
1250 (void) arg_ptr;
1252 our_pred = get_new_pred_chk_op (entry);
1253 our_pred->pred_func = pred_open;
1254 #ifdef DEBUG
1255 our_pred->p_name = find_pred_name (pred_open);
1256 #endif /* DEBUG */
1257 our_pred->p_type = OPEN_PAREN;
1258 our_pred->p_prec = NO_PREC;
1259 our_pred->need_stat = our_pred->need_type = false;
1260 return true;
1263 static boolean
1264 parse_or (const struct parser_table* entry, char **argv, int *arg_ptr)
1266 struct predicate *our_pred;
1268 (void) argv;
1269 (void) arg_ptr;
1271 our_pred = get_new_pred (entry);
1272 our_pred->pred_func = pred_or;
1273 #ifdef DEBUG
1274 our_pred->p_name = find_pred_name (pred_or);
1275 #endif /* DEBUG */
1276 our_pred->p_type = BI_OP;
1277 our_pred->p_prec = OR_PREC;
1278 our_pred->need_stat = our_pred->need_type = false;
1279 return true;
1282 /* -path is deprecated (at RMS's request) in favour of
1283 * -iwholename. See the node "GNU Manuals" in standards.texi
1284 * for the rationale for this (basically, GNU prefers the use
1285 * of the phrase "file name" to "path name".
1287 * We do not issue a warning that this usage is deprecated
1288 * since HPUX find supports this predicate also.
1290 static boolean
1291 parse_path (const struct parser_table* entry, char **argv, int *arg_ptr)
1293 return parse_wholename(entry, argv, arg_ptr);
1296 static boolean
1297 parse_wholename (const struct parser_table* entry, char **argv, int *arg_ptr)
1299 struct predicate *our_pred;
1301 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1302 return false;
1303 our_pred = insert_primary_withpred (entry, pred_path);
1304 our_pred->need_stat = our_pred->need_type = false;
1305 our_pred->args.str = argv[*arg_ptr];
1306 (*arg_ptr)++;
1307 return true;
1310 static boolean
1311 parse_perm (const struct parser_table* entry, char **argv, int *arg_ptr)
1313 mode_t perm_val;
1314 int mode_start = 0;
1315 boolean havekind = false;
1316 enum permissions_type kind = PERM_EXACT;
1317 struct mode_change *change = NULL;
1318 struct predicate *our_pred;
1320 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1321 return false;
1323 switch (argv[*arg_ptr][0])
1325 case '-':
1326 mode_start = 1;
1327 kind = PERM_AT_LEAST;
1328 havekind = true;
1329 break;
1331 case '+':
1332 change = mode_compile (argv[*arg_ptr]);
1333 if (NULL == change)
1335 /* Most likely the caller is an old script that is still
1336 * using the obsolete GNU syntax '-perm +MODE'. This old
1337 * syntax was withdrawn in favor of '-perm /MODE' because
1338 * it is incompatible with POSIX in some cases, but we
1339 * still support uses of it that are not incompatible with
1340 * POSIX.
1342 mode_start = 1;
1343 kind = PERM_ANY;
1345 else
1347 /* This is a POSIX-compatible usage */
1348 mode_start = 0;
1349 kind = PERM_EXACT;
1351 havekind = true;
1352 break;
1354 case '/': /* GNU extension */
1355 mode_start = 1;
1356 kind = PERM_ANY;
1357 havekind = true;
1358 break;
1360 default:
1361 /* For example, '-perm 0644', which is valid and matches
1362 * only files whose mode is exactly 0644.
1364 * We do nothing here, because mode_start and kind are already
1365 * correctly set.
1367 break;
1370 if (NULL == change)
1372 change = mode_compile (argv[*arg_ptr] + mode_start);
1373 if (NULL == change)
1374 error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
1376 perm_val = mode_adjust (0, change, 0);
1377 free (change);
1379 our_pred = insert_primary (entry);
1381 if (havekind)
1383 our_pred->args.perm.kind = kind;
1385 else
1388 switch (argv[*arg_ptr][0])
1390 case '-':
1391 our_pred->args.perm.kind = PERM_AT_LEAST;
1392 break;
1393 case '+':
1394 our_pred->args.perm.kind = PERM_ANY;
1395 break;
1396 default:
1397 our_pred->args.perm.kind = PERM_EXACT;
1398 break;
1401 our_pred->args.perm.val = perm_val & MODE_ALL;
1402 (*arg_ptr)++;
1403 return true;
1406 boolean
1407 parse_print (const struct parser_table* entry, char **argv, int *arg_ptr)
1409 struct predicate *our_pred;
1411 (void) argv;
1412 (void) arg_ptr;
1414 our_pred = insert_primary (entry);
1415 /* -print has the side effect of printing. This prevents us
1416 from doing undesired multiple printing when the user has
1417 already specified -print. */
1418 our_pred->side_effects = our_pred->no_default_print = true;
1419 our_pred->need_stat = our_pred->need_type = false;
1420 our_pred->args.printf_vec.segment = NULL;
1421 our_pred->args.printf_vec.stream = stdout;
1422 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(stdout);
1423 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
1425 return true;
1428 static boolean
1429 parse_print0 (const struct parser_table* entry, char **argv, int *arg_ptr)
1431 struct predicate *our_pred;
1433 (void) argv;
1434 (void) arg_ptr;
1436 our_pred = insert_primary (entry);
1437 /* -print0 has the side effect of printing. This prevents us
1438 from doing undesired multiple printing when the user has
1439 already specified -print0. */
1440 our_pred->side_effects = our_pred->no_default_print = true;
1441 our_pred->need_stat = our_pred->need_type = false;
1442 return true;
1445 static boolean
1446 parse_printf (const struct parser_table* entry, char **argv, int *arg_ptr)
1448 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1449 return false;
1450 return insert_fprintf (stdout, entry, pred_fprintf, argv, arg_ptr);
1453 static boolean
1454 parse_prune (const struct parser_table* entry, char **argv, int *arg_ptr)
1456 struct predicate *our_pred;
1458 (void) argv;
1459 (void) arg_ptr;
1461 our_pred = insert_primary (entry);
1462 our_pred->need_stat = our_pred->need_type = false;
1463 /* -prune has a side effect that it does not descend into
1464 the current directory. */
1465 our_pred->side_effects = true;
1466 our_pred->no_default_print = false;
1467 return true;
1470 static boolean
1471 parse_quit (const struct parser_table* entry, char **argv, int *arg_ptr)
1473 struct predicate *our_pred = insert_primary (entry);
1474 (void) argv;
1475 (void) arg_ptr;
1476 our_pred->need_stat = our_pred->need_type = false;
1477 our_pred->side_effects = true; /* Exiting is a side effect... */
1478 our_pred->no_default_print = false; /* Don't inhibit the default print, though. */
1479 return true;
1483 static boolean
1484 parse_regextype (const struct parser_table* entry, char **argv, int *arg_ptr)
1486 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1487 return false;
1489 /* collect the regex type name */
1490 options.regex_options = get_regex_type(argv[*arg_ptr]);
1491 (*arg_ptr)++;
1493 return true;
1497 static boolean
1498 parse_regex (const struct parser_table* entry, char **argv, int *arg_ptr)
1500 return insert_regex (argv, arg_ptr, entry, options.regex_options);
1503 static boolean
1504 insert_regex (char **argv, int *arg_ptr, const struct parser_table *entry, int regex_options)
1506 struct predicate *our_pred;
1507 struct re_pattern_buffer *re;
1508 const char *error_message;
1510 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1511 return false;
1512 our_pred = insert_primary_withpred (entry, pred_regex);
1513 our_pred->need_stat = our_pred->need_type = false;
1514 re = (struct re_pattern_buffer *)
1515 xmalloc (sizeof (struct re_pattern_buffer));
1516 our_pred->args.regex = re;
1517 re->allocated = 100;
1518 re->buffer = (unsigned char *) xmalloc (re->allocated);
1519 re->fastmap = NULL;
1521 re_set_syntax(regex_options);
1522 re->syntax = regex_options;
1523 re->translate = NULL;
1525 error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
1526 re);
1527 if (error_message)
1528 error (1, 0, "%s", error_message);
1529 (*arg_ptr)++;
1530 return true;
1533 static boolean
1534 parse_size (const struct parser_table* entry, char **argv, int *arg_ptr)
1536 struct predicate *our_pred;
1537 uintmax_t num;
1538 enum comparison_type c_type;
1539 int blksize = 512;
1540 int len;
1542 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1543 return false;
1544 len = strlen (argv[*arg_ptr]);
1545 if (len == 0)
1546 error (1, 0, _("invalid null argument to -size"));
1547 switch (argv[*arg_ptr][len - 1])
1549 case 'b':
1550 blksize = 512;
1551 argv[*arg_ptr][len - 1] = '\0';
1552 break;
1554 case 'c':
1555 blksize = 1;
1556 argv[*arg_ptr][len - 1] = '\0';
1557 break;
1559 case 'k':
1560 blksize = 1024;
1561 argv[*arg_ptr][len - 1] = '\0';
1562 break;
1564 case 'M': /* Megabytes */
1565 blksize = 1024*1024;
1566 argv[*arg_ptr][len - 1] = '\0';
1567 break;
1569 case 'G': /* Gigabytes */
1570 blksize = 1024*1024*1024;
1571 argv[*arg_ptr][len - 1] = '\0';
1572 break;
1574 case 'w':
1575 blksize = 2;
1576 argv[*arg_ptr][len - 1] = '\0';
1577 break;
1579 case '0':
1580 case '1':
1581 case '2':
1582 case '3':
1583 case '4':
1584 case '5':
1585 case '6':
1586 case '7':
1587 case '8':
1588 case '9':
1589 break;
1591 default:
1592 error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
1594 if (!get_num (argv[*arg_ptr], &num, &c_type))
1595 return false;
1596 our_pred = insert_primary (entry);
1597 our_pred->args.size.kind = c_type;
1598 our_pred->args.size.blocksize = blksize;
1599 our_pred->args.size.size = num;
1600 (*arg_ptr)++;
1601 return true;
1605 static boolean
1606 parse_samefile (const struct parser_table* entry, char **argv, int *arg_ptr)
1608 struct predicate *our_pred;
1609 struct stat st;
1611 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1612 return false;
1613 if ((*options.xstat) (argv[*arg_ptr], &st))
1614 error (1, errno, "%s", argv[*arg_ptr]);
1616 our_pred = insert_primary (entry);
1617 our_pred->args.fileid.ino = st.st_ino;
1618 our_pred->args.fileid.dev = st.st_dev;
1619 our_pred->need_type = false;
1620 our_pred->need_stat = true;
1621 (*arg_ptr)++;
1622 return true;
1626 static boolean
1627 parse_true (const struct parser_table* entry, char **argv, int *arg_ptr)
1629 struct predicate *our_pred;
1631 (void) argv;
1632 (void) arg_ptr;
1634 our_pred = insert_primary (entry);
1635 our_pred->need_stat = our_pred->need_type = false;
1636 return true;
1639 static boolean
1640 parse_type (const struct parser_table* entry, char **argv, int *arg_ptr)
1642 return insert_type (argv, arg_ptr, entry, pred_type);
1645 static boolean
1646 parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr)
1648 return insert_num (argv, arg_ptr, entry);
1651 static boolean
1652 parse_used (const struct parser_table* entry, char **argv, int *arg_ptr)
1654 struct predicate *our_pred;
1655 uintmax_t num_days;
1656 enum comparison_type c_type;
1657 time_t t;
1659 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1660 return false;
1661 if (!get_num (argv[*arg_ptr], &num_days, &c_type))
1662 return false;
1663 t = num_days * DAYSECS;
1664 our_pred = insert_primary (entry);
1665 our_pred->args.info.kind = c_type;
1666 our_pred->args.info.negative = t < 0;
1667 our_pred->args.info.l_val = t;
1668 (*arg_ptr)++;
1669 return true;
1672 static boolean
1673 parse_user (const struct parser_table* entry, char **argv, int *arg_ptr)
1675 struct passwd *cur_pwd;
1676 struct predicate *our_pred;
1677 uid_t uid;
1678 int uid_len;
1680 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1681 return false;
1682 cur_pwd = getpwnam (argv[*arg_ptr]);
1683 endpwent ();
1684 if (cur_pwd != NULL)
1685 uid = cur_pwd->pw_uid;
1686 else
1688 uid_len = strspn (argv[*arg_ptr], "0123456789");
1689 if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
1690 return false;
1691 uid = atoi (argv[*arg_ptr]);
1693 our_pred = insert_primary (entry);
1694 our_pred->args.uid = uid;
1695 (*arg_ptr)++;
1696 return true;
1699 static boolean
1700 parse_version (const struct parser_table* entry, char **argv, int *arg_ptr)
1702 extern char *version_string;
1703 int features = 0;
1705 (void) argv;
1706 (void) arg_ptr;
1707 (void) entry;
1709 fflush (stderr);
1710 printf (_("GNU find version %s\n"), version_string);
1711 printf (_("Features enabled: "));
1713 #if CACHE_IDS
1714 printf("CACHE_IDS ");
1715 ++features;
1716 #endif
1717 #if DEBUG
1718 printf("DEBUG ");
1719 ++features;
1720 #endif
1721 #if DEBUG_STAT
1722 printf("DEBUG_STAT ");
1723 ++features;
1724 #endif
1725 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1726 printf("D_TYPE ");
1727 ++features;
1728 #endif
1729 #if defined(O_NOFOLLOW)
1730 printf("O_NOFOLLOW(%s) ",
1731 (options.open_nofollow_available ? "enabled" : "disabled"));
1732 ++features;
1733 #endif
1734 #if defined(LEAF_OPTIMISATION)
1735 printf("LEAF_OPTIMISATION ");
1736 ++features;
1737 #endif
1739 if (is_fts_enabled())
1741 printf("FTS ");
1742 ++features;
1745 if (0 == features)
1747 /* For the moment, leave this as English in case someone wants
1748 to parse these strings. */
1749 printf("none");
1751 printf("\n");
1753 exit (0);
1756 static boolean
1757 parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr)
1759 (void) argv;
1760 (void) arg_ptr;
1761 (void) entry;
1762 options.stay_on_filesystem = true;
1763 return true;
1766 static boolean
1767 parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
1769 (void) argv;
1770 (void) arg_ptr;
1771 (void) entry;
1772 options.ignore_readdir_race = true;
1773 return true;
1776 static boolean
1777 parse_noignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
1779 (void) argv;
1780 (void) arg_ptr;
1781 (void) entry;
1782 options.ignore_readdir_race = false;
1783 return true;
1786 static boolean
1787 parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr)
1789 (void) argv;
1790 (void) arg_ptr;
1791 (void) entry;
1792 options.warnings = true;
1793 return true;
1796 static boolean
1797 parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr)
1799 (void) argv;
1800 (void) arg_ptr;
1801 return insert_type (argv, arg_ptr, entry, pred_xtype);
1804 static boolean
1805 insert_type (char **argv, int *arg_ptr, const struct parser_table *entry, PRED_FUNC which_pred)
1807 mode_t type_cell;
1808 struct predicate *our_pred;
1810 if ((argv == NULL) || (argv[*arg_ptr] == NULL)
1811 || (strlen (argv[*arg_ptr]) != 1))
1812 return false;
1813 switch (argv[*arg_ptr][0])
1815 case 'b': /* block special */
1816 type_cell = S_IFBLK;
1817 break;
1818 case 'c': /* character special */
1819 type_cell = S_IFCHR;
1820 break;
1821 case 'd': /* directory */
1822 type_cell = S_IFDIR;
1823 break;
1824 case 'f': /* regular file */
1825 type_cell = S_IFREG;
1826 break;
1827 #ifdef S_IFLNK
1828 case 'l': /* symbolic link */
1829 type_cell = S_IFLNK;
1830 break;
1831 #endif
1832 #ifdef S_IFIFO
1833 case 'p': /* pipe */
1834 type_cell = S_IFIFO;
1835 break;
1836 #endif
1837 #ifdef S_IFSOCK
1838 case 's': /* socket */
1839 type_cell = S_IFSOCK;
1840 break;
1841 #endif
1842 #ifdef S_IFDOOR
1843 case 'D': /* Solaris door */
1844 type_cell = S_IFDOOR;
1845 break;
1846 #endif
1847 default: /* None of the above ... nuke 'em. */
1848 return false;
1850 our_pred = insert_primary_withpred (entry, which_pred);
1852 /* Figure out if we will need to stat the file, because if we don't
1853 * need to follow symlinks, we can avoid a stat call by using
1854 * struct dirent.d_type.
1856 if (which_pred == pred_xtype)
1858 our_pred->need_stat = true;
1859 our_pred->need_type = false;
1861 else
1863 our_pred->need_stat = false; /* struct dirent is enough */
1864 our_pred->need_type = true;
1866 our_pred->args.type = type_cell;
1867 (*arg_ptr)++; /* Move on to next argument. */
1868 return true;
1872 /* Return true if the file accessed via FP is a terminal.
1874 static boolean
1875 stream_is_tty(FILE *fp)
1877 int fd = fileno(fp);
1878 if (-1 == fd)
1880 return false; /* not a valid stream */
1882 else
1884 return isatty(fd) ? true : false;
1891 /* If true, we've determined that the current fprintf predicate
1892 uses stat information. */
1893 static boolean fprintf_stat_needed;
1895 /* XXX: do we need to pass FUNC to this function? */
1896 static boolean
1897 insert_fprintf (FILE *fp, const struct parser_table *entry, PRED_FUNC func, char **argv, int *arg_ptr)
1899 char *format; /* Beginning of unprocessed format string. */
1900 register char *scan; /* Current address in scanning `format'. */
1901 register char *scan2; /* Address inside of element being scanned. */
1902 struct segment **segmentp; /* Address of current segment. */
1903 struct predicate *our_pred;
1905 format = argv[(*arg_ptr)++];
1907 fprintf_stat_needed = false; /* Might be overridden later. */
1908 our_pred = insert_primary_withpred (entry, func);
1909 our_pred->side_effects = our_pred->no_default_print = true;
1910 our_pred->args.printf_vec.stream = fp;
1911 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(fp);
1912 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
1913 segmentp = &our_pred->args.printf_vec.segment;
1914 *segmentp = NULL;
1916 for (scan = format; *scan; scan++)
1918 if (*scan == '\\')
1920 scan2 = scan + 1;
1921 if (*scan2 >= '0' && *scan2 <= '7')
1923 register int n, i;
1925 for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
1926 i++, scan2++)
1927 n = 8 * n + *scan2 - '0';
1928 scan2--;
1929 *scan = n;
1931 else
1933 switch (*scan2)
1935 case 'a':
1936 *scan = 7;
1937 break;
1938 case 'b':
1939 *scan = '\b';
1940 break;
1941 case 'c':
1942 make_segment (segmentp, format, scan - format, KIND_STOP);
1943 our_pred->need_stat = fprintf_stat_needed;
1944 return true;
1945 case 'f':
1946 *scan = '\f';
1947 break;
1948 case 'n':
1949 *scan = '\n';
1950 break;
1951 case 'r':
1952 *scan = '\r';
1953 break;
1954 case 't':
1955 *scan = '\t';
1956 break;
1957 case 'v':
1958 *scan = '\v';
1959 break;
1960 case '\\':
1961 /* *scan = '\\'; * it already is */
1962 break;
1963 default:
1964 error (0, 0,
1965 _("warning: unrecognized escape `\\%c'"), *scan2);
1966 scan++;
1967 continue;
1970 segmentp = make_segment (segmentp, format, scan - format + 1,
1971 KIND_PLAIN);
1972 format = scan2 + 1; /* Move past the escape. */
1973 scan = scan2; /* Incremented immediately by `for'. */
1975 else if (*scan == '%')
1977 if (scan[1] == '%')
1979 segmentp = make_segment (segmentp, format, scan - format + 1,
1980 KIND_PLAIN);
1981 scan++;
1982 format = scan + 1;
1983 continue;
1985 /* Scan past flags, width and precision, to verify kind. */
1986 for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
1987 /* Do nothing. */ ;
1988 while (ISDIGIT (*scan2))
1989 scan2++;
1990 if (*scan2 == '.')
1991 for (scan2++; ISDIGIT (*scan2); scan2++)
1992 /* Do nothing. */ ;
1993 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2))
1995 segmentp = make_segment (segmentp, format, scan2 - format,
1996 (int) *scan2);
1997 scan = scan2;
1998 format = scan + 1;
2000 else if (strchr ("ACT", *scan2) && scan2[1])
2002 segmentp = make_segment (segmentp, format, scan2 - format,
2003 *scan2 | (scan2[1] << 8));
2004 scan = scan2 + 1;
2005 format = scan + 1;
2006 continue;
2008 else
2010 /* An unrecognized % escape. Print the char after the %. */
2011 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
2012 *scan2);
2013 segmentp = make_segment (segmentp, format, scan - format,
2014 KIND_PLAIN);
2015 format = scan + 1;
2016 continue;
2021 if (scan > format)
2022 make_segment (segmentp, format, scan - format, KIND_PLAIN);
2023 our_pred->need_type = false;
2024 our_pred->need_stat = fprintf_stat_needed;
2025 return true;
2028 /* Create a new fprintf segment in *SEGMENT, with type KIND,
2029 from the text in FORMAT, which has length LEN.
2030 Return the address of the `next' pointer of the new segment. */
2032 static struct segment **
2033 make_segment (struct segment **segment, char *format, int len, int kind)
2035 char *fmt;
2037 *segment = (struct segment *) xmalloc (sizeof (struct segment));
2039 (*segment)->kind = kind;
2040 (*segment)->next = NULL;
2041 (*segment)->text_len = len;
2043 fmt = (*segment)->text = xmalloc (len + sizeof "d");
2044 strncpy (fmt, format, len);
2045 fmt += len;
2047 switch (kind & 0xff)
2049 case KIND_PLAIN: /* Plain text string, no % conversion. */
2050 case KIND_STOP: /* Terminate argument, no newline. */
2051 break;
2053 case 'a': /* atime in `ctime' format */
2054 case 'A': /* atime in user-specified strftime format */
2055 case 'c': /* ctime in `ctime' format */
2056 case 'C': /* ctime in user-specified strftime format */
2057 case 'F': /* filesystem type */
2058 case 'g': /* group name */
2059 case 'i': /* inode number */
2060 case 'l': /* object of symlink */
2061 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
2062 case 's': /* size in bytes */
2063 case 't': /* mtime in `ctime' format */
2064 case 'T': /* mtime in user-specified strftime format */
2065 case 'u': /* user name */
2066 case 'y': /* file type */
2067 case 'Y': /* symlink pointed file type */
2068 fprintf_stat_needed = true;
2069 /* FALLTHROUGH */
2070 case 'f': /* basename of path */
2071 case 'h': /* leading directories part of path */
2072 case 'H': /* ARGV element file was found under */
2073 case 'p': /* pathname */
2074 case 'P': /* pathname with ARGV element stripped */
2075 *fmt++ = 's';
2076 break;
2078 /* Numeric items that one might expect to honour
2079 * #, 0, + flags but which do not.
2081 case 'G': /* GID number */
2082 case 'U': /* UID number */
2083 case 'b': /* size in 512-byte blocks */
2084 case 'D': /* Filesystem device on which the file exits */
2085 case 'k': /* size in 1K blocks */
2086 case 'n': /* number of links */
2087 fprintf_stat_needed = true;
2088 *fmt++ = 's';
2089 break;
2091 /* Numeric items that DO honour #, 0, + flags.
2093 case 'd': /* depth in search tree (0 = ARGV element) */
2094 *fmt++ = 'd';
2095 break;
2097 case 'm': /* mode as octal number (perms only) */
2098 *fmt++ = 'o';
2099 fprintf_stat_needed = true;
2100 break;
2102 *fmt = '\0';
2104 return &(*segment)->next;
2107 static void
2108 check_path_safety(const char *action)
2110 const char *path = getenv("PATH");
2111 char *s;
2112 s = next_element(path, 1);
2113 while ((s = next_element ((char *) NULL, 1)) != NULL)
2115 if (0 == strcmp(s, "."))
2117 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)"),
2118 action);
2124 /* handles both exec and ok predicate */
2125 #if defined(NEW_EXEC)
2126 /* handles both exec and ok predicate */
2127 static boolean
2128 new_insert_exec_ok (const char *action,
2129 const struct parser_table *entry,
2130 char **argv,
2131 int *arg_ptr)
2133 int start, end; /* Indexes in ARGV of start & end of cmd. */
2134 int i; /* Index into cmd args */
2135 int saw_braces; /* True if previous arg was '{}'. */
2136 boolean allow_plus; /* True if + is a valid terminator */
2137 int brace_count; /* Number of instances of {}. */
2138 PRED_FUNC func = entry->pred_func;
2140 struct predicate *our_pred;
2141 struct exec_val *execp; /* Pointer for efficiency. */
2143 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2144 return false;
2146 our_pred = insert_primary_withpred (entry, func);
2147 our_pred->side_effects = our_pred->no_default_print = true;
2148 execp = &our_pred->args.exec_vec;
2150 if ((func != pred_okdir) && (func != pred_ok))
2152 allow_plus = true;
2153 execp->close_stdin = false;
2155 else
2157 allow_plus = false;
2158 /* If find reads stdin (i.e. for -ok and similar), close stdin
2159 * in the child to prevent some script from consiming the output
2160 * intended for find.
2162 execp->close_stdin = true;
2166 if ((func == pred_execdir) || (func == pred_okdir))
2168 options.ignore_readdir_race = false;
2169 check_path_safety(action);
2170 execp->use_current_dir = true;
2172 else
2174 execp->use_current_dir = false;
2177 our_pred->args.exec_vec.multiple = 0;
2179 /* Count the number of args with path replacements, up until the ';'.
2180 * Also figure out if the command is terminated by ";" or by "+".
2182 start = *arg_ptr;
2183 for (end = start, saw_braces=0, brace_count=0;
2184 (argv[end] != NULL)
2185 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2186 end++)
2188 /* For -exec and -execdir, "{} +" can terminate the command. */
2189 if ( allow_plus
2190 && argv[end][0] == '+' && argv[end][1] == 0
2191 && saw_braces)
2193 our_pred->args.exec_vec.multiple = 1;
2194 break;
2197 saw_braces = 0;
2198 if (strstr (argv[end], "{}"))
2200 saw_braces = 1;
2201 ++brace_count;
2203 if (0 == end && (func == pred_execdir || func == pred_okdir))
2205 /* The POSIX standard says that {} replacement should
2206 * occur even in the utility name. This is insecure
2207 * since it means we will be executing a command whose
2208 * name is chosen according to whatever find finds in
2209 * the filesystem. That can be influenced by an
2210 * attacker. Hence for -execdir and -okdir this is not
2211 * allowed. We can specify this as those options are
2212 * not defined by POSIX.
2214 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2219 /* Fail if no command given or no semicolon found. */
2220 if ((end == start) || (argv[end] == NULL))
2222 *arg_ptr = end;
2223 free(our_pred);
2224 return false;
2227 if (our_pred->args.exec_vec.multiple && brace_count > 1)
2230 const char *suffix;
2231 if (func == pred_execdir)
2232 suffix = "dir";
2233 else
2234 suffix = "";
2236 error(1, 0,
2237 _("Only one instance of {} is supported with -exec%s ... +"),
2238 suffix);
2241 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2242 bc_init_controlinfo(&execp->ctl);
2243 execp->ctl.exec_callback = launch;
2245 if (our_pred->args.exec_vec.multiple)
2247 /* "+" terminator, so we can just append our arguments after the
2248 * command and initial arguments.
2250 execp->replace_vec = NULL;
2251 execp->ctl.replace_pat = NULL;
2252 execp->ctl.rplen = 0;
2253 execp->ctl.lines_per_exec = 0; /* no limit */
2254 execp->ctl.args_per_exec = 0; /* no limit */
2256 /* remember how many arguments there are */
2257 execp->ctl.initial_argc = (end-start) - 1;
2259 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2260 bc_init_state(&execp->ctl, &execp->state, execp);
2262 /* Gather the initial arguments. Skip the {}. */
2263 for (i=start; i<end-1; ++i)
2265 bc_push_arg(&execp->ctl, &execp->state,
2266 argv[i], strlen(argv[i])+1,
2267 NULL, 0,
2271 else
2273 /* Semicolon terminator - more than one {} is supported, so we
2274 * have to do brace-replacement.
2276 execp->num_args = end - start;
2278 execp->ctl.replace_pat = "{}";
2279 execp->ctl.rplen = strlen(execp->ctl.replace_pat);
2280 execp->ctl.lines_per_exec = 0; /* no limit */
2281 execp->ctl.args_per_exec = 0; /* no limit */
2282 execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
2285 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2286 bc_init_state(&execp->ctl, &execp->state, execp);
2288 /* Remember the (pre-replacement) arguments for later. */
2289 for (i=0; i<execp->num_args; ++i)
2291 execp->replace_vec[i] = argv[i+start];
2295 if (argv[end] == NULL)
2296 *arg_ptr = end;
2297 else
2298 *arg_ptr = end + 1;
2300 return true;
2302 #else
2303 /* handles both exec and ok predicate */
2304 static boolean
2305 old_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
2307 int start, end; /* Indexes in ARGV of start & end of cmd. */
2308 int num_paths; /* Number of args with path replacements. */
2309 int path_pos; /* Index in array of path replacements. */
2310 int vec_pos; /* Index in array of args. */
2311 struct predicate *our_pred;
2312 struct exec_val *execp; /* Pointer for efficiency. */
2314 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2315 return false;
2317 /* Count the number of args with path replacements, up until the ';'. */
2318 start = *arg_ptr;
2319 for (end = start, num_paths = 0;
2320 (argv[end] != NULL)
2321 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2322 end++)
2323 if (strstr (argv[end], "{}"))
2324 num_paths++;
2325 /* Fail if no command given or no semicolon found. */
2326 if ((end == start) || (argv[end] == NULL))
2328 *arg_ptr = end;
2329 return false;
2332 our_pred = insert_primary (func);
2333 our_pred->side_effects = our_pred->no_default_print = true;
2334 execp = &our_pred->args.exec_vec;
2335 execp->usercontext = our_pred;
2336 execp->use_current_dir = false;
2337 execp->paths =
2338 (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1));
2339 execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1));
2340 /* Record the positions of all args, and the args with path replacements. */
2341 for (end = start, path_pos = vec_pos = 0;
2342 (argv[end] != NULL)
2343 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2344 end++)
2346 register char *p;
2348 execp->paths[path_pos].count = 0;
2349 for (p = argv[end]; *p; ++p)
2350 if (p[0] == '{' && p[1] == '}')
2352 execp->paths[path_pos].count++;
2353 ++p;
2355 if (execp->paths[path_pos].count)
2357 execp->paths[path_pos].offset = vec_pos;
2358 execp->paths[path_pos].origarg = argv[end];
2359 path_pos++;
2361 execp->vec[vec_pos++] = argv[end];
2363 execp->paths[path_pos].offset = -1;
2364 execp->vec[vec_pos] = NULL;
2366 if (argv[end] == NULL)
2367 *arg_ptr = end;
2368 else
2369 *arg_ptr = end + 1;
2370 return true;
2372 #endif
2376 static boolean
2377 insert_exec_ok (const char *action, const struct parser_table *entry, char **argv, int *arg_ptr)
2379 #if defined(NEW_EXEC)
2380 return new_insert_exec_ok(action, entry, argv, arg_ptr);
2381 #else
2382 return old_insert_exec_ok(func, argv, arg_ptr);
2383 #endif
2388 /* Get a number of days and comparison type.
2389 STR is the ASCII representation.
2390 Set *NUM_DAYS to the number of days, taken as being from
2391 the current moment (or possibly midnight). Thus the sense of the
2392 comparison type appears to be reversed.
2393 Set *COMP_TYPE to the kind of comparison that is requested.
2395 Return true if all okay, false if input error.
2397 Used by -atime, -ctime and -mtime (parsers) to
2398 get the appropriate information for a time predicate processor. */
2400 static boolean
2401 get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
2403 boolean r = get_num (str, num_days, comp_type);
2404 if (r)
2405 switch (*comp_type)
2407 case COMP_LT: *comp_type = COMP_GT; break;
2408 case COMP_GT: *comp_type = COMP_LT; break;
2409 default: break;
2411 return r;
2414 /* Insert a time predicate PRED.
2415 ARGV is a pointer to the argument array.
2416 ARG_PTR is a pointer to an index into the array, incremented if
2417 all went well.
2419 Return true if input is valid, false if not.
2421 A new predicate node is assigned, along with an argument node
2422 obtained with malloc.
2424 Used by -atime, -ctime, and -mtime parsers. */
2426 static boolean
2427 insert_time (char **argv, int *arg_ptr, const struct parser_table* entry, PRED_FUNC pred)
2429 struct predicate *our_pred;
2430 uintmax_t num_days;
2431 enum comparison_type c_type;
2432 time_t t;
2434 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2435 return false;
2436 if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
2437 return false;
2439 /* Figure out the timestamp value we are looking for. */
2440 t = ( options.cur_day_start - num_days * DAYSECS
2441 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2443 if (1)
2445 /* We introduce a scope in which 'val' can be declared, for the
2446 * benefit of compilers that are really C89 compilers
2447 * which support intmax_t because config.h #defines it
2449 intmax_t val = ( (intmax_t)options.cur_day_start - num_days * DAYSECS
2450 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2451 t = val;
2453 /* Check for possibility of an overflow */
2454 if ( (intmax_t)t != val )
2456 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv[*arg_ptr]);
2460 our_pred = insert_primary_withpred (entry, pred);
2461 our_pred->args.info.kind = c_type;
2462 our_pred->args.info.negative = t < 0;
2463 our_pred->args.info.l_val = t;
2464 (*arg_ptr)++;
2465 #ifdef DEBUG
2466 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2467 fprintf (stderr, " type: %s %s ",
2468 (c_type == COMP_GT) ? "gt" :
2469 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2470 (c_type == COMP_GT) ? " >" :
2471 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
2472 t = our_pred->args.info.l_val;
2473 fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2474 if (c_type == COMP_EQ)
2476 t = our_pred->args.info.l_val += DAYSECS;
2477 fprintf (stderr, " < %ju %s",
2478 (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2479 our_pred->args.info.l_val -= DAYSECS;
2481 #endif /* DEBUG */
2482 return true;
2485 /* Get a number with comparison information.
2486 The sense of the comparison information is 'normal'; that is,
2487 '+' looks for a count > than the number and '-' less than.
2489 STR is the ASCII representation of the number.
2490 Set *NUM to the number.
2491 Set *COMP_TYPE to the kind of comparison that is requested.
2493 Return true if all okay, false if input error. */
2495 static boolean
2496 get_num (char *str, uintmax_t *num, enum comparison_type *comp_type)
2498 if (str == NULL)
2499 return false;
2500 switch (str[0])
2502 case '+':
2503 *comp_type = COMP_GT;
2504 str++;
2505 break;
2506 case '-':
2507 *comp_type = COMP_LT;
2508 str++;
2509 break;
2510 default:
2511 *comp_type = COMP_EQ;
2512 break;
2515 return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK;
2518 /* Insert a number predicate.
2519 ARGV is a pointer to the argument array.
2520 *ARG_PTR is an index into ARGV, incremented if all went well.
2521 *PRED is the predicate processor to insert.
2523 Return true if input is valid, false if error.
2525 A new predicate node is assigned, along with an argument node
2526 obtained with malloc.
2528 Used by -inum and -links parsers. */
2530 static boolean
2531 insert_num (char **argv, int *arg_ptr, const struct parser_table *entry)
2533 struct predicate *our_pred;
2534 uintmax_t num;
2535 enum comparison_type c_type;
2537 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2538 return false;
2539 if (!get_num (argv[*arg_ptr], &num, &c_type))
2540 return false;
2541 our_pred = insert_primary (entry);
2542 our_pred->args.info.kind = c_type;
2543 our_pred->args.info.l_val = num;
2544 (*arg_ptr)++;
2545 #ifdef DEBUG
2546 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2547 fprintf (stderr, " type: %s %s ",
2548 (c_type == COMP_GT) ? "gt" :
2549 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2550 (c_type == COMP_GT) ? " >" :
2551 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
2552 fprintf (stderr, "%ju\n", our_pred->args.info.l_val);
2553 #endif /* DEBUG */
2554 return true;
2557 static FILE *
2558 open_output_file (char *path)
2560 FILE *f;
2562 if (!strcmp (path, "/dev/stderr"))
2563 return stderr;
2564 else if (!strcmp (path, "/dev/stdout"))
2565 return stdout;
2566 f = fopen_safer (path, "w");
2567 if (f == NULL)
2568 error (1, errno, "%s", path);
2569 return f;