INficate in the output of find --version if the leaf optimisation is enabled or not
[findutils.git] / find / parser.c
blobfb37205747974fba610e66de5850a7dce5d8932a
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;
309 void
310 parse_begin_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates)
312 (void) args;
313 (void) argno;
314 (void) last;
315 (void) predicates;
316 first_nonoption_arg = NULL;
319 void
320 parse_end_user_args (char **args, int argno, const struct predicate *last, const struct predicate *predicates)
322 /* does nothing */
323 (void) args;
324 (void) argno;
325 (void) last;
326 (void) predicates;
332 /* Return a pointer to the parser function to invoke for predicate
333 SEARCH_NAME.
334 Return NULL if SEARCH_NAME is not a valid predicate name. */
336 const struct parser_table*
337 find_parser (char *search_name)
339 int i;
340 const char *original_arg = search_name;
342 if (*search_name == '-')
343 search_name++;
344 for (i = 0; parse_table[i].parser_name != 0; i++)
346 if (strcmp (parse_table[i].parser_name, search_name) == 0)
348 /* If this is an option, but we have already had a
349 * non-option argument, the user may be under the
350 * impression that the behaviour of the option
351 * argument is conditional on some preceding
352 * tests. This might typically be the case with,
353 * for example, -maxdepth.
355 * The options -daystart and -follow are exempt
356 * from this treatment, since their positioning
357 * in the command line does have an effect on
358 * subsequent tests but not previous ones. That
359 * might be intentional on the part of the user.
361 if (parse_table[i].type != ARG_POSITIONAL_OPTION)
363 /* Something other than -follow/-daystart.
364 * If this is an option, check if it followed
365 * a non-option and if so, issue a warning.
367 if (parse_table[i].type == ARG_OPTION)
369 if ((first_nonoption_arg != NULL)
370 && options.warnings )
372 /* option which follows a non-option */
373 error (0, 0,
374 _("warning: you have specified the %s "
375 "option after a non-option argument %s, "
376 "but options are not positional (%s affects "
377 "tests specified before it as well as those "
378 "specified after it). Please specify options "
379 "before other arguments.\n"),
380 original_arg,
381 first_nonoption_arg,
382 original_arg);
385 else
387 /* Not an option or a positional option,
388 * so remember we've seen it in order to
389 * use it in a possible future warning message.
391 if (first_nonoption_arg == NULL)
393 first_nonoption_arg = original_arg;
398 return &parse_table[i];
401 return NULL;
404 /* The parsers are responsible to continue scanning ARGV for
405 their arguments. Each parser knows what is and isn't
406 allowed for itself.
408 ARGV is the argument array.
409 *ARG_PTR is the index to start at in ARGV,
410 updated to point beyond the last element consumed.
412 The predicate structure is updated with the new information. */
414 static boolean
415 parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr)
417 struct predicate *our_pred;
418 uintmax_t num;
419 enum comparison_type c_type;
420 time_t t;
422 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
423 return false;
424 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
425 return false;
426 t = options.cur_day_start + DAYSECS - num * 60;
427 our_pred = insert_primary (entry);
428 our_pred->args.info.kind = c_type;
429 our_pred->args.info.negative = t < 0;
430 our_pred->args.info.l_val = t;
431 (*arg_ptr)++;
432 return true;
435 static boolean
436 parse_and (const struct parser_table* entry, char **argv, int *arg_ptr)
438 struct predicate *our_pred;
440 (void) argv;
441 (void) arg_ptr;
443 our_pred = get_new_pred (entry);
444 our_pred->pred_func = pred_and;
445 #ifdef DEBUG
446 our_pred->p_name = find_pred_name (pred_and);
447 #endif /* DEBUG */
448 our_pred->p_type = BI_OP;
449 our_pred->p_prec = AND_PREC;
450 our_pred->need_stat = our_pred->need_type = false;
451 return true;
454 static boolean
455 parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr)
457 struct predicate *our_pred;
458 struct stat stat_newer;
460 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
461 return false;
462 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
463 error (1, errno, "%s", argv[*arg_ptr]);
464 our_pred = insert_primary (entry);
465 our_pred->args.time = stat_newer.st_mtime;
466 (*arg_ptr)++;
467 return true;
470 static boolean
471 parse_atime (const struct parser_table* entry, char **argv, int *arg_ptr)
473 return insert_time (argv, arg_ptr, entry, pred_atime);
476 boolean
477 parse_close (const struct parser_table* entry, char **argv, int *arg_ptr)
479 struct predicate *our_pred;
481 (void) argv;
482 (void) arg_ptr;
484 our_pred = get_new_pred (entry);
485 our_pred->pred_func = pred_close;
486 #ifdef DEBUG
487 our_pred->p_name = find_pred_name (pred_close);
488 #endif /* DEBUG */
489 our_pred->p_type = CLOSE_PAREN;
490 our_pred->p_prec = NO_PREC;
491 our_pred->need_stat = our_pred->need_type = false;
492 return true;
495 static boolean
496 parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr)
498 struct predicate *our_pred;
499 uintmax_t num;
500 enum comparison_type c_type;
501 time_t t;
503 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
504 return false;
505 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
506 return false;
507 t = options.cur_day_start + DAYSECS - num * 60;
508 our_pred = insert_primary (entry);
509 our_pred->args.info.kind = c_type;
510 our_pred->args.info.negative = t < 0;
511 our_pred->args.info.l_val = t;
512 (*arg_ptr)++;
513 return true;
516 static boolean
517 parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr)
519 struct predicate *our_pred;
520 struct stat stat_newer;
522 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
523 return false;
524 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
525 error (1, errno, "%s", argv[*arg_ptr]);
526 our_pred = insert_primary (entry);
527 our_pred->args.time = stat_newer.st_mtime;
528 (*arg_ptr)++;
529 return true;
532 static boolean
533 parse_comma (const struct parser_table* entry, char **argv, int *arg_ptr)
535 struct predicate *our_pred;
537 (void) argv;
538 (void) arg_ptr;
540 our_pred = get_new_pred (entry);
541 our_pred->pred_func = pred_comma;
542 #ifdef DEBUG
543 our_pred->p_name = find_pred_name (pred_comma);
544 #endif /* DEBUG */
545 our_pred->p_type = BI_OP;
546 our_pred->p_prec = COMMA_PREC;
547 our_pred->need_stat = our_pred->need_type = false;
548 return true;
551 static boolean
552 parse_ctime (const struct parser_table* entry, char **argv, int *arg_ptr)
554 return insert_time (argv, arg_ptr, entry, pred_ctime);
557 static boolean
558 parse_daystart (const struct parser_table* entry, char **argv, int *arg_ptr)
560 struct tm *local;
562 (void) entry;
563 (void) argv;
564 (void) arg_ptr;
566 if (options.full_days == false)
568 options.cur_day_start += DAYSECS;
569 local = localtime (&options.cur_day_start);
570 options.cur_day_start -= (local
571 ? (local->tm_sec + local->tm_min * 60
572 + local->tm_hour * 3600)
573 : options.cur_day_start % DAYSECS);
574 options.full_days = true;
576 return true;
579 static boolean
580 parse_delete (const struct parser_table* entry, char *argv[], int *arg_ptr)
582 struct predicate *our_pred;
583 (void) argv;
584 (void) arg_ptr;
586 our_pred = insert_primary (entry);
587 our_pred->side_effects = our_pred->no_default_print = true;
588 /* -delete implies -depth */
589 options.do_dir_first = false;
590 return true;
593 static boolean
594 parse_depth (const struct parser_table* entry, char **argv, int *arg_ptr)
596 (void) entry;
597 (void) argv;
598 (void) arg_ptr;
600 options.do_dir_first = false;
601 return true;
604 static boolean
605 parse_d (const struct parser_table* entry, char **argv, int *arg_ptr)
607 (void) argv;
608 (void) arg_ptr;
610 if (options.warnings)
612 error (0, 0,
613 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
615 return parse_depth(entry, argv, arg_ptr);
618 static boolean
619 parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr)
621 (void) argv;
622 (void) arg_ptr;
624 insert_primary (entry);
625 return true;
628 static boolean
629 parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr)
631 return insert_exec_ok ("-exec", entry, argv, arg_ptr);
634 static boolean
635 parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr)
637 return insert_exec_ok ("-execdir", entry, argv, arg_ptr);
640 static boolean
641 parse_false (const struct parser_table* entry, char **argv, int *arg_ptr)
643 struct predicate *our_pred;
645 (void) argv;
646 (void) arg_ptr;
648 our_pred = insert_primary (entry);
649 our_pred->need_stat = our_pred->need_type = false;
650 our_pred->side_effects = our_pred->no_default_print = false;
651 return true;
654 static boolean
655 parse_fls (const struct parser_table* entry, char **argv, int *arg_ptr)
657 struct predicate *our_pred;
659 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
660 return false;
661 our_pred = insert_primary (entry);
662 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
663 our_pred->side_effects = our_pred->no_default_print = true;
664 (*arg_ptr)++;
665 return true;
668 static boolean
669 parse_fprintf (const struct parser_table* entry, char **argv, int *arg_ptr)
671 FILE *fp;
673 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
674 return false;
675 if (argv[*arg_ptr + 1] == NULL)
677 /* Ensure we get "missing arg" message, not "invalid arg". */
678 (*arg_ptr)++;
679 return false;
681 fp = open_output_file (argv[*arg_ptr]);
682 (*arg_ptr)++;
683 return insert_fprintf (fp, entry, pred_fprintf, argv, arg_ptr);
686 static boolean
687 parse_follow (const struct parser_table* entry, char **argv, int *arg_ptr)
689 (void) entry;
690 (void) argv;
691 (void) arg_ptr;
693 set_follow_state(SYMLINK_ALWAYS_DEREF);
694 return true;
697 static boolean
698 parse_fprint (const struct parser_table* entry, char **argv, int *arg_ptr)
700 struct predicate *our_pred;
702 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
703 return false;
704 our_pred = insert_primary (entry);
705 our_pred->args.printf_vec.segment = NULL;
706 our_pred->args.printf_vec.stream = open_output_file (argv[*arg_ptr]);
707 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(our_pred->args.printf_vec.stream);
708 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
709 our_pred->side_effects = our_pred->no_default_print = true;
710 our_pred->need_stat = our_pred->need_type = false;
711 (*arg_ptr)++;
712 return true;
715 static boolean
716 parse_fprint0 (const struct parser_table* entry, char **argv, int *arg_ptr)
718 struct predicate *our_pred;
720 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
721 return false;
722 our_pred = insert_primary (entry);
723 our_pred->args.stream = open_output_file (argv[*arg_ptr]);
724 our_pred->side_effects = our_pred->no_default_print = true;
725 our_pred->need_stat = our_pred->need_type = false;
726 (*arg_ptr)++;
727 return true;
730 static boolean
731 parse_fstype (const struct parser_table* entry, char **argv, int *arg_ptr)
733 struct predicate *our_pred;
735 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
736 return false;
737 our_pred = insert_primary (entry);
738 our_pred->args.str = argv[*arg_ptr];
739 (*arg_ptr)++;
740 return true;
743 static boolean
744 parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr)
746 return insert_num (argv, arg_ptr, entry);
749 static boolean
750 parse_group (const struct parser_table* entry, char **argv, int *arg_ptr)
752 struct group *cur_gr;
753 struct predicate *our_pred;
754 gid_t gid;
755 int gid_len;
757 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
758 return false;
759 cur_gr = getgrnam (argv[*arg_ptr]);
760 endgrent ();
761 if (cur_gr != NULL)
762 gid = cur_gr->gr_gid;
763 else
765 gid_len = strspn (argv[*arg_ptr], "0123456789");
766 if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0'))
767 return false;
768 gid = atoi (argv[*arg_ptr]);
770 our_pred = insert_primary (entry);
771 our_pred->args.gid = gid;
772 (*arg_ptr)++;
773 return true;
776 static boolean
777 parse_help (const struct parser_table* entry, char **argv, int *arg_ptr)
779 (void) entry;
780 (void) argv;
781 (void) arg_ptr;
783 printf (_("\
784 Usage: %s [path...] [expression]\n"), program_name);
785 puts (_("\n\
786 default path is the current directory; default expression is -print\n\
787 expression may consist of: operators, options, tests, and actions:\n"));
788 puts (_("\
789 operators (decreasing precedence; -and is implicit where no others are given):\n\
790 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
791 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
792 puts (_("\
793 positional options (always true): -daystart -follow -regextype\n\n\
794 normal options (always true, specified before other expressions):\n\
795 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
796 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
797 puts (_("\
798 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
799 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
800 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
801 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
802 puts (_("\
803 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
804 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
805 -used N -user NAME -xtype [bcdpfls]\n"));
806 puts (_("\
807 actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
808 -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
809 -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
810 -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
811 "));
812 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
813 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
814 email to <bug-findutils@gnu.org>."));
815 exit (0);
818 static boolean
819 parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr)
821 struct predicate *our_pred;
823 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
824 return false;
825 our_pred = insert_primary (entry);
826 our_pred->args.str = argv[*arg_ptr];
827 (*arg_ptr)++;
828 return true;
832 /* sanity check the fnmatch() function to make sure
833 * it really is the GNU version.
835 static boolean
836 fnmatch_sanitycheck(void)
838 /* fprintf(stderr, "Performing find sanity check..."); */
839 if (0 != fnmatch("foo", "foo", 0)
840 || 0 == fnmatch("Foo", "foo", 0)
841 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD))
843 error (1, 0, _("sanity check of the fnmatch() library function failed."));
844 /* fprintf(stderr, "FAILED\n"); */
845 return false;
848 /* fprintf(stderr, "OK\n"); */
849 return true;
853 static boolean
854 check_name_arg(const char *pred, const char *arg)
856 if (strchr(arg, '/'))
858 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'."),
859 pred, arg, arg);
861 return true; /* allow it anyway */
866 static boolean
867 parse_iname (const struct parser_table* entry, char **argv, int *arg_ptr)
869 struct predicate *our_pred;
871 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
872 return false;
873 if (!check_name_arg("-iname", argv[*arg_ptr]))
874 return false;
876 fnmatch_sanitycheck();
878 our_pred = insert_primary (entry);
879 our_pred->need_stat = our_pred->need_type = false;
880 our_pred->args.str = argv[*arg_ptr];
881 (*arg_ptr)++;
882 return true;
885 static boolean
886 parse_inum (const struct parser_table* entry, char **argv, int *arg_ptr)
888 return insert_num (argv, arg_ptr, entry);
891 /* -ipath is deprecated (at RMS's request) in favour of
892 * -iwholename. See the node "GNU Manuals" in standards.texi
893 * for the rationale for this (basically, GNU prefers the use
894 * of the phrase "file name" to "path name"
896 static boolean
897 parse_ipath (const struct parser_table* entry, char **argv, int *arg_ptr)
899 error (0, 0,
900 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
902 return parse_iwholename(entry, argv, arg_ptr);
905 static boolean
906 parse_iwholename (const struct parser_table* entry, char **argv, int *arg_ptr)
908 struct predicate *our_pred;
910 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
911 return false;
913 fnmatch_sanitycheck();
915 our_pred = insert_primary_withpred (entry, pred_ipath);
916 our_pred->need_stat = our_pred->need_type = false;
917 our_pred->args.str = argv[*arg_ptr];
918 (*arg_ptr)++;
919 return true;
922 static boolean
923 parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr)
925 return insert_regex (argv, arg_ptr, entry, RE_ICASE|options.regex_options);
928 static boolean
929 parse_links (const struct parser_table* entry, char **argv, int *arg_ptr)
931 return insert_num (argv, arg_ptr, entry);
934 static boolean
935 parse_lname (const struct parser_table* entry, char **argv, int *arg_ptr)
937 struct predicate *our_pred;
939 (void) argv;
940 (void) arg_ptr;
942 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
943 return false;
945 fnmatch_sanitycheck();
947 our_pred = insert_primary (entry);
948 our_pred->args.str = argv[*arg_ptr];
949 (*arg_ptr)++;
950 return true;
953 static boolean
954 parse_ls (const struct parser_table* entry, char **argv, int *arg_ptr)
956 struct predicate *our_pred;
958 (void) &argv;
959 (void) &arg_ptr;
961 our_pred = insert_primary (entry);
962 our_pred->side_effects = our_pred->no_default_print = true;
963 return true;
966 static boolean
967 parse_maxdepth (const struct parser_table* entry, char **argv, int *arg_ptr)
969 int depth_len;
970 (void) entry;
972 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
973 return false;
974 depth_len = strspn (argv[*arg_ptr], "0123456789");
975 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
976 return false;
977 options.maxdepth = atoi (argv[*arg_ptr]);
978 if (options.maxdepth < 0)
979 return false;
980 (*arg_ptr)++;
981 return true;
984 static boolean
985 parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr)
987 int depth_len;
988 (void) entry;
990 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
991 return false;
992 depth_len = strspn (argv[*arg_ptr], "0123456789");
993 if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0'))
994 return false;
995 options.mindepth = atoi (argv[*arg_ptr]);
996 if (options.mindepth < 0)
997 return false;
998 (*arg_ptr)++;
999 return true;
1002 static boolean
1003 parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr)
1005 struct predicate *our_pred;
1006 uintmax_t num;
1007 enum comparison_type c_type;
1008 time_t t;
1010 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1011 return false;
1012 if (!get_num_days (argv[*arg_ptr], &num, &c_type))
1013 return false;
1014 t = options.cur_day_start + DAYSECS - num * 60;
1015 our_pred = insert_primary (entry);
1016 our_pred->args.info.kind = c_type;
1017 our_pred->args.info.negative = t < 0;
1018 our_pred->args.info.l_val = t;
1019 (*arg_ptr)++;
1020 return true;
1023 static boolean
1024 parse_mtime (const struct parser_table* entry, char **argv, int *arg_ptr)
1026 return insert_time (argv, arg_ptr, entry, pred_mtime);
1029 static boolean
1030 parse_name (const struct parser_table* entry, char **argv, int *arg_ptr)
1032 struct predicate *our_pred;
1034 (void) argv;
1035 (void) arg_ptr;
1037 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1038 return false;
1039 if (!check_name_arg("-name", argv[*arg_ptr]))
1040 return false;
1041 fnmatch_sanitycheck();
1043 our_pred = insert_primary (entry);
1044 our_pred->need_stat = our_pred->need_type = false;
1045 our_pred->args.str = argv[*arg_ptr];
1046 (*arg_ptr)++;
1047 return true;
1050 static boolean
1051 parse_negate (const struct parser_table* entry, char **argv, int *arg_ptr)
1053 struct predicate *our_pred;
1055 (void) &argv;
1056 (void) &arg_ptr;
1058 our_pred = get_new_pred_chk_op (entry);
1059 our_pred->pred_func = pred_negate;
1060 #ifdef DEBUG
1061 our_pred->p_name = find_pred_name (pred_negate);
1062 #endif /* DEBUG */
1063 our_pred->p_type = UNI_OP;
1064 our_pred->p_prec = NEGATE_PREC;
1065 our_pred->need_stat = our_pred->need_type = false;
1066 return true;
1069 static boolean
1070 parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr)
1072 struct predicate *our_pred;
1073 struct stat stat_newer;
1075 (void) argv;
1076 (void) arg_ptr;
1078 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1079 return false;
1080 if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
1081 error (1, errno, "%s", argv[*arg_ptr]);
1082 our_pred = insert_primary (entry);
1083 our_pred->args.time = stat_newer.st_mtime;
1084 (*arg_ptr)++;
1085 return true;
1088 static boolean
1089 parse_noleaf (const struct parser_table* entry, char **argv, int *arg_ptr)
1091 (void) &argv;
1092 (void) &arg_ptr;
1093 (void) entry;
1095 options.no_leaf_check = true;
1096 return true;
1099 #ifdef CACHE_IDS
1100 /* Arbitrary amount by which to increase size
1101 of `uid_unused' and `gid_unused'. */
1102 #define ALLOC_STEP 2048
1104 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1105 char *uid_unused = NULL;
1107 /* Number of elements in `uid_unused'. */
1108 unsigned uid_allocated;
1110 /* Similar for GIDs and group entries. */
1111 char *gid_unused = NULL;
1112 unsigned gid_allocated;
1113 #endif
1115 static boolean
1116 parse_nogroup (const struct parser_table* entry, char **argv, int *arg_ptr)
1118 struct predicate *our_pred;
1120 (void) &argv;
1121 (void) &arg_ptr;
1123 our_pred = insert_primary (entry);
1124 #ifdef CACHE_IDS
1125 if (gid_unused == NULL)
1127 struct group *gr;
1129 gid_allocated = ALLOC_STEP;
1130 gid_unused = xmalloc (gid_allocated);
1131 memset (gid_unused, 1, gid_allocated);
1132 setgrent ();
1133 while ((gr = getgrent ()) != NULL)
1135 if ((unsigned) gr->gr_gid >= gid_allocated)
1137 unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP;
1138 gid_unused = xrealloc (gid_unused, new_allocated);
1139 memset (gid_unused + gid_allocated, 1,
1140 new_allocated - gid_allocated);
1141 gid_allocated = new_allocated;
1143 gid_unused[(unsigned) gr->gr_gid] = 0;
1145 endgrent ();
1147 #endif
1148 return true;
1151 static boolean
1152 parse_nouser (const struct parser_table* entry, char **argv, int *arg_ptr)
1154 struct predicate *our_pred;
1155 (void) argv;
1156 (void) arg_ptr;
1159 our_pred = insert_primary (entry);
1160 #ifdef CACHE_IDS
1161 if (uid_unused == NULL)
1163 struct passwd *pw;
1165 uid_allocated = ALLOC_STEP;
1166 uid_unused = xmalloc (uid_allocated);
1167 memset (uid_unused, 1, uid_allocated);
1168 setpwent ();
1169 while ((pw = getpwent ()) != NULL)
1171 if ((unsigned) pw->pw_uid >= uid_allocated)
1173 unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP;
1174 uid_unused = xrealloc (uid_unused, new_allocated);
1175 memset (uid_unused + uid_allocated, 1,
1176 new_allocated - uid_allocated);
1177 uid_allocated = new_allocated;
1179 uid_unused[(unsigned) pw->pw_uid] = 0;
1181 endpwent ();
1183 #endif
1184 return true;
1187 static boolean
1188 parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr)
1190 (void) argv;
1191 (void) arg_ptr;
1192 (void) entry;
1194 options.warnings = false;
1195 return true;;
1198 static boolean
1199 parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr)
1201 return insert_exec_ok ("-ok", entry, argv, arg_ptr);
1204 static boolean
1205 parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr)
1207 return insert_exec_ok ("-okdir", entry, argv, arg_ptr);
1210 boolean
1211 parse_open (const struct parser_table* entry, char **argv, int *arg_ptr)
1213 struct predicate *our_pred;
1215 (void) argv;
1216 (void) arg_ptr;
1218 our_pred = get_new_pred_chk_op (entry);
1219 our_pred->pred_func = pred_open;
1220 #ifdef DEBUG
1221 our_pred->p_name = find_pred_name (pred_open);
1222 #endif /* DEBUG */
1223 our_pred->p_type = OPEN_PAREN;
1224 our_pred->p_prec = NO_PREC;
1225 our_pred->need_stat = our_pred->need_type = false;
1226 return true;
1229 static boolean
1230 parse_or (const struct parser_table* entry, char **argv, int *arg_ptr)
1232 struct predicate *our_pred;
1234 (void) argv;
1235 (void) arg_ptr;
1237 our_pred = get_new_pred (entry);
1238 our_pred->pred_func = pred_or;
1239 #ifdef DEBUG
1240 our_pred->p_name = find_pred_name (pred_or);
1241 #endif /* DEBUG */
1242 our_pred->p_type = BI_OP;
1243 our_pred->p_prec = OR_PREC;
1244 our_pred->need_stat = our_pred->need_type = false;
1245 return true;
1248 /* -path is deprecated (at RMS's request) in favour of
1249 * -iwholename. See the node "GNU Manuals" in standards.texi
1250 * for the rationale for this (basically, GNU prefers the use
1251 * of the phrase "file name" to "path name".
1253 * We do not issue a warning that this usage is deprecated
1254 * since HPUX find supports this predicate also.
1256 static boolean
1257 parse_path (const struct parser_table* entry, char **argv, int *arg_ptr)
1259 return parse_wholename(entry, argv, arg_ptr);
1262 static boolean
1263 parse_wholename (const struct parser_table* entry, char **argv, int *arg_ptr)
1265 struct predicate *our_pred;
1267 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1268 return false;
1269 our_pred = insert_primary_withpred (entry, pred_path);
1270 our_pred->need_stat = our_pred->need_type = false;
1271 our_pred->args.str = argv[*arg_ptr];
1272 (*arg_ptr)++;
1273 return true;
1276 static boolean
1277 parse_perm (const struct parser_table* entry, char **argv, int *arg_ptr)
1279 mode_t perm_val;
1280 int mode_start = 0;
1281 enum permissions_type kind = PERM_EXACT;
1282 struct mode_change *change = NULL;
1283 struct predicate *our_pred;
1285 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1286 return false;
1288 switch (argv[*arg_ptr][0])
1290 case '-':
1291 mode_start = 1;
1292 kind = PERM_AT_LEAST;
1293 break;
1295 case '+':
1296 change = mode_compile (argv[*arg_ptr]);
1297 if (NULL == change)
1299 /* Most likely the caller is an old script that is still
1300 * using the obsolete GNU syntax '-perm +MODE'. This old
1301 * syntax was withdrawn in favor of '-perm /MODE' because
1302 * it is incompatible with POSIX in some cases, but we
1303 * still support uses of it that are not incompatible with
1304 * POSIX.
1306 mode_start = 1;
1307 kind = PERM_ANY;
1309 else
1311 /* This is a POSIX-compatible usage */
1312 mode_start = 0;
1313 kind = PERM_EXACT;
1315 break;
1317 case '/': /* GNU extension */
1318 mode_start = 1;
1319 kind = PERM_ANY;
1320 break;
1322 default:
1323 /* For example, '-perm 0644', which is valid and matches
1324 * only files whose mode is exactly 0644.
1326 * We do nothing here, because mode_start and kind are already
1327 * correctly set.
1329 break;
1332 if (NULL == change)
1334 change = mode_compile (argv[*arg_ptr] + mode_start);
1335 if (NULL == change)
1336 error (1, 0, _("invalid mode `%s'"), argv[*arg_ptr]);
1338 perm_val = mode_adjust (0, change, 0);
1339 free (change);
1341 our_pred = insert_primary (entry);
1343 switch (argv[*arg_ptr][0])
1345 case '-':
1346 our_pred->args.perm.kind = PERM_AT_LEAST;
1347 break;
1348 case '+':
1349 our_pred->args.perm.kind = PERM_ANY;
1350 break;
1351 default:
1352 our_pred->args.perm.kind = PERM_EXACT;
1353 break;
1355 our_pred->args.perm.val = perm_val & MODE_ALL;
1356 (*arg_ptr)++;
1357 return true;
1360 boolean
1361 parse_print (const struct parser_table* entry, char **argv, int *arg_ptr)
1363 struct predicate *our_pred;
1365 (void) argv;
1366 (void) arg_ptr;
1368 our_pred = insert_primary (entry);
1369 /* -print has the side effect of printing. This prevents us
1370 from doing undesired multiple printing when the user has
1371 already specified -print. */
1372 our_pred->side_effects = our_pred->no_default_print = true;
1373 our_pred->need_stat = our_pred->need_type = false;
1374 our_pred->args.printf_vec.segment = NULL;
1375 our_pred->args.printf_vec.stream = stdout;
1376 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(stdout);
1377 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
1379 return true;
1382 static boolean
1383 parse_print0 (const struct parser_table* entry, char **argv, int *arg_ptr)
1385 struct predicate *our_pred;
1387 (void) argv;
1388 (void) arg_ptr;
1390 our_pred = insert_primary (entry);
1391 /* -print0 has the side effect of printing. This prevents us
1392 from doing undesired multiple printing when the user has
1393 already specified -print0. */
1394 our_pred->side_effects = our_pred->no_default_print = true;
1395 our_pred->need_stat = our_pred->need_type = false;
1396 return true;
1399 static boolean
1400 parse_printf (const struct parser_table* entry, char **argv, int *arg_ptr)
1402 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1403 return false;
1404 return insert_fprintf (stdout, entry, pred_fprintf, argv, arg_ptr);
1407 static boolean
1408 parse_prune (const struct parser_table* entry, char **argv, int *arg_ptr)
1410 struct predicate *our_pred;
1412 (void) argv;
1413 (void) arg_ptr;
1415 our_pred = insert_primary (entry);
1416 our_pred->need_stat = our_pred->need_type = false;
1417 /* -prune has a side effect that it does not descend into
1418 the current directory. */
1419 our_pred->side_effects = true;
1420 our_pred->no_default_print = false;
1421 return true;
1424 static boolean
1425 parse_quit (const struct parser_table* entry, char **argv, int *arg_ptr)
1427 struct predicate *our_pred = insert_primary (entry);
1428 (void) argv;
1429 (void) arg_ptr;
1430 our_pred->need_stat = our_pred->need_type = false;
1431 our_pred->side_effects = true; /* Exiting is a side effect... */
1432 our_pred->no_default_print = false; /* Don't inhibit the default print, though. */
1433 return true;
1437 static boolean
1438 parse_regextype (const struct parser_table* entry, char **argv, int *arg_ptr)
1440 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1441 return false;
1443 /* collect the regex type name */
1444 options.regex_options = get_regex_type(argv[*arg_ptr]);
1445 (*arg_ptr)++;
1447 return true;
1451 static boolean
1452 parse_regex (const struct parser_table* entry, char **argv, int *arg_ptr)
1454 return insert_regex (argv, arg_ptr, entry, options.regex_options);
1457 static boolean
1458 insert_regex (char **argv, int *arg_ptr, const struct parser_table *entry, int regex_options)
1460 struct predicate *our_pred;
1461 struct re_pattern_buffer *re;
1462 const char *error_message;
1464 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1465 return false;
1466 our_pred = insert_primary_withpred (entry, pred_regex);
1467 our_pred->need_stat = our_pred->need_type = false;
1468 re = (struct re_pattern_buffer *)
1469 xmalloc (sizeof (struct re_pattern_buffer));
1470 our_pred->args.regex = re;
1471 re->allocated = 100;
1472 re->buffer = (unsigned char *) xmalloc (re->allocated);
1473 re->fastmap = NULL;
1475 re_set_syntax(regex_options);
1476 re->syntax = regex_options;
1477 re->translate = NULL;
1479 error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]),
1480 re);
1481 if (error_message)
1482 error (1, 0, "%s", error_message);
1483 (*arg_ptr)++;
1484 return true;
1487 static boolean
1488 parse_size (const struct parser_table* entry, char **argv, int *arg_ptr)
1490 struct predicate *our_pred;
1491 uintmax_t num;
1492 enum comparison_type c_type;
1493 int blksize = 512;
1494 int len;
1496 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1497 return false;
1498 len = strlen (argv[*arg_ptr]);
1499 if (len == 0)
1500 error (1, 0, _("invalid null argument to -size"));
1501 switch (argv[*arg_ptr][len - 1])
1503 case 'b':
1504 blksize = 512;
1505 argv[*arg_ptr][len - 1] = '\0';
1506 break;
1508 case 'c':
1509 blksize = 1;
1510 argv[*arg_ptr][len - 1] = '\0';
1511 break;
1513 case 'k':
1514 blksize = 1024;
1515 argv[*arg_ptr][len - 1] = '\0';
1516 break;
1518 case 'M': /* Megabytes */
1519 blksize = 1024*1024;
1520 argv[*arg_ptr][len - 1] = '\0';
1521 break;
1523 case 'G': /* Gigabytes */
1524 blksize = 1024*1024*1024;
1525 argv[*arg_ptr][len - 1] = '\0';
1526 break;
1528 case 'w':
1529 blksize = 2;
1530 argv[*arg_ptr][len - 1] = '\0';
1531 break;
1533 case '0':
1534 case '1':
1535 case '2':
1536 case '3':
1537 case '4':
1538 case '5':
1539 case '6':
1540 case '7':
1541 case '8':
1542 case '9':
1543 break;
1545 default:
1546 error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]);
1548 if (!get_num (argv[*arg_ptr], &num, &c_type))
1549 return false;
1550 our_pred = insert_primary (entry);
1551 our_pred->args.size.kind = c_type;
1552 our_pred->args.size.blocksize = blksize;
1553 our_pred->args.size.size = num;
1554 (*arg_ptr)++;
1555 return true;
1559 static boolean
1560 parse_samefile (const struct parser_table* entry, char **argv, int *arg_ptr)
1562 struct predicate *our_pred;
1563 struct stat st;
1565 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1566 return false;
1567 if ((*options.xstat) (argv[*arg_ptr], &st))
1568 error (1, errno, "%s", argv[*arg_ptr]);
1570 our_pred = insert_primary (entry);
1571 our_pred->args.fileid.ino = st.st_ino;
1572 our_pred->args.fileid.dev = st.st_dev;
1573 our_pred->need_type = false;
1574 our_pred->need_stat = true;
1575 (*arg_ptr)++;
1576 return true;
1580 static boolean
1581 parse_true (const struct parser_table* entry, char **argv, int *arg_ptr)
1583 struct predicate *our_pred;
1585 (void) argv;
1586 (void) arg_ptr;
1588 our_pred = insert_primary (entry);
1589 our_pred->need_stat = our_pred->need_type = false;
1590 return true;
1593 static boolean
1594 parse_type (const struct parser_table* entry, char **argv, int *arg_ptr)
1596 return insert_type (argv, arg_ptr, entry, pred_type);
1599 static boolean
1600 parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr)
1602 return insert_num (argv, arg_ptr, entry);
1605 static boolean
1606 parse_used (const struct parser_table* entry, char **argv, int *arg_ptr)
1608 struct predicate *our_pred;
1609 uintmax_t num_days;
1610 enum comparison_type c_type;
1611 time_t t;
1613 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1614 return false;
1615 if (!get_num (argv[*arg_ptr], &num_days, &c_type))
1616 return false;
1617 t = num_days * DAYSECS;
1618 our_pred = insert_primary (entry);
1619 our_pred->args.info.kind = c_type;
1620 our_pred->args.info.negative = t < 0;
1621 our_pred->args.info.l_val = t;
1622 (*arg_ptr)++;
1623 return true;
1626 static boolean
1627 parse_user (const struct parser_table* entry, char **argv, int *arg_ptr)
1629 struct passwd *cur_pwd;
1630 struct predicate *our_pred;
1631 uid_t uid;
1632 int uid_len;
1634 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
1635 return false;
1636 cur_pwd = getpwnam (argv[*arg_ptr]);
1637 endpwent ();
1638 if (cur_pwd != NULL)
1639 uid = cur_pwd->pw_uid;
1640 else
1642 uid_len = strspn (argv[*arg_ptr], "0123456789");
1643 if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0'))
1644 return false;
1645 uid = atoi (argv[*arg_ptr]);
1647 our_pred = insert_primary (entry);
1648 our_pred->args.uid = uid;
1649 (*arg_ptr)++;
1650 return true;
1653 static boolean
1654 parse_version (const struct parser_table* entry, char **argv, int *arg_ptr)
1656 extern char *version_string;
1657 int features = 0;
1659 (void) argv;
1660 (void) arg_ptr;
1661 (void) entry;
1663 fflush (stderr);
1664 printf (_("GNU find version %s\n"), version_string);
1665 printf (_("Features enabled: "));
1667 #if CACHE_IDS
1668 printf("CACHE_IDS ");
1669 ++features;
1670 #endif
1671 #if DEBUG
1672 printf("DEBUG ");
1673 ++features;
1674 #endif
1675 #if DEBUG_STAT
1676 printf("DEBUG_STAT ");
1677 ++features;
1678 #endif
1679 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1680 printf("D_TYPE ");
1681 ++features;
1682 #endif
1683 #if defined(O_NOFOLLOW)
1684 printf("O_NOFOLLOW(%s) ",
1685 (options.open_nofollow_available ? "enabled" : "disabled"));
1686 ++features;
1687 #endif
1688 #if defined(LEAF_OPTIMISATION)
1689 printf("LEAF_OPTIMISATION ");
1690 ++features;
1691 #endif
1692 if (0 == features)
1694 /* For the moment, leave this as English in case someone wants
1695 to parse these strings. */
1696 printf("none");
1698 printf("\n");
1700 exit (0);
1703 static boolean
1704 parse_xdev (const struct parser_table* entry, char **argv, int *arg_ptr)
1706 (void) argv;
1707 (void) arg_ptr;
1708 (void) entry;
1709 options.stay_on_filesystem = true;
1710 return true;
1713 static boolean
1714 parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
1716 (void) argv;
1717 (void) arg_ptr;
1718 (void) entry;
1719 options.ignore_readdir_race = true;
1720 return true;
1723 static boolean
1724 parse_noignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
1726 (void) argv;
1727 (void) arg_ptr;
1728 (void) entry;
1729 options.ignore_readdir_race = false;
1730 return true;
1733 static boolean
1734 parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr)
1736 (void) argv;
1737 (void) arg_ptr;
1738 (void) entry;
1739 options.warnings = true;
1740 return true;
1743 static boolean
1744 parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr)
1746 (void) argv;
1747 (void) arg_ptr;
1748 return insert_type (argv, arg_ptr, entry, pred_xtype);
1751 static boolean
1752 insert_type (char **argv, int *arg_ptr, const struct parser_table *entry, PRED_FUNC which_pred)
1754 mode_t type_cell;
1755 struct predicate *our_pred;
1757 if ((argv == NULL) || (argv[*arg_ptr] == NULL)
1758 || (strlen (argv[*arg_ptr]) != 1))
1759 return false;
1760 switch (argv[*arg_ptr][0])
1762 case 'b': /* block special */
1763 type_cell = S_IFBLK;
1764 break;
1765 case 'c': /* character special */
1766 type_cell = S_IFCHR;
1767 break;
1768 case 'd': /* directory */
1769 type_cell = S_IFDIR;
1770 break;
1771 case 'f': /* regular file */
1772 type_cell = S_IFREG;
1773 break;
1774 #ifdef S_IFLNK
1775 case 'l': /* symbolic link */
1776 type_cell = S_IFLNK;
1777 break;
1778 #endif
1779 #ifdef S_IFIFO
1780 case 'p': /* pipe */
1781 type_cell = S_IFIFO;
1782 break;
1783 #endif
1784 #ifdef S_IFSOCK
1785 case 's': /* socket */
1786 type_cell = S_IFSOCK;
1787 break;
1788 #endif
1789 #ifdef S_IFDOOR
1790 case 'D': /* Solaris door */
1791 type_cell = S_IFDOOR;
1792 break;
1793 #endif
1794 default: /* None of the above ... nuke 'em. */
1795 return false;
1797 our_pred = insert_primary_withpred (entry, which_pred);
1799 /* Figure out if we will need to stat the file, because if we don't
1800 * need to follow symlinks, we can avoid a stat call by using
1801 * struct dirent.d_type.
1803 if (which_pred == pred_xtype)
1805 our_pred->need_stat = true;
1806 our_pred->need_type = false;
1808 else
1810 our_pred->need_stat = false; /* struct dirent is enough */
1811 our_pred->need_type = true;
1813 our_pred->args.type = type_cell;
1814 (*arg_ptr)++; /* Move on to next argument. */
1815 return true;
1819 /* Return true if the file accessed via FP is a terminal.
1821 static boolean
1822 stream_is_tty(FILE *fp)
1824 int fd = fileno(fp);
1825 if (-1 == fd)
1827 return false; /* not a valid stream */
1829 else
1831 return isatty(fd) ? true : false;
1838 /* If true, we've determined that the current fprintf predicate
1839 uses stat information. */
1840 static boolean fprintf_stat_needed;
1842 /* XXX: do we need to pass FUNC to this function? */
1843 static boolean
1844 insert_fprintf (FILE *fp, const struct parser_table *entry, PRED_FUNC func, char **argv, int *arg_ptr)
1846 char *format; /* Beginning of unprocessed format string. */
1847 register char *scan; /* Current address in scanning `format'. */
1848 register char *scan2; /* Address inside of element being scanned. */
1849 struct segment **segmentp; /* Address of current segment. */
1850 struct predicate *our_pred;
1852 format = argv[(*arg_ptr)++];
1854 fprintf_stat_needed = false; /* Might be overridden later. */
1855 our_pred = insert_primary_withpred (entry, func);
1856 our_pred->side_effects = our_pred->no_default_print = true;
1857 our_pred->args.printf_vec.stream = fp;
1858 our_pred->args.printf_vec.dest_is_tty = stream_is_tty(fp);
1859 our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
1860 segmentp = &our_pred->args.printf_vec.segment;
1861 *segmentp = NULL;
1863 for (scan = format; *scan; scan++)
1865 if (*scan == '\\')
1867 scan2 = scan + 1;
1868 if (*scan2 >= '0' && *scan2 <= '7')
1870 register int n, i;
1872 for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7');
1873 i++, scan2++)
1874 n = 8 * n + *scan2 - '0';
1875 scan2--;
1876 *scan = n;
1878 else
1880 switch (*scan2)
1882 case 'a':
1883 *scan = 7;
1884 break;
1885 case 'b':
1886 *scan = '\b';
1887 break;
1888 case 'c':
1889 make_segment (segmentp, format, scan - format, KIND_STOP);
1890 our_pred->need_stat = fprintf_stat_needed;
1891 return true;
1892 case 'f':
1893 *scan = '\f';
1894 break;
1895 case 'n':
1896 *scan = '\n';
1897 break;
1898 case 'r':
1899 *scan = '\r';
1900 break;
1901 case 't':
1902 *scan = '\t';
1903 break;
1904 case 'v':
1905 *scan = '\v';
1906 break;
1907 case '\\':
1908 /* *scan = '\\'; * it already is */
1909 break;
1910 default:
1911 error (0, 0,
1912 _("warning: unrecognized escape `\\%c'"), *scan2);
1913 scan++;
1914 continue;
1917 segmentp = make_segment (segmentp, format, scan - format + 1,
1918 KIND_PLAIN);
1919 format = scan2 + 1; /* Move past the escape. */
1920 scan = scan2; /* Incremented immediately by `for'. */
1922 else if (*scan == '%')
1924 if (scan[1] == '%')
1926 segmentp = make_segment (segmentp, format, scan - format + 1,
1927 KIND_PLAIN);
1928 scan++;
1929 format = scan + 1;
1930 continue;
1932 /* Scan past flags, width and precision, to verify kind. */
1933 for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);)
1934 /* Do nothing. */ ;
1935 while (ISDIGIT (*scan2))
1936 scan2++;
1937 if (*scan2 == '.')
1938 for (scan2++; ISDIGIT (*scan2); scan2++)
1939 /* Do nothing. */ ;
1940 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2))
1942 segmentp = make_segment (segmentp, format, scan2 - format,
1943 (int) *scan2);
1944 scan = scan2;
1945 format = scan + 1;
1947 else if (strchr ("ACT", *scan2) && scan2[1])
1949 segmentp = make_segment (segmentp, format, scan2 - format,
1950 *scan2 | (scan2[1] << 8));
1951 scan = scan2 + 1;
1952 format = scan + 1;
1953 continue;
1955 else
1957 /* An unrecognized % escape. Print the char after the %. */
1958 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1959 *scan2);
1960 segmentp = make_segment (segmentp, format, scan - format,
1961 KIND_PLAIN);
1962 format = scan + 1;
1963 continue;
1968 if (scan > format)
1969 make_segment (segmentp, format, scan - format, KIND_PLAIN);
1970 our_pred->need_type = false;
1971 our_pred->need_stat = fprintf_stat_needed;
1972 return true;
1975 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1976 from the text in FORMAT, which has length LEN.
1977 Return the address of the `next' pointer of the new segment. */
1979 static struct segment **
1980 make_segment (struct segment **segment, char *format, int len, int kind)
1982 char *fmt;
1984 *segment = (struct segment *) xmalloc (sizeof (struct segment));
1986 (*segment)->kind = kind;
1987 (*segment)->next = NULL;
1988 (*segment)->text_len = len;
1990 fmt = (*segment)->text = xmalloc (len + sizeof "d");
1991 strncpy (fmt, format, len);
1992 fmt += len;
1994 switch (kind & 0xff)
1996 case KIND_PLAIN: /* Plain text string, no % conversion. */
1997 case KIND_STOP: /* Terminate argument, no newline. */
1998 break;
2000 case 'a': /* atime in `ctime' format */
2001 case 'A': /* atime in user-specified strftime format */
2002 case 'c': /* ctime in `ctime' format */
2003 case 'C': /* ctime in user-specified strftime format */
2004 case 'F': /* filesystem type */
2005 case 'g': /* group name */
2006 case 'i': /* inode number */
2007 case 'l': /* object of symlink */
2008 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
2009 case 's': /* size in bytes */
2010 case 't': /* mtime in `ctime' format */
2011 case 'T': /* mtime in user-specified strftime format */
2012 case 'u': /* user name */
2013 case 'y': /* file type */
2014 case 'Y': /* symlink pointed file type */
2015 fprintf_stat_needed = true;
2016 /* FALLTHROUGH */
2017 case 'f': /* basename of path */
2018 case 'h': /* leading directories part of path */
2019 case 'H': /* ARGV element file was found under */
2020 case 'p': /* pathname */
2021 case 'P': /* pathname with ARGV element stripped */
2022 *fmt++ = 's';
2023 break;
2025 /* Numeric items that one might expect to honour
2026 * #, 0, + flags but which do not.
2028 case 'G': /* GID number */
2029 case 'U': /* UID number */
2030 case 'b': /* size in 512-byte blocks */
2031 case 'D': /* Filesystem device on which the file exits */
2032 case 'k': /* size in 1K blocks */
2033 case 'n': /* number of links */
2034 fprintf_stat_needed = true;
2035 *fmt++ = 's';
2036 break;
2038 /* Numeric items that DO honour #, 0, + flags.
2040 case 'd': /* depth in search tree (0 = ARGV element) */
2041 *fmt++ = 'd';
2042 break;
2044 case 'm': /* mode as octal number (perms only) */
2045 *fmt++ = 'o';
2046 fprintf_stat_needed = true;
2047 break;
2049 *fmt = '\0';
2051 return &(*segment)->next;
2054 static void
2055 check_path_safety(const char *action)
2057 const char *path = getenv("PATH");
2058 char *s;
2059 s = next_element(path, 1);
2060 while ((s = next_element ((char *) NULL, 1)) != NULL)
2062 if (0 == strcmp(s, "."))
2064 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)"),
2065 action);
2071 /* handles both exec and ok predicate */
2072 #if defined(NEW_EXEC)
2073 /* handles both exec and ok predicate */
2074 static boolean
2075 new_insert_exec_ok (const char *action,
2076 const struct parser_table *entry,
2077 char **argv,
2078 int *arg_ptr)
2080 int start, end; /* Indexes in ARGV of start & end of cmd. */
2081 int i; /* Index into cmd args */
2082 int saw_braces; /* True if previous arg was '{}'. */
2083 boolean allow_plus; /* True if + is a valid terminator */
2084 int brace_count; /* Number of instances of {}. */
2085 PRED_FUNC func = entry->pred_func;
2087 struct predicate *our_pred;
2088 struct exec_val *execp; /* Pointer for efficiency. */
2090 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2091 return false;
2093 our_pred = insert_primary_withpred (entry, func);
2094 our_pred->side_effects = our_pred->no_default_print = true;
2095 execp = &our_pred->args.exec_vec;
2097 if ((func != pred_okdir) && (func != pred_ok))
2099 allow_plus = true;
2100 execp->close_stdin = false;
2102 else
2104 allow_plus = false;
2105 /* If find reads stdin (i.e. for -ok and similar), close stdin
2106 * in the child to prevent some script from consiming the output
2107 * intended for find.
2109 execp->close_stdin = true;
2113 if ((func == pred_execdir) || (func == pred_okdir))
2115 options.ignore_readdir_race = false;
2116 check_path_safety(action);
2117 execp->use_current_dir = true;
2119 else
2121 execp->use_current_dir = false;
2124 our_pred->args.exec_vec.multiple = 0;
2126 /* Count the number of args with path replacements, up until the ';'.
2127 * Also figure out if the command is terminated by ";" or by "+".
2129 start = *arg_ptr;
2130 for (end = start, saw_braces=0, brace_count=0;
2131 (argv[end] != NULL)
2132 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2133 end++)
2135 /* For -exec and -execdir, "{} +" can terminate the command. */
2136 if ( allow_plus
2137 && argv[end][0] == '+' && argv[end][1] == 0
2138 && saw_braces)
2140 our_pred->args.exec_vec.multiple = 1;
2141 break;
2144 saw_braces = 0;
2145 if (strstr (argv[end], "{}"))
2147 saw_braces = 1;
2148 ++brace_count;
2150 if (0 == end && (func == pred_execdir || func == pred_okdir))
2152 /* The POSIX standard says that {} replacement should
2153 * occur even in the utility name. This is insecure
2154 * since it means we will be executing a command whose
2155 * name is chosen according to whatever find finds in
2156 * the filesystem. That can be influenced by an
2157 * attacker. Hence for -execdir and -okdir this is not
2158 * allowed. We can specify this as those options are
2159 * not defined by POSIX.
2161 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2166 /* Fail if no command given or no semicolon found. */
2167 if ((end == start) || (argv[end] == NULL))
2169 *arg_ptr = end;
2170 free(our_pred);
2171 return false;
2174 if (our_pred->args.exec_vec.multiple && brace_count > 1)
2177 const char *suffix;
2178 if (func == pred_execdir)
2179 suffix = "dir";
2180 else
2181 suffix = "";
2183 error(1, 0,
2184 _("Only one instance of {} is supported with -exec%s ... +"),
2185 suffix);
2188 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2189 bc_init_controlinfo(&execp->ctl);
2190 execp->ctl.exec_callback = launch;
2192 if (our_pred->args.exec_vec.multiple)
2194 /* "+" terminator, so we can just append our arguments after the
2195 * command and initial arguments.
2197 execp->replace_vec = NULL;
2198 execp->ctl.replace_pat = NULL;
2199 execp->ctl.rplen = 0;
2200 execp->ctl.lines_per_exec = 0; /* no limit */
2201 execp->ctl.args_per_exec = 0; /* no limit */
2203 /* remember how many arguments there are */
2204 execp->ctl.initial_argc = (end-start) - 1;
2206 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2207 bc_init_state(&execp->ctl, &execp->state, execp);
2209 /* Gather the initial arguments. Skip the {}. */
2210 for (i=start; i<end-1; ++i)
2212 bc_push_arg(&execp->ctl, &execp->state,
2213 argv[i], strlen(argv[i])+1,
2214 NULL, 0,
2218 else
2220 /* Semicolon terminator - more than one {} is supported, so we
2221 * have to do brace-replacement.
2223 execp->num_args = end - start;
2225 execp->ctl.replace_pat = "{}";
2226 execp->ctl.rplen = strlen(execp->ctl.replace_pat);
2227 execp->ctl.lines_per_exec = 0; /* no limit */
2228 execp->ctl.args_per_exec = 0; /* no limit */
2229 execp->replace_vec = xmalloc(sizeof(char*)*execp->num_args);
2232 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2233 bc_init_state(&execp->ctl, &execp->state, execp);
2235 /* Remember the (pre-replacement) arguments for later. */
2236 for (i=0; i<execp->num_args; ++i)
2238 execp->replace_vec[i] = argv[i+start];
2242 if (argv[end] == NULL)
2243 *arg_ptr = end;
2244 else
2245 *arg_ptr = end + 1;
2247 return true;
2249 #else
2250 /* handles both exec and ok predicate */
2251 static boolean
2252 old_insert_exec_ok (boolean (*func) (/* ??? */), char **argv, int *arg_ptr)
2254 int start, end; /* Indexes in ARGV of start & end of cmd. */
2255 int num_paths; /* Number of args with path replacements. */
2256 int path_pos; /* Index in array of path replacements. */
2257 int vec_pos; /* Index in array of args. */
2258 struct predicate *our_pred;
2259 struct exec_val *execp; /* Pointer for efficiency. */
2261 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2262 return false;
2264 /* Count the number of args with path replacements, up until the ';'. */
2265 start = *arg_ptr;
2266 for (end = start, num_paths = 0;
2267 (argv[end] != NULL)
2268 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2269 end++)
2270 if (strstr (argv[end], "{}"))
2271 num_paths++;
2272 /* Fail if no command given or no semicolon found. */
2273 if ((end == start) || (argv[end] == NULL))
2275 *arg_ptr = end;
2276 return false;
2279 our_pred = insert_primary (func);
2280 our_pred->side_effects = our_pred->no_default_print = true;
2281 execp = &our_pred->args.exec_vec;
2282 execp->usercontext = our_pred;
2283 execp->use_current_dir = false;
2284 execp->paths =
2285 (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1));
2286 execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1));
2287 /* Record the positions of all args, and the args with path replacements. */
2288 for (end = start, path_pos = vec_pos = 0;
2289 (argv[end] != NULL)
2290 && ((argv[end][0] != ';') || (argv[end][1] != '\0'));
2291 end++)
2293 register char *p;
2295 execp->paths[path_pos].count = 0;
2296 for (p = argv[end]; *p; ++p)
2297 if (p[0] == '{' && p[1] == '}')
2299 execp->paths[path_pos].count++;
2300 ++p;
2302 if (execp->paths[path_pos].count)
2304 execp->paths[path_pos].offset = vec_pos;
2305 execp->paths[path_pos].origarg = argv[end];
2306 path_pos++;
2308 execp->vec[vec_pos++] = argv[end];
2310 execp->paths[path_pos].offset = -1;
2311 execp->vec[vec_pos] = NULL;
2313 if (argv[end] == NULL)
2314 *arg_ptr = end;
2315 else
2316 *arg_ptr = end + 1;
2317 return true;
2319 #endif
2323 static boolean
2324 insert_exec_ok (const char *action, const struct parser_table *entry, char **argv, int *arg_ptr)
2326 #if defined(NEW_EXEC)
2327 return new_insert_exec_ok(action, entry, argv, arg_ptr);
2328 #else
2329 return old_insert_exec_ok(func, argv, arg_ptr);
2330 #endif
2335 /* Get a number of days and comparison type.
2336 STR is the ASCII representation.
2337 Set *NUM_DAYS to the number of days, taken as being from
2338 the current moment (or possibly midnight). Thus the sense of the
2339 comparison type appears to be reversed.
2340 Set *COMP_TYPE to the kind of comparison that is requested.
2342 Return true if all okay, false if input error.
2344 Used by -atime, -ctime and -mtime (parsers) to
2345 get the appropriate information for a time predicate processor. */
2347 static boolean
2348 get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
2350 boolean r = get_num (str, num_days, comp_type);
2351 if (r)
2352 switch (*comp_type)
2354 case COMP_LT: *comp_type = COMP_GT; break;
2355 case COMP_GT: *comp_type = COMP_LT; break;
2356 default: break;
2358 return r;
2361 /* Insert a time predicate PRED.
2362 ARGV is a pointer to the argument array.
2363 ARG_PTR is a pointer to an index into the array, incremented if
2364 all went well.
2366 Return true if input is valid, false if not.
2368 A new predicate node is assigned, along with an argument node
2369 obtained with malloc.
2371 Used by -atime, -ctime, and -mtime parsers. */
2373 static boolean
2374 insert_time (char **argv, int *arg_ptr, const struct parser_table* entry, PRED_FUNC pred)
2376 struct predicate *our_pred;
2377 uintmax_t num_days;
2378 enum comparison_type c_type;
2379 time_t t;
2381 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2382 return false;
2383 if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
2384 return false;
2386 /* Figure out the timestamp value we are looking for. */
2387 t = ( options.cur_day_start - num_days * DAYSECS
2388 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2390 if (1)
2392 /* We introduce a scope in which 'val' can be declared, for the
2393 * benefit of compilers that are really C89 compilers
2394 * which support intmax_t because config.h #defines it
2396 intmax_t val = ( (intmax_t)options.cur_day_start - num_days * DAYSECS
2397 + ((c_type == COMP_GT) ? DAYSECS - 1 : 0));
2398 t = val;
2400 /* Check for possibility of an overflow */
2401 if ( (intmax_t)t != val )
2403 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv[*arg_ptr]);
2407 our_pred = insert_primary_withpred (entry, pred);
2408 our_pred->args.info.kind = c_type;
2409 our_pred->args.info.negative = t < 0;
2410 our_pred->args.info.l_val = t;
2411 (*arg_ptr)++;
2412 #ifdef DEBUG
2413 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2414 fprintf (stderr, " type: %s %s ",
2415 (c_type == COMP_GT) ? "gt" :
2416 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2417 (c_type == COMP_GT) ? " >" :
2418 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
2419 t = our_pred->args.info.l_val;
2420 fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2421 if (c_type == COMP_EQ)
2423 t = our_pred->args.info.l_val += DAYSECS;
2424 fprintf (stderr, " < %ju %s",
2425 (uintmax_t) our_pred->args.info.l_val, ctime (&t));
2426 our_pred->args.info.l_val -= DAYSECS;
2428 #endif /* DEBUG */
2429 return true;
2432 /* Get a number with comparison information.
2433 The sense of the comparison information is 'normal'; that is,
2434 '+' looks for a count > than the number and '-' less than.
2436 STR is the ASCII representation of the number.
2437 Set *NUM to the number.
2438 Set *COMP_TYPE to the kind of comparison that is requested.
2440 Return true if all okay, false if input error. */
2442 static boolean
2443 get_num (char *str, uintmax_t *num, enum comparison_type *comp_type)
2445 if (str == NULL)
2446 return false;
2447 switch (str[0])
2449 case '+':
2450 *comp_type = COMP_GT;
2451 str++;
2452 break;
2453 case '-':
2454 *comp_type = COMP_LT;
2455 str++;
2456 break;
2457 default:
2458 *comp_type = COMP_EQ;
2459 break;
2462 return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK;
2465 /* Insert a number predicate.
2466 ARGV is a pointer to the argument array.
2467 *ARG_PTR is an index into ARGV, incremented if all went well.
2468 *PRED is the predicate processor to insert.
2470 Return true if input is valid, false if error.
2472 A new predicate node is assigned, along with an argument node
2473 obtained with malloc.
2475 Used by -inum and -links parsers. */
2477 static boolean
2478 insert_num (char **argv, int *arg_ptr, const struct parser_table *entry)
2480 struct predicate *our_pred;
2481 uintmax_t num;
2482 enum comparison_type c_type;
2484 if ((argv == NULL) || (argv[*arg_ptr] == NULL))
2485 return false;
2486 if (!get_num (argv[*arg_ptr], &num, &c_type))
2487 return false;
2488 our_pred = insert_primary (entry);
2489 our_pred->args.info.kind = c_type;
2490 our_pred->args.info.l_val = num;
2491 (*arg_ptr)++;
2492 #ifdef DEBUG
2493 fprintf (stderr, "inserting %s\n", our_pred->p_name);
2494 fprintf (stderr, " type: %s %s ",
2495 (c_type == COMP_GT) ? "gt" :
2496 ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
2497 (c_type == COMP_GT) ? " >" :
2498 ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
2499 fprintf (stderr, "%ju\n", our_pred->args.info.l_val);
2500 #endif /* DEBUG */
2501 return true;
2504 static FILE *
2505 open_output_file (char *path)
2507 FILE *f;
2509 if (!strcmp (path, "/dev/stderr"))
2510 return stderr;
2511 else if (!strcmp (path, "/dev/stdout"))
2512 return stdout;
2513 f = fopen_safer (path, "w");
2514 if (f == NULL)
2515 error (1, errno, "%s", path);
2516 return f;