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, 2006 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)
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,
28 #include "modechange.h"
36 #include "stdio-safer.h"
37 #include "regextype.h"
45 /* The presence of unistd.h is assumed by gnulib these days, so we
46 * might as well assume it too.
48 /* We need <unistd.h> for isatty(). */
53 # define _(Text) gettext (Text)
58 # define N_(String) gettext_noop (String)
60 /* See locate.c for explanation as to why not use (String) */
61 # define N_(String) String
64 #if !defined (isascii) || defined (STDC_HEADERS)
71 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
72 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
81 static boolean parse_accesscheck
PARAMS((const struct parser_table
* entry
, char **argv
, int *arg_ptr
));
82 static boolean parse_amin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
83 static boolean parse_and
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
84 static boolean parse_anewer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
85 static boolean parse_cmin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
86 static boolean parse_cnewer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
87 static boolean parse_comma
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_name
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
118 static boolean parse_negate
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
119 static boolean parse_newer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
120 static boolean parse_noleaf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
121 static boolean parse_nogroup
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
122 static boolean parse_nouser
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
123 static boolean parse_nowarn
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
124 static boolean parse_ok
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
125 static boolean parse_okdir
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
126 static boolean parse_or
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
127 static boolean parse_path
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
128 static boolean parse_perm
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
129 static boolean parse_print0
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
130 static boolean parse_printf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
131 static boolean parse_prune
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
132 static boolean parse_regex
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
133 static boolean parse_regextype
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
134 static boolean parse_samefile
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
136 static boolean parse_show_control_chars
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
138 static boolean parse_size
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
139 static boolean parse_time
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
140 static boolean parse_true
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
141 static boolean parse_type
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
142 static boolean parse_uid
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
143 static boolean parse_used
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
144 static boolean parse_user
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
145 static boolean parse_version
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
146 static boolean parse_wholename
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
147 static boolean parse_xdev
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
148 static boolean parse_ignore_race
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
149 static boolean parse_noignore_race
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
150 static boolean parse_warn
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
151 static boolean parse_xtype
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
152 static boolean parse_quit
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
154 boolean parse_print
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
157 static boolean insert_type
PARAMS((char **argv
, int *arg_ptr
, const struct parser_table
*entry
, PRED_FUNC which_pred
));
158 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, const struct parser_table
*entry
, int regex_options
));
159 static boolean insert_fprintf
PARAMS((FILE *fp
, const struct parser_table
*entry
, PRED_FUNC func
, char *argv
[], int *arg_ptr
));
161 static struct segment
**make_segment
PARAMS((struct segment
**segment
, char *format
, int len
,
162 int kind
, char format_char
, char aux_format_char
,
163 struct predicate
*pred
));
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 get_num
PARAMS((char *str
, uintmax_t *num
, enum comparison_type
*comp_type
));
167 static struct predicate
* insert_num
PARAMS((char *argv
[], int *arg_ptr
, const struct parser_table
*entry
));
168 static FILE *open_output_file
PARAMS((char *path
));
169 static boolean
stream_is_tty(FILE *fp
);
170 static boolean parse_noop
PARAMS((const struct parser_table
* entry
, char **argv
, int *arg_ptr
));
172 #define PASTE(x,y) x##y
173 #define STRINGIFY(s) #s
175 #define PARSE_OPTION(what,suffix) \
176 { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL }
178 #define PARSE_POSOPT(what,suffix) \
179 { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL }
181 #define PARSE_TEST(what,suffix) \
182 { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
184 #define PARSE_TEST_NP(what,suffix) \
185 { (ARG_TEST), (what), PASTE(parse_,suffix), NULL }
187 #define PARSE_ACTION(what,suffix) \
188 { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
190 #define PARSE_ACTION_NP(what,suffix) \
191 { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL }
193 #define PARSE_PUNCTUATION(what,suffix) \
194 { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
197 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
198 If they are in some Unix versions of find, they are marked `Unix'. */
200 static struct parser_table
const parse_table
[] =
202 PARSE_PUNCTUATION("!", negate
),
203 PARSE_PUNCTUATION("not", negate
), /* GNU */
204 PARSE_PUNCTUATION("(", open
),
205 PARSE_PUNCTUATION(")", close
),
206 PARSE_PUNCTUATION(",", comma
), /* GNU */
207 PARSE_PUNCTUATION("a", and),
208 PARSE_TEST ("amin", amin
), /* GNU */
209 PARSE_PUNCTUATION("and", and), /* GNU */
210 PARSE_TEST ("anewer", anewer
), /* GNU */
211 {ARG_TEST
, "atime", parse_time
, pred_atime
},
212 PARSE_TEST ("cmin", cmin
), /* GNU */
213 PARSE_TEST ("cnewer", cnewer
), /* GNU */
214 {ARG_TEST
, "ctime", parse_time
, pred_ctime
},
215 PARSE_POSOPT ("daystart", daystart
), /* GNU */
216 PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
217 PARSE_OPTION ("d", d
), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
218 PARSE_OPTION ("depth", depth
),
219 PARSE_TEST ("empty", empty
), /* GNU */
220 {ARG_ACTION
, "exec", parse_exec
, pred_exec
}, /* POSIX */
221 {ARG_TEST
, "executable", parse_accesscheck
, pred_executable
}, /* GNU, 4.3.0+ */
222 PARSE_ACTION ("execdir", execdir
), /* *BSD, GNU */
223 PARSE_ACTION ("fls", fls
), /* GNU */
224 PARSE_POSOPT ("follow", follow
), /* GNU, Unix */
225 PARSE_ACTION ("fprint", fprint
), /* GNU */
226 PARSE_ACTION ("fprint0", fprint0
), /* GNU */
227 {ARG_ACTION
, "fprintf", parse_fprintf
, pred_fprintf
}, /* GNU */
228 PARSE_TEST ("fstype", fstype
), /* GNU, Unix */
229 PARSE_TEST ("gid", gid
), /* GNU */
230 PARSE_TEST ("group", group
),
231 PARSE_OPTION ("ignore_readdir_race", ignore_race
), /* GNU */
232 PARSE_TEST ("ilname", ilname
), /* GNU */
233 PARSE_TEST ("iname", iname
), /* GNU */
234 PARSE_TEST ("inum", inum
), /* GNU, Unix */
235 PARSE_TEST ("ipath", ipath
), /* GNU, deprecated in favour of iwholename */
236 PARSE_TEST_NP ("iregex", iregex
), /* GNU */
237 PARSE_TEST_NP ("iwholename", iwholename
), /* GNU */
238 PARSE_TEST ("links", links
),
239 PARSE_TEST ("lname", lname
), /* GNU */
240 PARSE_ACTION ("ls", ls
), /* GNU, Unix */
241 PARSE_OPTION ("maxdepth", maxdepth
), /* GNU */
242 PARSE_OPTION ("mindepth", mindepth
), /* GNU */
243 PARSE_TEST ("mmin", mmin
), /* GNU */
244 PARSE_OPTION ("mount", xdev
), /* Unix */
245 {ARG_TEST
, "mtime", parse_time
, pred_mtime
},
246 PARSE_TEST ("name", name
),
247 #ifdef UNIMPLEMENTED_UNIX
248 PARSE(ARG_UNIMPLEMENTED
, "ncpio", ncpio
), /* Unix */
250 PARSE_TEST ("newer", newer
),
251 PARSE_OPTION ("noleaf", noleaf
), /* GNU */
252 PARSE_TEST ("nogroup", nogroup
),
253 PARSE_TEST ("nouser", nouser
),
254 PARSE_OPTION ("noignore_readdir_race", noignore_race
), /* GNU */
255 PARSE_POSOPT ("nowarn", nowarn
), /* GNU */
256 PARSE_PUNCTUATION("o", or),
257 PARSE_PUNCTUATION("or", or), /* GNU */
258 PARSE_ACTION ("ok", ok
),
259 PARSE_ACTION ("okdir", okdir
), /* GNU (-execdir is BSD) */
260 PARSE_TEST ("path", path
), /* GNU, HP-UX, GNU prefers wholename */
261 PARSE_TEST ("perm", perm
),
262 PARSE_ACTION ("print", print
),
263 PARSE_ACTION ("print0", print0
), /* GNU */
264 {ARG_ACTION
, "printf", parse_printf
, NULL
}, /* GNU */
265 PARSE_ACTION ("prune", prune
),
266 PARSE_ACTION ("quit", quit
), /* GNU */
267 {ARG_TEST
, "readable", parse_accesscheck
, pred_readable
}, /* GNU, 4.3.0+ */
268 PARSE_TEST ("regex", regex
), /* GNU */
269 PARSE_OPTION ("regextype", regextype
), /* GNU */
270 PARSE_TEST ("samefile", samefile
), /* GNU */
272 PARSE_OPTION ("show-control-chars", show_control_chars
), /* GNU, 4.3.0+ */
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 {ARG_TEST
, "writable", parse_accesscheck
, pred_writable
}, /* GNU, 4.3.0+ */
282 PARSE_OPTION ("xdev", xdev
),
283 PARSE_TEST ("xtype", xtype
), /* GNU */
284 #ifdef UNIMPLEMENTED_UNIX
285 /* It's pretty ugly for find to know about archive formats.
286 Plus what it could do with cpio archives is very limited.
287 Better to leave it out. */
288 PARSE(ARG_UNIMPLEMENTED
, "cpio", cpio
), /* Unix */
290 /* gnulib's stdbool.h might have made true and false into macros,
291 * so we can't leave named 'true' and 'false' tokens, so we have
292 * to expeant the relevant entries longhand.
294 {ARG_TEST
, "false", parse_false
, pred_false
}, /* GNU */
295 {ARG_TEST
, "true", parse_true
, pred_true
}, /* GNU */
296 {ARG_NOOP
, "noop", NULL
, pred_true
}, /* GNU, internal use only */
298 /* Various other cases that don't fit neatly into our macro scheme. */
299 {ARG_TEST
, "help", parse_help
, NULL
}, /* GNU */
300 {ARG_TEST
, "-help", parse_help
, NULL
}, /* GNU */
301 {ARG_TEST
, "version", parse_version
, NULL
}, /* GNU */
302 {ARG_TEST
, "-version", parse_version
, NULL
}, /* GNU */
307 static const char *first_nonoption_arg
= NULL
;
308 static const struct parser_table
*noop
= NULL
;
312 static const struct parser_table
*
318 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
320 if (ARG_NOOP
==parse_table
[i
].type
)
322 noop
= &(parse_table
[i
]);
333 set_follow_state(enum SymlinkOption opt
)
335 if (options
.debug_options
& DebugStat
)
337 /* For DebugStat, the choice is made at runtime within debug_stat()
338 * by checking the contents of the symlink_handling variable.
340 options
.xstat
= debug_stat
;
346 case SYMLINK_ALWAYS_DEREF
: /* -L */
347 options
.xstat
= optionl_stat
;
348 options
.no_leaf_check
= true;
351 case SYMLINK_NEVER_DEREF
: /* -P (default) */
352 options
.xstat
= optionp_stat
;
353 /* Can't turn no_leaf_check off because the user might have specified
358 case SYMLINK_DEREF_ARGSONLY
: /* -H */
359 options
.xstat
= optionh_stat
;
360 options
.no_leaf_check
= true;
363 options
.symlink_handling
= opt
;
368 parse_begin_user_args (char **args
, int argno
, const struct predicate
*last
, const struct predicate
*predicates
)
374 first_nonoption_arg
= NULL
;
378 parse_end_user_args (char **args
, int argno
, const struct predicate
*last
, const struct predicate
*predicates
)
390 /* Return a pointer to the parser function to invoke for predicate
392 Return NULL if SEARCH_NAME is not a valid predicate name. */
394 const struct parser_table
*
395 find_parser (char *search_name
)
398 const char *original_arg
= search_name
;
400 if (*search_name
== '-')
402 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
404 if (strcmp (parse_table
[i
].parser_name
, search_name
) == 0)
406 /* If this is an option, but we have already had a
407 * non-option argument, the user may be under the
408 * impression that the behaviour of the option
409 * argument is conditional on some preceding
410 * tests. This might typically be the case with,
411 * for example, -maxdepth.
413 * The options -daystart and -follow are exempt
414 * from this treatment, since their positioning
415 * in the command line does have an effect on
416 * subsequent tests but not previous ones. That
417 * might be intentional on the part of the user.
419 if (parse_table
[i
].type
!= ARG_POSITIONAL_OPTION
)
421 /* Something other than -follow/-daystart.
422 * If this is an option, check if it followed
423 * a non-option and if so, issue a warning.
425 if (parse_table
[i
].type
== ARG_OPTION
)
427 if ((first_nonoption_arg
!= NULL
)
428 && options
.warnings
)
430 /* option which follows a non-option */
432 _("warning: you have specified the %s "
433 "option after a non-option argument %s, "
434 "but options are not positional (%s affects "
435 "tests specified before it as well as those "
436 "specified after it). Please specify options "
437 "before other arguments.\n"),
445 /* Not an option or a positional option,
446 * so remember we've seen it in order to
447 * use it in a possible future warning message.
449 if (first_nonoption_arg
== NULL
)
451 first_nonoption_arg
= original_arg
;
456 return &parse_table
[i
];
463 estimate_file_age_success_rate(float num_days
)
467 /* Assume 1% of files have timestamps in the future */
470 else if (num_days
< 1)
472 /* Assume 30% of files have timestamps today */
475 else if (num_days
> 100)
477 /* Assume 30% of files are very old */
482 /* Assume 39% of files are between 1 and 100 days old. */
488 estimate_timestamp_success_rate(time_t when
)
490 int num_days
= (when
- options
.cur_day_start
) / 86400;
491 return estimate_file_age_success_rate(num_days
);
495 /* The parsers are responsible to continue scanning ARGV for
496 their arguments. Each parser knows what is and isn't
499 ARGV is the argument array.
500 *ARG_PTR is the index to start at in ARGV,
501 updated to point beyond the last element consumed.
503 The predicate structure is updated with the new information. */
506 parse_amin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
508 struct predicate
*our_pred
;
510 enum comparison_type c_type
;
513 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
515 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
517 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
518 our_pred
= insert_primary (entry
);
519 our_pred
->args
.info
.kind
= c_type
;
520 our_pred
->args
.info
.negative
= t
< 0;
521 our_pred
->args
.info
.l_val
= t
;
522 our_pred
->est_success_rate
= estimate_file_age_success_rate(num
);
528 parse_and (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
530 struct predicate
*our_pred
;
535 our_pred
= get_new_pred (entry
);
536 our_pred
->pred_func
= pred_and
;
537 our_pred
->p_type
= BI_OP
;
538 our_pred
->p_prec
= AND_PREC
;
539 our_pred
->need_stat
= our_pred
->need_type
= false;
544 parse_anewer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
546 struct predicate
*our_pred
;
547 struct stat stat_newer
;
549 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
551 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
552 error (1, errno
, "%s", argv
[*arg_ptr
]);
553 our_pred
= insert_primary (entry
);
554 our_pred
->args
.time
= stat_newer
.st_mtime
;
555 our_pred
->est_success_rate
= estimate_timestamp_success_rate(stat_newer
.st_mtime
);
561 parse_close (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
563 struct predicate
*our_pred
;
568 our_pred
= get_new_pred (entry
);
569 our_pred
->pred_func
= pred_close
;
570 our_pred
->p_type
= CLOSE_PAREN
;
571 our_pred
->p_prec
= NO_PREC
;
572 our_pred
->need_stat
= our_pred
->need_type
= false;
577 parse_cmin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
579 struct predicate
*our_pred
;
581 enum comparison_type c_type
;
584 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
586 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
588 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
589 our_pred
= insert_primary (entry
);
590 our_pred
->args
.info
.kind
= c_type
;
591 our_pred
->args
.info
.negative
= t
< 0;
592 our_pred
->args
.info
.l_val
= t
;
593 our_pred
->est_success_rate
= estimate_file_age_success_rate(num
);
599 parse_cnewer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
601 struct predicate
*our_pred
;
602 struct stat stat_newer
;
604 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
606 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
607 error (1, errno
, "%s", argv
[*arg_ptr
]);
608 our_pred
= insert_primary (entry
);
609 our_pred
->args
.time
= stat_newer
.st_mtime
;
610 our_pred
->est_success_rate
= estimate_timestamp_success_rate(stat_newer
.st_mtime
);
616 parse_comma (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
618 struct predicate
*our_pred
;
623 our_pred
= get_new_pred (entry
);
624 our_pred
->pred_func
= pred_comma
;
625 our_pred
->p_type
= BI_OP
;
626 our_pred
->p_prec
= COMMA_PREC
;
627 our_pred
->need_stat
= our_pred
->need_type
= false;
628 our_pred
->est_success_rate
= 1.0f
;
633 parse_daystart (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
641 if (options
.full_days
== false)
643 options
.cur_day_start
+= DAYSECS
;
644 local
= localtime (&options
.cur_day_start
);
645 options
.cur_day_start
-= (local
646 ? (local
->tm_sec
+ local
->tm_min
* 60
647 + local
->tm_hour
* 3600)
648 : options
.cur_day_start
% DAYSECS
);
649 options
.full_days
= true;
655 parse_delete (const struct parser_table
* entry
, char *argv
[], int *arg_ptr
)
657 struct predicate
*our_pred
;
661 our_pred
= insert_primary (entry
);
662 our_pred
->side_effects
= our_pred
->no_default_print
= true;
663 /* -delete implies -depth */
664 options
.do_dir_first
= false;
665 our_pred
->est_success_rate
= 1.0f
;
670 parse_depth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
676 options
.do_dir_first
= false;
677 return parse_noop(entry
, argv
, arg_ptr
);
681 parse_d (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
686 if (options
.warnings
)
689 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
691 return parse_depth(entry
, argv
, arg_ptr
);
695 parse_empty (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
697 struct predicate
*our_pred
;
701 our_pred
= insert_primary (entry
);
702 our_pred
->est_success_rate
= 0.01f
; /* assume 1% of files are empty. */
707 parse_exec (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
709 return insert_exec_ok ("-exec", entry
, argv
, arg_ptr
);
713 parse_execdir (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
715 return insert_exec_ok ("-execdir", entry
, argv
, arg_ptr
);
719 parse_false (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
721 struct predicate
*our_pred
;
726 our_pred
= insert_primary (entry
);
727 our_pred
->need_stat
= our_pred
->need_type
= false;
728 our_pred
->side_effects
= our_pred
->no_default_print
= false;
729 our_pred
->est_success_rate
= 0.0f
;
734 parse_fls (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
736 struct predicate
*our_pred
;
738 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
740 our_pred
= insert_primary (entry
);
741 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
742 our_pred
->side_effects
= our_pred
->no_default_print
= true;
743 our_pred
->est_success_rate
= 1.0f
;
749 parse_fprintf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
753 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
755 if (argv
[*arg_ptr
+ 1] == NULL
)
757 /* Ensure we get "missing arg" message, not "invalid arg". */
761 fp
= open_output_file (argv
[*arg_ptr
]);
763 return insert_fprintf (fp
, entry
, pred_fprintf
, argv
, arg_ptr
);
767 parse_follow (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
773 set_follow_state(SYMLINK_ALWAYS_DEREF
);
774 return parse_noop(entry
, argv
, arg_ptr
);
778 parse_fprint (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
780 struct predicate
*our_pred
;
782 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
784 our_pred
= insert_primary (entry
);
785 our_pred
->args
.printf_vec
.segment
= NULL
;
786 our_pred
->args
.printf_vec
.stream
= open_output_file (argv
[*arg_ptr
]);
787 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(our_pred
->args
.printf_vec
.stream
);
788 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
789 our_pred
->side_effects
= our_pred
->no_default_print
= true;
790 our_pred
->need_stat
= our_pred
->need_type
= false;
791 our_pred
->est_success_rate
= 1.0f
;
797 parse_fprint0 (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
799 struct predicate
*our_pred
;
801 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
803 our_pred
= insert_primary (entry
);
804 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
805 our_pred
->side_effects
= our_pred
->no_default_print
= true;
806 our_pred
->need_stat
= our_pred
->need_type
= false;
807 our_pred
->est_success_rate
= 1.0f
;
812 static float estimate_fstype_success_rate(const char *fsname
)
814 struct stat dir_stat
;
815 const char *dir
= "/";
816 if (0 == stat(dir
, &dir_stat
))
818 const char *fstype
= filesystem_type(&dir_stat
, dir
);
819 /* Assume most files are on the same filesystem type as the root fs. */
820 if (0 == strcmp(fsname
, fstype
))
830 parse_fstype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
832 struct predicate
*our_pred
;
834 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
836 our_pred
= insert_primary (entry
);
837 our_pred
->args
.str
= argv
[*arg_ptr
];
839 /* This is an expensive operation, so although there are
840 * circumstances where it is selective, we ignore this fact because
841 * we probably don't want to promote this test to the front anyway.
843 our_pred
->est_success_rate
= estimate_fstype_success_rate(argv
[*arg_ptr
]);
849 parse_gid (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
851 struct predicate
*p
= insert_num (argv
, arg_ptr
, entry
);
852 p
->est_success_rate
= (p
->args
.info
.l_val
< 100) ? 0.99 : 0.2;
857 parse_group (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
859 struct group
*cur_gr
;
860 struct predicate
*our_pred
;
864 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
866 cur_gr
= getgrnam (argv
[*arg_ptr
]);
869 gid
= cur_gr
->gr_gid
;
872 gid_len
= strspn (argv
[*arg_ptr
], "0123456789");
873 if ((gid_len
== 0) || (argv
[*arg_ptr
][gid_len
] != '\0'))
875 gid
= atoi (argv
[*arg_ptr
]);
877 our_pred
= insert_primary (entry
);
878 our_pred
->args
.gid
= gid
;
879 our_pred
->est_success_rate
= (our_pred
->args
.info
.l_val
< 100) ? 0.99 : 0.2;
885 parse_help (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
891 usage(stdout
, 0, NULL
);
893 default path is the current directory; default expression is -print\n\
894 expression may consist of: operators, options, tests, and actions:\n"));
896 operators (decreasing precedence; -and is implicit where no others are given):\n\
897 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
898 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
900 positional options (always true): -daystart -follow -regextype\n\n\
901 normal options (always true, specified before other expressions):\n\
902 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
903 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
905 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
906 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
907 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
908 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
910 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
911 -readable -writable -executable\n\
912 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
913 -used N -user NAME -xtype [bcdpfls]\n"));
915 actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
916 -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
917 -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
918 -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
920 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
921 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
922 email to <bug-findutils@gnu.org>."));
926 static float estimate_pattern_match_rate(const char *pattern
, int is_regex
)
928 if (strpbrk(pattern
, "*?[") || (is_regex
&& strpbrk(pattern
, ".")))
930 /* A wildcard; assume the pattern matches most files. */
940 parse_ilname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
942 struct predicate
*our_pred
;
944 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
946 our_pred
= insert_primary (entry
);
947 our_pred
->args
.str
= argv
[*arg_ptr
];
948 /* Use the generic glob pattern estimator to figure out how many
949 * links will match, but bear in mind that most files won't be links.
951 our_pred
->est_success_rate
= 0.1 * estimate_pattern_match_rate(our_pred
->args
.str
, 0);
957 /* sanity check the fnmatch() function to make sure
958 * it really is the GNU version.
961 fnmatch_sanitycheck(void)
963 /* fprintf(stderr, "Performing find sanity check..."); */
964 if (0 != fnmatch("foo", "foo", 0)
965 || 0 == fnmatch("Foo", "foo", 0)
966 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD
))
968 error (1, 0, _("sanity check of the fnmatch() library function failed."));
969 /* fprintf(stderr, "FAILED\n"); */
973 /* fprintf(stderr, "OK\n"); */
979 check_name_arg(const char *pred
, const char *arg
)
981 if (strchr(arg
, '/'))
983 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'."),
986 return true; /* allow it anyway */
992 parse_iname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
994 struct predicate
*our_pred
;
996 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
998 if (!check_name_arg("-iname", argv
[*arg_ptr
]))
1001 fnmatch_sanitycheck();
1003 our_pred
= insert_primary (entry
);
1004 our_pred
->need_stat
= our_pred
->need_type
= false;
1005 our_pred
->args
.str
= argv
[*arg_ptr
];
1006 our_pred
->est_success_rate
= estimate_pattern_match_rate(our_pred
->args
.str
, 0);
1012 parse_inum (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1014 struct predicate
*p
= insert_num (argv
, arg_ptr
, entry
);
1015 /* inode number is exact match only, so very low proportions of files match */
1016 p
->est_success_rate
= 1e-6;
1020 /* -ipath is deprecated (at RMS's request) in favour of
1021 * -iwholename. See the node "GNU Manuals" in standards.texi
1022 * for the rationale for this (basically, GNU prefers the use
1023 * of the phrase "file name" to "path name"
1026 parse_ipath (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1029 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
1031 return parse_iwholename(entry
, argv
, arg_ptr
);
1035 parse_iwholename (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1037 struct predicate
*our_pred
;
1039 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1042 fnmatch_sanitycheck();
1044 our_pred
= insert_primary_withpred (entry
, pred_ipath
);
1045 our_pred
->need_stat
= our_pred
->need_type
= false;
1046 our_pred
->args
.str
= argv
[*arg_ptr
];
1047 our_pred
->est_success_rate
= estimate_pattern_match_rate(our_pred
->args
.str
, 0);
1053 parse_iregex (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1055 return insert_regex (argv
, arg_ptr
, entry
, RE_ICASE
|options
.regex_options
);
1059 parse_links (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1061 struct predicate
*p
= insert_num (argv
, arg_ptr
, entry
);
1062 if (p
->args
.info
.l_val
== 1)
1063 p
->est_success_rate
= 0.99;
1064 else if (p
->args
.info
.l_val
== 2)
1065 p
->est_success_rate
= 0.01;
1067 p
->est_success_rate
= 1e-3;
1072 parse_lname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1074 struct predicate
*our_pred
;
1079 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1082 fnmatch_sanitycheck();
1084 our_pred
= insert_primary (entry
);
1085 our_pred
->args
.str
= argv
[*arg_ptr
];
1086 our_pred
->est_success_rate
= 0.1 * estimate_pattern_match_rate(our_pred
->args
.str
, 0);
1092 parse_ls (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1094 struct predicate
*our_pred
;
1099 our_pred
= insert_primary (entry
);
1100 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1105 parse_maxdepth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1110 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1112 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
1113 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
1115 options
.maxdepth
= atoi (argv
[*arg_ptr
]);
1116 if (options
.maxdepth
< 0)
1119 return parse_noop(entry
, argv
, arg_ptr
);
1123 parse_mindepth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1128 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1130 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
1131 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
1133 options
.mindepth
= atoi (argv
[*arg_ptr
]);
1134 if (options
.mindepth
< 0)
1137 return parse_noop(entry
, argv
, arg_ptr
);
1141 parse_mmin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1143 struct predicate
*our_pred
;
1145 enum comparison_type c_type
;
1148 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1150 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
1152 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
1153 our_pred
= insert_primary (entry
);
1154 our_pred
->args
.info
.kind
= c_type
;
1155 our_pred
->args
.info
.negative
= t
< 0;
1156 our_pred
->args
.info
.l_val
= t
;
1157 our_pred
->est_success_rate
= estimate_file_age_success_rate(num
);
1163 parse_name (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1165 struct predicate
*our_pred
;
1170 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1172 if (!check_name_arg("-name", argv
[*arg_ptr
]))
1174 fnmatch_sanitycheck();
1176 our_pred
= insert_primary (entry
);
1177 our_pred
->need_stat
= our_pred
->need_type
= false;
1178 our_pred
->args
.str
= argv
[*arg_ptr
];
1179 our_pred
->est_success_rate
= estimate_pattern_match_rate(our_pred
->args
.str
, 0);
1185 parse_negate (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1187 struct predicate
*our_pred
;
1192 our_pred
= get_new_pred_chk_op (entry
);
1193 our_pred
->pred_func
= pred_negate
;
1194 our_pred
->p_type
= UNI_OP
;
1195 our_pred
->p_prec
= NEGATE_PREC
;
1196 our_pred
->need_stat
= our_pred
->need_type
= false;
1201 parse_newer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1203 struct predicate
*our_pred
;
1204 struct stat stat_newer
;
1209 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1211 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
1212 error (1, errno
, "%s", argv
[*arg_ptr
]);
1213 our_pred
= insert_primary (entry
);
1214 our_pred
->args
.time
= stat_newer
.st_mtime
;
1215 our_pred
->est_success_rate
= estimate_timestamp_success_rate(stat_newer
.st_mtime
);
1221 parse_noleaf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1227 options
.no_leaf_check
= true;
1228 return parse_noop(entry
, argv
, arg_ptr
);
1232 /* Arbitrary amount by which to increase size
1233 of `uid_unused' and `gid_unused'. */
1234 #define ALLOC_STEP 2048
1236 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1237 char *uid_unused
= NULL
;
1239 /* Number of elements in `uid_unused'. */
1240 unsigned uid_allocated
;
1242 /* Similar for GIDs and group entries. */
1243 char *gid_unused
= NULL
;
1244 unsigned gid_allocated
;
1248 parse_nogroup (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1250 struct predicate
*our_pred
;
1255 our_pred
= insert_primary (entry
);
1256 our_pred
->est_success_rate
= 1e-4;
1258 if (gid_unused
== NULL
)
1262 gid_allocated
= ALLOC_STEP
;
1263 gid_unused
= xmalloc (gid_allocated
);
1264 memset (gid_unused
, 1, gid_allocated
);
1266 while ((gr
= getgrent ()) != NULL
)
1268 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
1270 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
1271 gid_unused
= xrealloc (gid_unused
, new_allocated
);
1272 memset (gid_unused
+ gid_allocated
, 1,
1273 new_allocated
- gid_allocated
);
1274 gid_allocated
= new_allocated
;
1276 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
1285 parse_nouser (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1287 struct predicate
*our_pred
;
1292 our_pred
= insert_primary (entry
);
1293 our_pred
->est_success_rate
= 1e-3;
1295 if (uid_unused
== NULL
)
1299 uid_allocated
= ALLOC_STEP
;
1300 uid_unused
= xmalloc (uid_allocated
);
1301 memset (uid_unused
, 1, uid_allocated
);
1303 while ((pw
= getpwent ()) != NULL
)
1305 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
1307 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
1308 uid_unused
= xrealloc (uid_unused
, new_allocated
);
1309 memset (uid_unused
+ uid_allocated
, 1,
1310 new_allocated
- uid_allocated
);
1311 uid_allocated
= new_allocated
;
1313 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
1322 parse_nowarn (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1328 options
.warnings
= false;
1329 return parse_noop(entry
, argv
, arg_ptr
);
1333 parse_ok (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1335 return insert_exec_ok ("-ok", entry
, argv
, arg_ptr
);
1339 parse_okdir (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1341 return insert_exec_ok ("-okdir", entry
, argv
, arg_ptr
);
1345 parse_open (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1347 struct predicate
*our_pred
;
1352 our_pred
= get_new_pred_chk_op (entry
);
1353 our_pred
->pred_func
= pred_open
;
1354 our_pred
->p_type
= OPEN_PAREN
;
1355 our_pred
->p_prec
= NO_PREC
;
1356 our_pred
->need_stat
= our_pred
->need_type
= false;
1361 parse_or (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1363 struct predicate
*our_pred
;
1368 our_pred
= get_new_pred (entry
);
1369 our_pred
->pred_func
= pred_or
;
1370 our_pred
->p_type
= BI_OP
;
1371 our_pred
->p_prec
= OR_PREC
;
1372 our_pred
->need_stat
= our_pred
->need_type
= false;
1376 /* -path is deprecated (at RMS's request) in favour of
1377 * -iwholename. See the node "GNU Manuals" in standards.texi
1378 * for the rationale for this (basically, GNU prefers the use
1379 * of the phrase "file name" to "path name".
1381 * We do not issue a warning that this usage is deprecated
1382 * since HPUX find supports this predicate also.
1385 parse_path (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1387 return parse_wholename(entry
, argv
, arg_ptr
);
1391 parse_wholename (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1393 struct predicate
*our_pred
;
1395 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1397 our_pred
= insert_primary_withpred (entry
, pred_path
);
1398 our_pred
->need_stat
= our_pred
->need_type
= false;
1399 our_pred
->args
.str
= argv
[*arg_ptr
];
1400 our_pred
->est_success_rate
= estimate_pattern_match_rate(our_pred
->args
.str
, 0);
1406 parse_perm (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1411 boolean havekind
= false;
1412 enum permissions_type kind
= PERM_EXACT
;
1413 struct mode_change
*change
= NULL
;
1414 struct predicate
*our_pred
;
1416 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1419 switch (argv
[*arg_ptr
][0])
1423 kind
= PERM_AT_LEAST
;
1429 change
= mode_compile (argv
[*arg_ptr
]);
1432 /* Most likely the caller is an old script that is still
1433 * using the obsolete GNU syntax '-perm +MODE'. This old
1434 * syntax was withdrawn in favor of '-perm /MODE' because
1435 * it is incompatible with POSIX in some cases, but we
1436 * still support uses of it that are not incompatible with
1445 /* This is a POSIX-compatible usage */
1453 case '/': /* GNU extension */
1461 /* For example, '-perm 0644', which is valid and matches
1462 * only files whose mode is exactly 0644.
1473 change
= mode_compile (argv
[*arg_ptr
] + mode_start
);
1475 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
1477 perm_val
[0] = mode_adjust (0, false, 0, change
, NULL
);
1478 perm_val
[1] = mode_adjust (0, true, 0, change
, NULL
);
1481 if (('/' == argv
[*arg_ptr
][0]) && (0 == perm_val
[0]) && (0 == perm_val
[1]))
1483 /* The meaning of -perm /000 will change in the future. It
1484 * currently matches no files, but like -perm -000 it should
1487 * Starting in 2005, we used to issue a warning message
1488 * informing the user that the behaviour would change in the
1489 * future. We have now changed the behaviour and issue a
1490 * warning message that the behaviour recently changed.
1493 _("warning: you have specified a mode pattern %s (which is "
1494 "equivalent to /000). The meaning of -perm /000 has now been "
1495 "changed to be consistent with -perm -000; that is, while it "
1496 "used to match no files, it now matches all files."),
1499 kind
= PERM_AT_LEAST
;
1502 /* The "magic" number below is just the fraction of files on my
1503 * own system that "-type l -xtype l" fails for (i.e. unbroken symlinks).
1504 * Actual totals are 1472 and 1073833.
1506 rate
= 0.9986; /* probably matches anything but a broken symlink */
1509 our_pred
= insert_primary (entry
);
1510 our_pred
->est_success_rate
= rate
;
1513 our_pred
->args
.perm
.kind
= kind
;
1518 switch (argv
[*arg_ptr
][0])
1521 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
1524 our_pred
->args
.perm
.kind
= PERM_ANY
;
1527 our_pred
->args
.perm
.kind
= PERM_EXACT
;
1531 memcpy (our_pred
->args
.perm
.val
, perm_val
, sizeof perm_val
);
1537 parse_print (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1539 struct predicate
*our_pred
;
1544 our_pred
= insert_primary (entry
);
1545 /* -print has the side effect of printing. This prevents us
1546 from doing undesired multiple printing when the user has
1547 already specified -print. */
1548 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1549 our_pred
->need_stat
= our_pred
->need_type
= false;
1550 our_pred
->args
.printf_vec
.segment
= NULL
;
1551 our_pred
->args
.printf_vec
.stream
= stdout
;
1552 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(stdout
);
1553 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
1559 parse_print0 (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1561 struct predicate
*our_pred
;
1566 our_pred
= insert_primary (entry
);
1567 /* -print0 has the side effect of printing. This prevents us
1568 from doing undesired multiple printing when the user has
1569 already specified -print0. */
1570 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1571 our_pred
->need_stat
= our_pred
->need_type
= false;
1576 parse_printf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1578 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1580 return insert_fprintf (stdout
, entry
, pred_fprintf
, argv
, arg_ptr
);
1584 parse_prune (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1586 struct predicate
*our_pred
;
1591 our_pred
= insert_primary (entry
);
1592 our_pred
->need_stat
= our_pred
->need_type
= false;
1593 /* -prune has a side effect that it does not descend into
1594 the current directory. */
1595 our_pred
->side_effects
= true;
1596 our_pred
->no_default_print
= false;
1601 parse_quit (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1603 struct predicate
*our_pred
= insert_primary (entry
);
1606 our_pred
->need_stat
= our_pred
->need_type
= false;
1607 our_pred
->side_effects
= true; /* Exiting is a side effect... */
1608 our_pred
->no_default_print
= false; /* Don't inhibit the default print, though. */
1609 our_pred
->est_success_rate
= 1e-6;
1615 parse_regextype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1617 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1620 /* collect the regex type name */
1621 options
.regex_options
= get_regex_type(argv
[*arg_ptr
]);
1624 return parse_noop(entry
, argv
, arg_ptr
);
1629 parse_regex (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1631 return insert_regex (argv
, arg_ptr
, entry
, options
.regex_options
);
1635 insert_regex (char **argv
, int *arg_ptr
, const struct parser_table
*entry
, int regex_options
)
1637 struct predicate
*our_pred
;
1638 struct re_pattern_buffer
*re
;
1639 const char *error_message
;
1641 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1643 our_pred
= insert_primary_withpred (entry
, pred_regex
);
1644 our_pred
->need_stat
= our_pred
->need_type
= false;
1645 re
= (struct re_pattern_buffer
*)
1646 xmalloc (sizeof (struct re_pattern_buffer
));
1647 our_pred
->args
.regex
= re
;
1648 re
->allocated
= 100;
1649 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1652 re_set_syntax(regex_options
);
1653 re
->syntax
= regex_options
;
1654 re
->translate
= NULL
;
1656 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1659 error (1, 0, "%s", error_message
);
1660 our_pred
->est_success_rate
= estimate_pattern_match_rate(argv
[*arg_ptr
], 1);
1666 parse_size (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1668 struct predicate
*our_pred
;
1670 enum comparison_type c_type
;
1675 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1677 len
= strlen (argv
[*arg_ptr
]);
1679 error (1, 0, _("invalid null argument to -size"));
1680 switch (argv
[*arg_ptr
][len
- 1])
1684 argv
[*arg_ptr
][len
- 1] = '\0';
1689 argv
[*arg_ptr
][len
- 1] = '\0';
1694 argv
[*arg_ptr
][len
- 1] = '\0';
1697 case 'M': /* Megabytes */
1698 blksize
= 1024*1024;
1699 argv
[*arg_ptr
][len
- 1] = '\0';
1702 case 'G': /* Gigabytes */
1703 blksize
= 1024*1024*1024;
1704 argv
[*arg_ptr
][len
- 1] = '\0';
1709 argv
[*arg_ptr
][len
- 1] = '\0';
1725 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1727 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1729 our_pred
= insert_primary (entry
);
1730 our_pred
->args
.size
.kind
= c_type
;
1731 our_pred
->args
.size
.blocksize
= blksize
;
1732 our_pred
->args
.size
.size
= num
;
1733 our_pred
->need_stat
= true;
1734 our_pred
->need_type
= false;
1736 if (COMP_GT
== c_type
)
1737 our_pred
->est_success_rate
= (num
*blksize
> 20480) ? 0.1 : 0.9;
1738 else if (COMP_LT
== c_type
)
1739 our_pred
->est_success_rate
= (num
*blksize
> 20480) ? 0.9 : 0.1;
1741 our_pred
->est_success_rate
= 0.01;
1749 parse_samefile (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1751 struct predicate
*our_pred
;
1754 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1756 if ((*options
.xstat
) (argv
[*arg_ptr
], &st
))
1757 error (1, errno
, "%s", argv
[*arg_ptr
]);
1759 our_pred
= insert_primary (entry
);
1760 our_pred
->args
.fileid
.ino
= st
.st_ino
;
1761 our_pred
->args
.fileid
.dev
= st
.st_dev
;
1762 our_pred
->need_type
= false;
1763 our_pred
->need_stat
= true;
1764 our_pred
->est_success_rate
= 0.01f
;
1771 parse_show_control_chars (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1774 const char *errmsg
= _("The -show-control-chars option takes a single argument which "
1775 "must be 'literal' or 'safe'");
1777 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1779 error (1, errno
, "%s", errmsg
);
1784 arg
= argv
[*arg_ptr
];
1786 if (0 == strcmp("literal", arg
))
1788 options
.literal_control_chars
= true;
1790 else if (0 == strcmp("safe", arg
))
1792 options
.literal_control_chars
= false;
1796 error (1, errno
, "%s", errmsg
);
1799 (*arg_ptr
)++; /* consume the argument. */
1807 parse_true (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1809 struct predicate
*our_pred
;
1814 our_pred
= insert_primary (entry
);
1815 our_pred
->need_stat
= our_pred
->need_type
= false;
1816 our_pred
->est_success_rate
= 1.0f
;
1821 parse_noop (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1824 return parse_true(get_noop(), argv
, arg_ptr
);
1828 parse_accesscheck (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1830 struct predicate
*our_pred
;
1833 our_pred
= insert_primary (entry
);
1834 our_pred
->need_stat
= our_pred
->need_type
= false;
1835 our_pred
->side_effects
= our_pred
->no_default_print
= false;
1836 if (our_pred
->pred_func
== pred_executable
)
1837 our_pred
->est_success_rate
= 0.2;
1839 our_pred
->est_success_rate
= 0.9;
1844 parse_type (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1846 return insert_type (argv
, arg_ptr
, entry
, pred_type
);
1850 parse_uid (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1852 struct predicate
*p
= insert_num (argv
, arg_ptr
, entry
);
1853 p
->est_success_rate
= (p
->args
.info
.l_val
< 100) ? 0.99 : 0.2;
1858 parse_used (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1860 struct predicate
*our_pred
;
1862 enum comparison_type c_type
;
1865 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1867 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1869 t
= num_days
* DAYSECS
;
1870 our_pred
= insert_primary (entry
);
1871 our_pred
->args
.info
.kind
= c_type
;
1872 our_pred
->args
.info
.negative
= t
< 0;
1873 our_pred
->args
.info
.l_val
= t
;
1874 our_pred
->est_success_rate
= estimate_file_age_success_rate(num_days
);
1880 parse_user (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1882 struct passwd
*cur_pwd
;
1883 struct predicate
*our_pred
;
1887 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1889 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1891 if (cur_pwd
!= NULL
)
1892 uid
= cur_pwd
->pw_uid
;
1895 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1896 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1898 uid
= atoi (argv
[*arg_ptr
]);
1900 our_pred
= insert_primary (entry
);
1901 our_pred
->args
.uid
= uid
;
1902 our_pred
->est_success_rate
= (our_pred
->args
.uid
< 100) ? 0.99 : 0.2;
1908 parse_version (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1910 extern char *version_string
;
1918 printf (_("GNU find version %s\n"), version_string
);
1919 printf (_("Features enabled: "));
1922 printf("CACHE_IDS ");
1930 printf("DEBUG_STAT ");
1933 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1937 #if defined(O_NOFOLLOW)
1938 printf("O_NOFOLLOW(%s) ",
1939 (options
.open_nofollow_available
? "enabled" : "disabled"));
1942 #if defined(LEAF_OPTIMISATION)
1943 printf("LEAF_OPTIMISATION ");
1947 if (is_fts_enabled())
1953 printf("CBO(level=%d) ", (int)(options
.optimisation_level
));
1958 /* For the moment, leave this as English in case someone wants
1959 to parse these strings. */
1968 parse_xdev (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1973 options
.stay_on_filesystem
= true;
1974 return parse_noop(entry
, argv
, arg_ptr
);
1978 parse_ignore_race (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1983 options
.ignore_readdir_race
= true;
1984 return parse_noop(entry
, argv
, arg_ptr
);
1988 parse_noignore_race (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1993 options
.ignore_readdir_race
= false;
1994 return parse_noop(entry
, argv
, arg_ptr
);
1998 parse_warn (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2003 options
.warnings
= true;
2004 return parse_noop(entry
, argv
, arg_ptr
);
2008 parse_xtype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2012 return insert_type (argv
, arg_ptr
, entry
, pred_xtype
);
2016 insert_type (char **argv
, int *arg_ptr
, const struct parser_table
*entry
, PRED_FUNC which_pred
)
2019 struct predicate
*our_pred
;
2022 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
2023 || (strlen (argv
[*arg_ptr
]) != 1))
2025 switch (argv
[*arg_ptr
][0])
2027 case 'b': /* block special */
2028 type_cell
= S_IFBLK
;
2031 case 'c': /* character special */
2032 type_cell
= S_IFCHR
;
2035 case 'd': /* directory */
2036 type_cell
= S_IFDIR
;
2039 case 'f': /* regular file */
2040 type_cell
= S_IFREG
;
2044 case 'l': /* symbolic link */
2045 type_cell
= S_IFLNK
;
2050 case 'p': /* pipe */
2051 type_cell
= S_IFIFO
;
2056 case 's': /* socket */
2057 type_cell
= S_IFSOCK
;
2062 case 'D': /* Solaris door */
2063 type_cell
= S_IFDOOR
;
2067 default: /* None of the above ... nuke 'em. */
2070 our_pred
= insert_primary_withpred (entry
, which_pred
);
2071 our_pred
->est_success_rate
= rate
;
2073 /* Figure out if we will need to stat the file, because if we don't
2074 * need to follow symlinks, we can avoid a stat call by using
2075 * struct dirent.d_type.
2077 if (which_pred
== pred_xtype
)
2079 our_pred
->need_stat
= true;
2080 our_pred
->need_type
= false;
2084 our_pred
->need_stat
= false; /* struct dirent is enough */
2085 our_pred
->need_type
= true;
2087 our_pred
->args
.type
= type_cell
;
2088 (*arg_ptr
)++; /* Move on to next argument. */
2093 /* Return true if the file accessed via FP is a terminal.
2096 stream_is_tty(FILE *fp
)
2098 int fd
= fileno(fp
);
2101 return false; /* not a valid stream */
2105 return isatty(fd
) ? true : false;
2113 /* XXX: do we need to pass FUNC to this function? */
2115 insert_fprintf (FILE *fp
, const struct parser_table
*entry
, PRED_FUNC func
, char **argv
, int *arg_ptr
)
2117 char *format
; /* Beginning of unprocessed format string. */
2118 register char *scan
; /* Current address in scanning `format'. */
2119 register char *scan2
; /* Address inside of element being scanned. */
2120 struct segment
**segmentp
; /* Address of current segment. */
2121 struct predicate
*our_pred
;
2123 format
= argv
[(*arg_ptr
)++];
2125 our_pred
= insert_primary_withpred (entry
, func
);
2126 our_pred
->side_effects
= our_pred
->no_default_print
= true;
2127 our_pred
->args
.printf_vec
.stream
= fp
;
2128 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(fp
);
2129 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
2130 our_pred
->need_type
= false;
2131 our_pred
->need_stat
= false;
2133 segmentp
= &our_pred
->args
.printf_vec
.segment
;
2136 for (scan
= format
; *scan
; scan
++)
2141 if (*scan2
>= '0' && *scan2
<= '7')
2145 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
2147 n
= 8 * n
+ *scan2
- '0';
2162 make_segment (segmentp
, format
, scan
- format
,
2165 if (our_pred
->need_stat
&& (our_pred
->p_cost
< NeedsStatInfo
))
2166 our_pred
->p_cost
= NeedsStatInfo
;
2184 /* *scan = '\\'; * it already is */
2188 _("warning: unrecognized escape `\\%c'"), *scan2
);
2193 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
2196 format
= scan2
+ 1; /* Move past the escape. */
2197 scan
= scan2
; /* Incremented immediately by `for'. */
2199 else if (*scan
== '%')
2203 /* Trailing %. We don't like those. */
2204 error (1, 0, _("error: %s at end of format string"), scan
);
2206 else if (scan
[1] == '%')
2208 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
2215 /* Scan past flags, width and precision, to verify kind. */
2216 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
2218 while (ISDIGIT (*scan2
))
2221 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
2223 if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2
))
2225 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
2226 KIND_FORMAT
, *scan2
, 0,
2231 else if (strchr ("ACT", *scan2
) && scan2
[1])
2233 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
2234 KIND_FORMAT
, scan2
[0], scan2
[1],
2242 /* An unrecognized % escape. Print the char after the %. */
2243 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
2245 segmentp
= make_segment (segmentp
, format
, scan
- format
,
2255 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
, 0, 0,
2260 /* Create a new fprintf segment in *SEGMENT, with type KIND,
2261 from the text in FORMAT, which has length LEN.
2262 Return the address of the `next' pointer of the new segment. */
2264 static struct segment
**
2265 make_segment (struct segment
**segment
,
2270 char aux_format_char
,
2271 struct predicate
*pred
)
2273 enum EvaluationCost mycost
= NeedsNothing
;
2276 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
2278 (*segment
)->segkind
= kind
;
2279 (*segment
)->format_char
[0] = format_char
;
2280 (*segment
)->format_char
[1] = aux_format_char
;
2281 (*segment
)->next
= NULL
;
2282 (*segment
)->text_len
= len
;
2284 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
2285 strncpy (fmt
, format
, len
);
2290 case KIND_PLAIN
: /* Plain text string, no % conversion. */
2291 case KIND_STOP
: /* Terminate argument, no newline. */
2292 assert(0 == format_char
);
2293 assert(0 == aux_format_char
);
2295 if (mycost
> pred
->p_cost
)
2296 pred
->p_cost
= NeedsNothing
;
2297 return &(*segment
)->next
;
2301 assert(kind
== KIND_FORMAT
);
2302 switch (format_char
)
2304 case 'l': /* object of symlink */
2305 pred
->need_stat
= true;
2306 mycost
= NeedsLinkName
;
2310 case 'y': /* file type */
2311 pred
->need_type
= true;
2316 case 'a': /* atime in `ctime' format */
2317 case 'A': /* atime in user-specified strftime format */
2318 case 'c': /* ctime in `ctime' format */
2319 case 'C': /* ctime in user-specified strftime format */
2320 case 'F': /* filesystem type */
2321 case 'g': /* group name */
2322 case 'i': /* inode number */
2323 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
2324 case 's': /* size in bytes */
2325 case 't': /* mtime in `ctime' format */
2326 case 'T': /* mtime in user-specified strftime format */
2327 case 'u': /* user name */
2328 pred
->need_stat
= true;
2329 mycost
= NeedsStatInfo
;
2333 case 'S': /* sparseness */
2334 pred
->need_stat
= true;
2335 mycost
= NeedsStatInfo
;
2339 case 'Y': /* symlink pointed file type */
2340 pred
->need_stat
= true;
2341 mycost
= NeedsType
; /* true for amortised effect */
2345 case 'f': /* basename of path */
2346 case 'h': /* leading directories part of path */
2347 case 'p': /* pathname */
2348 case 'P': /* pathname with ARGV element stripped */
2352 case 'H': /* ARGV element file was found under */
2356 /* Numeric items that one might expect to honour
2357 * #, 0, + flags but which do not.
2359 case 'G': /* GID number */
2360 case 'U': /* UID number */
2361 case 'b': /* size in 512-byte blocks */
2362 case 'D': /* Filesystem device on which the file exits */
2363 case 'k': /* size in 1K blocks */
2364 case 'n': /* number of links */
2365 pred
->need_stat
= true;
2366 mycost
= NeedsStatInfo
;
2370 /* Numeric items that DO honour #, 0, + flags.
2372 case 'd': /* depth in search tree (0 = ARGV element) */
2376 case 'm': /* mode as octal number (perms only) */
2378 pred
->need_stat
= true;
2379 mycost
= NeedsStatInfo
;
2386 _("error: the format directive `%%%c' is reserved for future use"),
2393 if (mycost
> pred
->p_cost
)
2394 pred
->p_cost
= mycost
;
2395 return &(*segment
)->next
;
2399 check_path_safety(const char *action
, char **argv
)
2401 const char *path
= getenv("PATH");
2406 s
= next_element(path
, 1);
2407 while ((s
= next_element ((char *) NULL
, 1)) != NULL
)
2409 if (0 == strcmp(s
, "."))
2411 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)"),
2414 else if ('/' != s
[0])
2416 /* Relative paths are also dangerous in $PATH. */
2417 error(1, 0, _("The ralative path %s is included in the PATH environment variable, which is insecure in combination with the %s action of find. Please remove that entry from $PATH"),
2424 /* handles both exec and ok predicate */
2426 new_insert_exec_ok (const char *action
,
2427 const struct parser_table
*entry
,
2431 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2432 int i
; /* Index into cmd args */
2433 int saw_braces
; /* True if previous arg was '{}'. */
2434 boolean allow_plus
; /* True if + is a valid terminator */
2435 int brace_count
; /* Number of instances of {}. */
2436 PRED_FUNC func
= entry
->pred_func
;
2437 enum BC_INIT_STATUS bcstatus
;
2439 struct predicate
*our_pred
;
2440 struct exec_val
*execp
; /* Pointer for efficiency. */
2442 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2445 our_pred
= insert_primary_withpred (entry
, func
);
2446 our_pred
->side_effects
= our_pred
->no_default_print
= true;
2447 our_pred
->need_type
= our_pred
->need_stat
= false;
2449 execp
= &our_pred
->args
.exec_vec
;
2451 if ((func
!= pred_okdir
) && (func
!= pred_ok
))
2454 execp
->close_stdin
= false;
2459 /* If find reads stdin (i.e. for -ok and similar), close stdin
2460 * in the child to prevent some script from consiming the output
2461 * intended for find.
2463 execp
->close_stdin
= true;
2467 if ((func
== pred_execdir
) || (func
== pred_okdir
))
2469 options
.ignore_readdir_race
= false;
2470 check_path_safety(action
, argv
);
2471 execp
->use_current_dir
= true;
2475 execp
->use_current_dir
= false;
2478 our_pred
->args
.exec_vec
.multiple
= 0;
2480 /* Count the number of args with path replacements, up until the ';'.
2481 * Also figure out if the command is terminated by ";" or by "+".
2484 for (end
= start
, saw_braces
=0, brace_count
=0;
2486 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2489 /* For -exec and -execdir, "{} +" can terminate the command. */
2491 && argv
[end
][0] == '+' && argv
[end
][1] == 0
2494 our_pred
->args
.exec_vec
.multiple
= 1;
2499 if (strstr (argv
[end
], "{}"))
2504 if (0 == end
&& (func
== pred_execdir
|| func
== pred_okdir
))
2506 /* The POSIX standard says that {} replacement should
2507 * occur even in the utility name. This is insecure
2508 * since it means we will be executing a command whose
2509 * name is chosen according to whatever find finds in
2510 * the filesystem. That can be influenced by an
2511 * attacker. Hence for -execdir and -okdir this is not
2512 * allowed. We can specify this as those options are
2513 * not defined by POSIX.
2515 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2520 /* Fail if no command given or no semicolon found. */
2521 if ((end
== start
) || (argv
[end
] == NULL
))
2528 if (our_pred
->args
.exec_vec
.multiple
&& brace_count
> 1)
2532 if (func
== pred_execdir
)
2538 _("Only one instance of {} is supported with -exec%s ... +"),
2542 /* We use a switch statement here so that
2543 * the compiler warns us when we forget to handle a
2544 * newly invented enum value.
2546 bcstatus
= bc_init_controlinfo(&execp
->ctl
);
2549 case BC_INIT_ENV_TOO_BIG
:
2551 _("The environment is too large for exec()."));
2554 /* Good news. Carry on. */
2557 bc_use_sensible_arg_max(&execp
->ctl
);
2560 execp
->ctl
.exec_callback
= launch
;
2562 if (our_pred
->args
.exec_vec
.multiple
)
2564 /* "+" terminator, so we can just append our arguments after the
2565 * command and initial arguments.
2567 execp
->replace_vec
= NULL
;
2568 execp
->ctl
.replace_pat
= NULL
;
2569 execp
->ctl
.rplen
= 0;
2570 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2571 execp
->ctl
.args_per_exec
= 0; /* no limit */
2573 /* remember how many arguments there are */
2574 execp
->ctl
.initial_argc
= (end
-start
) - 1;
2576 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2577 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2579 /* Gather the initial arguments. Skip the {}. */
2580 for (i
=start
; i
<end
-1; ++i
)
2582 bc_push_arg(&execp
->ctl
, &execp
->state
,
2583 argv
[i
], strlen(argv
[i
])+1,
2590 /* Semicolon terminator - more than one {} is supported, so we
2591 * have to do brace-replacement.
2593 execp
->num_args
= end
- start
;
2595 execp
->ctl
.replace_pat
= "{}";
2596 execp
->ctl
.rplen
= strlen(execp
->ctl
.replace_pat
);
2597 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2598 execp
->ctl
.args_per_exec
= 0; /* no limit */
2599 execp
->replace_vec
= xmalloc(sizeof(char*)*execp
->num_args
);
2602 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2603 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2605 /* Remember the (pre-replacement) arguments for later. */
2606 for (i
=0; i
<execp
->num_args
; ++i
)
2608 execp
->replace_vec
[i
] = argv
[i
+start
];
2612 if (argv
[end
] == NULL
)
2623 insert_exec_ok (const char *action
, const struct parser_table
*entry
, char **argv
, int *arg_ptr
)
2625 return new_insert_exec_ok(action
, entry
, argv
, arg_ptr
);
2630 /* Get a number of days and comparison type.
2631 STR is the ASCII representation.
2632 Set *NUM_DAYS to the number of days, taken as being from
2633 the current moment (or possibly midnight). Thus the sense of the
2634 comparison type appears to be reversed.
2635 Set *COMP_TYPE to the kind of comparison that is requested.
2637 Return true if all okay, false if input error.
2639 Used by -atime, -ctime and -mtime (parsers) to
2640 get the appropriate information for a time predicate processor. */
2643 get_num_days (char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
)
2645 boolean r
= get_num (str
, num_days
, comp_type
);
2649 case COMP_LT
: *comp_type
= COMP_GT
; break;
2650 case COMP_GT
: *comp_type
= COMP_LT
; break;
2656 /* Insert a time predicate PRED.
2657 ARGV is a pointer to the argument array.
2658 ARG_PTR is a pointer to an index into the array, incremented if
2661 Return true if input is valid, false if not.
2663 A new predicate node is assigned, along with an argument node
2664 obtained with malloc.
2666 Used by -atime, -ctime, and -mtime parsers. */
2669 parse_time (const struct parser_table
* entry
, char *argv
[], int *arg_ptr
)
2671 struct predicate
*our_pred
;
2673 enum comparison_type c_type
;
2676 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2678 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
2681 /* Figure out the timestamp value we are looking for. */
2682 t
= ( options
.cur_day_start
- num_days
* DAYSECS
2683 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2687 /* We introduce a scope in which 'val' can be declared, for the
2688 * benefit of compilers that are really C89 compilers
2689 * which support intmax_t because config.h #defines it
2691 intmax_t val
= ( (intmax_t)options
.cur_day_start
- num_days
* DAYSECS
2692 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2695 /* Check for possibility of an overflow */
2696 if ( (intmax_t)t
!= val
)
2698 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv
[*arg_ptr
]);
2702 our_pred
= insert_primary (entry
);
2703 our_pred
->args
.info
.kind
= c_type
;
2704 our_pred
->args
.info
.negative
= t
< 0;
2705 our_pred
->args
.info
.l_val
= t
;
2706 our_pred
->est_success_rate
= estimate_file_age_success_rate(num_days
);
2709 if (options
.debug_options
& DebugExpressionTree
)
2711 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2712 fprintf (stderr
, " type: %s %s ",
2713 (c_type
== COMP_GT
) ? "gt" :
2714 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2715 (c_type
== COMP_GT
) ? " >" :
2716 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
2717 t
= our_pred
->args
.info
.l_val
;
2718 fprintf (stderr
, "%ju %s", (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2719 if (c_type
== COMP_EQ
)
2721 t
= our_pred
->args
.info
.l_val
+= DAYSECS
;
2722 fprintf (stderr
, " < %ju %s",
2723 (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2724 our_pred
->args
.info
.l_val
-= DAYSECS
;
2731 /* Get a number with comparison information.
2732 The sense of the comparison information is 'normal'; that is,
2733 '+' looks for a count > than the number and '-' less than.
2735 STR is the ASCII representation of the number.
2736 Set *NUM to the number.
2737 Set *COMP_TYPE to the kind of comparison that is requested.
2739 Return true if all okay, false if input error. */
2742 get_num (char *str
, uintmax_t *num
, enum comparison_type
*comp_type
)
2749 *comp_type
= COMP_GT
;
2753 *comp_type
= COMP_LT
;
2757 *comp_type
= COMP_EQ
;
2761 return xstrtoumax (str
, NULL
, 10, num
, "") == LONGINT_OK
;
2764 /* Insert a number predicate.
2765 ARGV is a pointer to the argument array.
2766 *ARG_PTR is an index into ARGV, incremented if all went well.
2767 *PRED is the predicate processor to insert.
2769 Return true if input is valid, false if error.
2771 A new predicate node is assigned, along with an argument node
2772 obtained with malloc.
2774 Used by -inum and -links parsers. */
2776 static struct predicate
*
2777 insert_num (char **argv
, int *arg_ptr
, const struct parser_table
*entry
)
2779 struct predicate
*our_pred
;
2781 enum comparison_type c_type
;
2783 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2785 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
2787 our_pred
= insert_primary (entry
);
2788 our_pred
->args
.info
.kind
= c_type
;
2789 our_pred
->args
.info
.l_val
= num
;
2792 if (options
.debug_options
& DebugExpressionTree
)
2794 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2795 fprintf (stderr
, " type: %s %s ",
2796 (c_type
== COMP_GT
) ? "gt" :
2797 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2798 (c_type
== COMP_GT
) ? " >" :
2799 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
2800 fprintf (stderr
, "%ju\n", our_pred
->args
.info
.l_val
);
2806 open_output_file (char *path
)
2810 if (!strcmp (path
, "/dev/stderr"))
2812 else if (!strcmp (path
, "/dev/stdout"))
2814 f
= fopen_safer (path
, "w");
2816 error (1, errno
, "%s", path
);