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, 2007 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 3 of the License, or
8 (at your option) 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, see <http://www.gnu.org/licenses/>.
29 #include "modechange.h"
37 #include "stdio-safer.h"
38 #include "regextype.h"
39 #include "stat-time.h"
44 #include "findutils-version.h"
49 /* The presence of unistd.h is assumed by gnulib these days, so we
50 * might as well assume it too.
52 /* We need <unistd.h> for isatty(). */
58 # define _(Text) gettext (Text)
63 # define N_(String) gettext_noop (String)
65 /* See locate.c for explanation as to why not use (String) */
66 # define N_(String) String
69 #if !defined (isascii) || defined (STDC_HEADERS)
76 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
77 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
86 static boolean parse_accesscheck
PARAMS((const struct parser_table
* entry
, char **argv
, int *arg_ptr
));
87 static boolean parse_amin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
88 static boolean parse_and
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
89 static boolean parse_anewer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
90 static boolean parse_cmin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
91 static boolean parse_cnewer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
92 static boolean parse_comma
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
93 static boolean parse_daystart
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
94 static boolean parse_delete
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
95 static boolean parse_d
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
96 static boolean parse_depth
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
97 static boolean parse_empty
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
98 static boolean parse_exec
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
99 static boolean parse_execdir
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
100 static boolean parse_false
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
101 static boolean parse_fls
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
102 static boolean parse_fprintf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
103 static boolean parse_follow
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
104 static boolean parse_fprint
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
105 static boolean parse_fprint0
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
106 static boolean parse_fstype
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
107 static boolean parse_gid
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
108 static boolean parse_group
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
109 static boolean parse_help
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
110 static boolean parse_ilname
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
111 static boolean parse_iname
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
112 static boolean parse_inum
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
113 static boolean parse_ipath
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
114 static boolean parse_iregex
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
115 static boolean parse_iwholename
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
116 static boolean parse_links
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
117 static boolean parse_lname
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
118 static boolean parse_ls
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
119 static boolean parse_maxdepth
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
120 static boolean parse_mindepth
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
121 static boolean parse_mmin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
122 static boolean parse_name
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
123 static boolean parse_negate
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
124 static boolean parse_newer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
125 static boolean parse_newerXY
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
126 static boolean parse_noleaf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
127 static boolean parse_nogroup
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
128 static boolean parse_nouser
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
129 static boolean parse_nowarn
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
130 static boolean parse_ok
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
131 static boolean parse_okdir
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
132 static boolean parse_or
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
133 static boolean parse_path
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
134 static boolean parse_perm
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
135 static boolean parse_print0
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
136 static boolean parse_printf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
137 static boolean parse_prune
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
138 static boolean parse_regex
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
139 static boolean parse_regextype
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
140 static boolean parse_samefile
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
142 static boolean parse_show_control_chars
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
144 static boolean parse_size
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
145 static boolean parse_time
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
146 static boolean parse_true
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
147 static boolean parse_type
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
148 static boolean parse_uid
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
149 static boolean parse_used
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
150 static boolean parse_user
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
151 static boolean parse_version
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
152 static boolean parse_wholename
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
153 static boolean parse_xdev
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
154 static boolean parse_ignore_race
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
155 static boolean parse_noignore_race
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
156 static boolean parse_warn
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
157 static boolean parse_xtype
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
158 static boolean parse_quit
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
160 boolean parse_print
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
163 static boolean insert_type
PARAMS((char **argv
, int *arg_ptr
,
164 const struct parser_table
*entry
,
165 PRED_FUNC which_pred
));
166 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
,
167 const struct parser_table
*entry
,
169 static boolean
insert_fprintf (struct format_val
*vec
,
170 const struct parser_table
*entry
,
174 static struct segment
**make_segment
PARAMS((struct segment
**segment
,
175 char *format
, int len
,
176 int kind
, char format_char
,
177 char aux_format_char
,
178 struct predicate
*pred
));
179 static boolean insert_exec_ok
PARAMS((const char *action
,
180 const struct parser_table
*entry
,
184 static boolean get_comp_type
PARAMS((const char **str
,
185 enum comparison_type
*comp_type
));
186 static boolean get_relative_timestamp
PARAMS((const char *str
,
187 struct time_val
*tval
,
190 const char *overflowmessage
));
191 static boolean get_num
PARAMS((const char *str
,
193 enum comparison_type
*comp_type
));
194 static struct predicate
* insert_num
PARAMS((char *argv
[], int *arg_ptr
,
195 const struct parser_table
*entry
));
196 static void open_output_file (const char *path
, struct format_val
*p
);
197 static void open_stdout (struct format_val
*p
);
198 static boolean
stream_is_tty(FILE *fp
);
199 static boolean parse_noop
PARAMS((const struct parser_table
* entry
,
200 char **argv
, int *arg_ptr
));
202 #define PASTE(x,y) x##y
203 #define STRINGIFY(s) #s
205 #define PARSE_OPTION(what,suffix) \
206 { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL }
208 #define PARSE_POSOPT(what,suffix) \
209 { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL }
211 #define PARSE_TEST(what,suffix) \
212 { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
214 #define PARSE_TEST_NP(what,suffix) \
215 { (ARG_TEST), (what), PASTE(parse_,suffix), NULL }
217 #define PARSE_ACTION(what,suffix) \
218 { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
220 #define PARSE_ACTION_NP(what,suffix) \
221 { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL }
223 #define PARSE_PUNCTUATION(what,suffix) \
224 { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
227 /* Predicates we cannot handle in the usual way. If you add an entry
228 * to this table, double-check the switch statement in
229 * pred_sanity_check() to make sure that the new case is being
232 static struct parser_table
const parse_entry_newerXY
=
234 ARG_SPECIAL_PARSE
, "newerXY", parse_newerXY
, pred_newerXY
/* BSD */
237 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
238 If they are in some Unix versions of find, they are marked `Unix'. */
240 static struct parser_table
const parse_table
[] =
242 PARSE_PUNCTUATION("!", negate
), /* POSIX */
243 PARSE_PUNCTUATION("not", negate
), /* GNU */
244 PARSE_PUNCTUATION("(", openparen
), /* POSIX */
245 PARSE_PUNCTUATION(")", closeparen
), /* POSIX */
246 PARSE_PUNCTUATION(",", comma
), /* GNU */
247 PARSE_PUNCTUATION("a", and), /* POSIX */
248 PARSE_TEST ("amin", amin
), /* GNU */
249 PARSE_PUNCTUATION("and", and), /* GNU */
250 PARSE_TEST ("anewer", anewer
), /* GNU */
251 {ARG_TEST
, "atime", parse_time
, pred_atime
}, /* POSIX */
252 PARSE_TEST ("cmin", cmin
), /* GNU */
253 PARSE_TEST ("cnewer", cnewer
), /* GNU */
254 {ARG_TEST
, "ctime", parse_time
, pred_ctime
}, /* POSIX */
255 PARSE_POSOPT ("daystart", daystart
), /* GNU */
256 PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
257 PARSE_OPTION ("d", d
), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
258 PARSE_OPTION ("depth", depth
), /* POSIX */
259 PARSE_TEST ("empty", empty
), /* GNU */
260 {ARG_ACTION
, "exec", parse_exec
, pred_exec
}, /* POSIX */
261 {ARG_TEST
, "executable", parse_accesscheck
, pred_executable
}, /* GNU, 4.3.0+ */
262 PARSE_ACTION ("execdir", execdir
), /* *BSD, GNU */
263 PARSE_ACTION ("fls", fls
), /* GNU */
264 PARSE_POSOPT ("follow", follow
), /* GNU, Unix */
265 PARSE_ACTION ("fprint", fprint
), /* GNU */
266 PARSE_ACTION ("fprint0", fprint0
), /* GNU */
267 {ARG_ACTION
, "fprintf", parse_fprintf
, pred_fprintf
}, /* GNU */
268 PARSE_TEST ("fstype", fstype
), /* GNU, Unix */
269 PARSE_TEST ("gid", gid
), /* GNU */
270 PARSE_TEST ("group", group
), /* POSIX */
271 PARSE_OPTION ("ignore_readdir_race", ignore_race
), /* GNU */
272 PARSE_TEST ("ilname", ilname
), /* GNU */
273 PARSE_TEST ("iname", iname
), /* GNU */
274 PARSE_TEST ("inum", inum
), /* GNU, Unix */
275 PARSE_TEST ("ipath", ipath
), /* GNU, deprecated in favour of iwholename */
276 PARSE_TEST_NP ("iregex", iregex
), /* GNU */
277 PARSE_TEST_NP ("iwholename", iwholename
), /* GNU */
278 PARSE_TEST ("links", links
), /* POSIX */
279 PARSE_TEST ("lname", lname
), /* GNU */
280 PARSE_ACTION ("ls", ls
), /* GNU, Unix */
281 PARSE_OPTION ("maxdepth", maxdepth
), /* GNU */
282 PARSE_OPTION ("mindepth", mindepth
), /* GNU */
283 PARSE_TEST ("mmin", mmin
), /* GNU */
284 PARSE_OPTION ("mount", xdev
), /* Unix */
285 {ARG_TEST
, "mtime", parse_time
, pred_mtime
}, /* POSIX */
286 PARSE_TEST ("name", name
),
287 #ifdef UNIMPLEMENTED_UNIX
288 PARSE(ARG_UNIMPLEMENTED
, "ncpio", ncpio
), /* Unix */
290 PARSE_TEST ("newer", newer
), /* POSIX */
291 {ARG_TEST
, "atime", parse_time
, pred_atime
}, /* POSIX */
292 PARSE_OPTION ("noleaf", noleaf
), /* GNU */
293 PARSE_TEST ("nogroup", nogroup
), /* POSIX */
294 PARSE_TEST ("nouser", nouser
), /* POSIX */
295 PARSE_OPTION ("noignore_readdir_race", noignore_race
), /* GNU */
296 PARSE_POSOPT ("nowarn", nowarn
), /* GNU */
297 PARSE_PUNCTUATION("o", or), /* POSIX */
298 PARSE_PUNCTUATION("or", or), /* GNU */
299 PARSE_ACTION ("ok", ok
), /* POSIX */
300 PARSE_ACTION ("okdir", okdir
), /* GNU (-execdir is BSD) */
301 PARSE_TEST ("path", path
), /* GNU, HP-UX, RMS prefers wholename, but anyway soon POSIX */
302 PARSE_TEST ("perm", perm
), /* POSIX */
303 PARSE_ACTION ("print", print
), /* POSIX */
304 PARSE_ACTION ("print0", print0
), /* GNU */
305 {ARG_ACTION
, "printf", parse_printf
, NULL
}, /* GNU */
306 PARSE_ACTION ("prune", prune
), /* POSIX */
307 PARSE_ACTION ("quit", quit
), /* GNU */
308 {ARG_TEST
, "readable", parse_accesscheck
, pred_readable
}, /* GNU, 4.3.0+ */
309 PARSE_TEST ("regex", regex
), /* GNU */
310 PARSE_OPTION ("regextype", regextype
), /* GNU */
311 PARSE_TEST ("samefile", samefile
), /* GNU */
313 PARSE_OPTION ("show-control-chars", show_control_chars
), /* GNU, 4.3.0+ */
315 PARSE_TEST ("size", size
), /* POSIX */
316 PARSE_TEST ("type", type
), /* POSIX */
317 PARSE_TEST ("uid", uid
), /* GNU */
318 PARSE_TEST ("used", used
), /* GNU */
319 PARSE_TEST ("user", user
), /* POSIX */
320 PARSE_OPTION ("warn", warn
), /* GNU */
321 PARSE_TEST_NP ("wholename", wholename
), /* GNU, replaced -path, but anyway -path will soon be in POSIX */
322 {ARG_TEST
, "writable", parse_accesscheck
, pred_writable
}, /* GNU, 4.3.0+ */
323 PARSE_OPTION ("xdev", xdev
), /* POSIX */
324 PARSE_TEST ("xtype", xtype
), /* GNU */
325 #ifdef UNIMPLEMENTED_UNIX
326 /* It's pretty ugly for find to know about archive formats.
327 Plus what it could do with cpio archives is very limited.
328 Better to leave it out. */
329 PARSE(ARG_UNIMPLEMENTED
, "cpio", cpio
), /* Unix */
331 /* gnulib's stdbool.h might have made true and false into macros,
332 * so we can't leave named 'true' and 'false' tokens, so we have
333 * to expeant the relevant entries longhand.
335 {ARG_TEST
, "false", parse_false
, pred_false
}, /* GNU */
336 {ARG_TEST
, "true", parse_true
, pred_true
}, /* GNU */
337 {ARG_NOOP
, "noop", NULL
, pred_true
}, /* GNU, internal use only */
339 /* Various other cases that don't fit neatly into our macro scheme. */
340 {ARG_TEST
, "help", parse_help
, NULL
}, /* GNU */
341 {ARG_TEST
, "-help", parse_help
, NULL
}, /* GNU */
342 {ARG_TEST
, "version", parse_version
, NULL
}, /* GNU */
343 {ARG_TEST
, "-version", parse_version
, NULL
}, /* GNU */
348 static const char *first_nonoption_arg
= NULL
;
349 static const struct parser_table
*noop
= NULL
;
353 check_option_combinations(const struct predicate
*p
)
355 enum { seen_delete
=1u, seen_prune
=2u };
356 unsigned int predicates
= 0u;
360 if (p
->pred_func
== pred_delete
)
361 predicates
|= seen_delete
;
362 else if (p
->pred_func
== pred_prune
)
363 predicates
|= seen_prune
;
367 if ((predicates
& seen_prune
) && (predicates
& seen_delete
))
369 /* The user specified both -delete and -prune. One might test
370 * this by first doing
371 * find dirs .... -prune ..... -print
372 * to fnd out what's going to get deleted, and then switch to
373 * find dirs .... -prune ..... -delete
374 * once we are happy. Unfortunately, the -delete action also
375 * implicitly turns on -depth, which will affect the behaviour
376 * of -prune (in fact, it makes it a no-op). In this case we
377 * would like to prevent unfortunate accidents, so we require
378 * the user to have explicitly used -depth.
380 * We only get away with this because the -delete predicate is not
381 * in POSIX. If it was, we couldn't issue a fatal error here.
383 if (!options
.explicit_depth
)
385 /* This fixes Savannah bug #20865. */
386 error (1, 0, _("The -delete action atomatically turns on -depth, "
387 "but -prune does nothing when -depth is in effect. "
388 "If you want to carry on anyway, just explicitly use "
389 "the -depth option."));
395 static const struct parser_table
*
401 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
403 if (ARG_NOOP
==parse_table
[i
].type
)
405 noop
= &(parse_table
[i
]);
414 get_stat_Ytime(const struct stat
*p
,
416 struct timespec
*ret
)
421 *ret
= get_stat_atime(p
);
424 *ret
= get_stat_birthtime(p
);
425 return (ret
->tv_nsec
>= 0);
427 *ret
= get_stat_ctime(p
);
430 *ret
= get_stat_mtime(p
);
439 set_follow_state(enum SymlinkOption opt
)
441 if (options
.debug_options
& DebugStat
)
443 /* For DebugStat, the choice is made at runtime within debug_stat()
444 * by checking the contents of the symlink_handling variable.
446 options
.xstat
= debug_stat
;
452 case SYMLINK_ALWAYS_DEREF
: /* -L */
453 options
.xstat
= optionl_stat
;
454 options
.no_leaf_check
= true;
457 case SYMLINK_NEVER_DEREF
: /* -P (default) */
458 options
.xstat
= optionp_stat
;
459 /* Can't turn no_leaf_check off because the user might have specified
464 case SYMLINK_DEREF_ARGSONLY
: /* -H */
465 options
.xstat
= optionh_stat
;
466 options
.no_leaf_check
= true;
469 options
.symlink_handling
= opt
;
474 parse_begin_user_args (char **args
, int argno
,
475 const struct predicate
*last
,
476 const struct predicate
*predicates
)
482 first_nonoption_arg
= NULL
;
486 parse_end_user_args (char **args
, int argno
,
487 const struct predicate
*last
,
488 const struct predicate
*predicates
)
498 /* Check that it is legal to fid the given primary in its
499 * position and return it.
501 const struct parser_table
*
502 found_parser(const char *original_arg
, const struct parser_table
*entry
)
504 /* If this is an option, but we have already had a
505 * non-option argument, the user may be under the
506 * impression that the behaviour of the option
507 * argument is conditional on some preceding
508 * tests. This might typically be the case with,
509 * for example, -maxdepth.
511 * The options -daystart and -follow are exempt
512 * from this treatment, since their positioning
513 * in the command line does have an effect on
514 * subsequent tests but not previous ones. That
515 * might be intentional on the part of the user.
517 if (entry
->type
!= ARG_POSITIONAL_OPTION
)
519 /* Something other than -follow/-daystart.
520 * If this is an option, check if it followed
521 * a non-option and if so, issue a warning.
523 if (entry
->type
== ARG_OPTION
)
525 if ((first_nonoption_arg
!= NULL
)
526 && options
.warnings
)
528 /* option which follows a non-option */
530 _("warning: you have specified the %1$s "
531 "option after a non-option argument %2$s, "
532 "but options are not positional (%3$s affects "
533 "tests specified before it as well as those "
534 "specified after it). Please specify options "
535 "before other arguments.\n"),
543 /* Not an option or a positional option,
544 * so remember we've seen it in order to
545 * use it in a possible future warning message.
547 if (first_nonoption_arg
== NULL
)
549 first_nonoption_arg
= original_arg
;
558 /* Return a pointer to the parser function to invoke for predicate
560 Return NULL if SEARCH_NAME is not a valid predicate name. */
562 const struct parser_table
*
563 find_parser (char *search_name
)
566 const char *original_arg
= search_name
;
568 /* Ugh. Special case -newerXY. */
569 if (0 == strncmp("-newer", search_name
, 6)
570 && (8 == strlen(search_name
)))
572 return found_parser(original_arg
, &parse_entry_newerXY
);
575 if (*search_name
== '-')
578 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
580 if (strcmp (parse_table
[i
].parser_name
, search_name
) == 0)
582 return found_parser(original_arg
, &parse_table
[i
]);
589 estimate_file_age_success_rate(float num_days
)
593 /* Assume 1% of files have timestamps in the future */
596 else if (num_days
< 1)
598 /* Assume 30% of files have timestamps today */
601 else if (num_days
> 100)
603 /* Assume 30% of files are very old */
608 /* Assume 39% of files are between 1 and 100 days old. */
614 estimate_timestamp_success_rate(time_t when
)
616 int num_days
= (options
.cur_day_start
- when
) / 86400;
617 return estimate_file_age_success_rate(num_days
);
620 /* Collect an argument from the argument list, or
624 collect_arg(char **argv
, int *arg_ptr
, const char **collected_arg
)
626 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
628 *collected_arg
= NULL
;
633 *collected_arg
= argv
[*arg_ptr
];
640 collect_arg_stat_info(char **argv
, int *arg_ptr
, struct stat
*p
)
642 const char *filename
;
643 if (collect_arg(argv
, arg_ptr
, &filename
))
645 if (0 == (options
.xstat
)(filename
, p
))
651 fatal_file_error(filename
);
660 /* The parsers are responsible to continue scanning ARGV for
661 their arguments. Each parser knows what is and isn't
664 ARGV is the argument array.
665 *ARG_PTR is the index to start at in ARGV,
666 updated to point beyond the last element consumed.
668 The predicate structure is updated with the new information. */
672 parse_and (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
674 struct predicate
*our_pred
;
679 our_pred
= get_new_pred (entry
);
680 our_pred
->pred_func
= pred_and
;
681 our_pred
->p_type
= BI_OP
;
682 our_pred
->p_prec
= AND_PREC
;
683 our_pred
->need_stat
= our_pred
->need_type
= false;
688 parse_anewer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
690 struct stat stat_newer
;
692 set_stat_placeholders(&stat_newer
);
693 if (collect_arg_stat_info(argv
, arg_ptr
, &stat_newer
))
695 struct predicate
*our_pred
= insert_primary (entry
);
696 our_pred
->args
.reftime
.xval
= XVAL_ATIME
;
697 our_pred
->args
.reftime
.ts
= get_stat_mtime(&stat_newer
);
698 our_pred
->args
.reftime
.kind
= COMP_GT
;
699 our_pred
->est_success_rate
= estimate_timestamp_success_rate(stat_newer
.st_mtime
);
706 parse_closeparen (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
708 struct predicate
*our_pred
;
713 our_pred
= get_new_pred (entry
);
714 our_pred
->pred_func
= pred_closeparen
;
715 our_pred
->p_type
= CLOSE_PAREN
;
716 our_pred
->p_prec
= NO_PREC
;
717 our_pred
->need_stat
= our_pred
->need_type
= false;
722 parse_cnewer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
724 struct stat stat_newer
;
726 set_stat_placeholders(&stat_newer
);
727 if (collect_arg_stat_info(argv
, arg_ptr
, &stat_newer
))
729 struct predicate
*our_pred
= insert_primary (entry
);
730 our_pred
->args
.reftime
.xval
= XVAL_CTIME
; /* like -newercm */
731 our_pred
->args
.reftime
.ts
= get_stat_mtime(&stat_newer
);
732 our_pred
->args
.reftime
.kind
= COMP_GT
;
733 our_pred
->est_success_rate
= estimate_timestamp_success_rate(stat_newer
.st_mtime
);
740 parse_comma (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
742 struct predicate
*our_pred
;
747 our_pred
= get_new_pred (entry
);
748 our_pred
->pred_func
= pred_comma
;
749 our_pred
->p_type
= BI_OP
;
750 our_pred
->p_prec
= COMMA_PREC
;
751 our_pred
->need_stat
= our_pred
->need_type
= false;
752 our_pred
->est_success_rate
= 1.0f
;
757 parse_daystart (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
765 if (options
.full_days
== false)
767 options
.cur_day_start
+= DAYSECS
;
768 local
= localtime (&options
.cur_day_start
);
769 options
.cur_day_start
-= (local
770 ? (local
->tm_sec
+ local
->tm_min
* 60
771 + local
->tm_hour
* 3600)
772 : options
.cur_day_start
% DAYSECS
);
773 options
.full_days
= true;
779 parse_delete (const struct parser_table
* entry
, char *argv
[], int *arg_ptr
)
781 struct predicate
*our_pred
;
785 our_pred
= insert_primary (entry
);
786 our_pred
->side_effects
= our_pred
->no_default_print
= true;
787 /* -delete implies -depth */
788 options
.do_dir_first
= false;
790 /* We do not need stat information because we check for the case
791 * (errno==EISDIR) in pred_delete.
793 our_pred
->need_stat
= our_pred
->need_type
= false;
795 our_pred
->est_success_rate
= 1.0f
;
800 parse_depth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
805 options
.do_dir_first
= false;
806 options
.explicit_depth
= true;
807 return parse_noop(entry
, argv
, arg_ptr
);
811 parse_d (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
813 if (options
.warnings
)
816 _("warning: the -d option is deprecated; please use "
817 "-depth instead, because the latter is a "
818 "POSIX-compliant feature."));
820 return parse_depth(entry
, argv
, arg_ptr
);
824 parse_empty (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
826 struct predicate
*our_pred
;
830 our_pred
= insert_primary (entry
);
831 our_pred
->est_success_rate
= 0.01f
; /* assume 1% of files are empty. */
836 parse_exec (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
838 return insert_exec_ok ("-exec", entry
, get_start_dirfd(), argv
, arg_ptr
);
842 parse_execdir (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
844 return insert_exec_ok ("-execdir", entry
, -1, argv
, arg_ptr
);
848 parse_false (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
850 struct predicate
*our_pred
;
855 our_pred
= insert_primary (entry
);
856 our_pred
->need_stat
= our_pred
->need_type
= false;
857 our_pred
->side_effects
= our_pred
->no_default_print
= false;
858 our_pred
->est_success_rate
= 0.0f
;
863 insert_fls (const struct parser_table
* entry
, const char *filename
)
865 struct predicate
*our_pred
= insert_primary (entry
);
867 open_output_file (filename
, &our_pred
->args
.printf_vec
);
869 open_stdout (&our_pred
->args
.printf_vec
);
870 our_pred
->side_effects
= our_pred
->no_default_print
= true;
871 our_pred
->est_success_rate
= 1.0f
;
877 parse_fls (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
879 const char *filename
;
880 return collect_arg(argv
, arg_ptr
, &filename
)
881 && insert_fls(entry
, filename
);
885 parse_follow (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
887 set_follow_state(SYMLINK_ALWAYS_DEREF
);
888 return parse_noop(entry
, argv
, arg_ptr
);
892 parse_fprint (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
894 struct predicate
*our_pred
;
895 const char *filename
;
896 if (collect_arg(argv
, arg_ptr
, &filename
))
898 our_pred
= insert_primary (entry
);
899 open_output_file (filename
, &our_pred
->args
.printf_vec
);
900 our_pred
->side_effects
= our_pred
->no_default_print
= true;
901 our_pred
->need_stat
= our_pred
->need_type
= false;
902 our_pred
->est_success_rate
= 1.0f
;
912 insert_fprint(const struct parser_table
* entry
, const char *filename
)
914 struct predicate
*our_pred
= insert_primary (entry
);
916 open_output_file (filename
, &our_pred
->args
.printf_vec
);
918 open_stdout (&our_pred
->args
.printf_vec
);
919 our_pred
->side_effects
= our_pred
->no_default_print
= true;
920 our_pred
->need_stat
= our_pred
->need_type
= false;
921 our_pred
->est_success_rate
= 1.0f
;
927 parse_fprint0 (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
929 const char *filename
;
930 if (collect_arg(argv
, arg_ptr
, &filename
))
931 return insert_fprint(entry
, filename
);
936 static float estimate_fstype_success_rate(const char *fsname
)
938 struct stat dir_stat
;
939 const char *dir
= "/";
940 if (0 == stat(dir
, &dir_stat
))
942 const char *fstype
= filesystem_type(&dir_stat
, dir
);
943 /* Assume most files are on the same file system type as the root fs. */
944 if (0 == strcmp(fsname
, fstype
))
954 parse_fstype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
956 const char *typename
;
957 if (collect_arg(argv
, arg_ptr
, &typename
))
959 struct predicate
*our_pred
= insert_primary (entry
);
960 our_pred
->args
.str
= typename
;
962 /* This is an expensive operation, so although there are
963 * circumstances where it is selective, we ignore this fact
964 * because we probably don't want to promote this test to the
967 our_pred
->est_success_rate
= estimate_fstype_success_rate(typename
);
977 parse_gid (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
979 struct predicate
*p
= insert_num (argv
, arg_ptr
, entry
);
982 p
->est_success_rate
= (p
->args
.numinfo
.l_val
< 100) ? 0.99 : 0.2;
993 safe_atoi (const char *s
)
999 lval
= strtol(s
, &end
, 10);
1000 if ( (LONG_MAX
== lval
) || (LONG_MIN
== lval
) )
1002 /* max/min possible value, or an error. */
1003 if (errno
== ERANGE
)
1005 /* too big, or too small. */
1006 error(1, errno
, "%s", s
);
1010 /* not a valid number */
1011 error(1, errno
, "%s", s
);
1013 /* Otherwise, we do a range chack against INT_MAX and INT_MIN
1018 if (lval
> INT_MAX
|| lval
< INT_MIN
)
1020 /* The number was in range for long, but not int. */
1022 error(1, errno
, "%s", s
);
1026 error(1, errno
, "Unexpected suffix %s on %s",
1027 quotearg_n_style(0, options
.err_quoting_style
, end
),
1028 quotearg_n_style(1, options
.err_quoting_style
, s
));
1032 error(1, errno
, "Expected an integer: %s",
1033 quotearg_n_style(0, options
.err_quoting_style
, s
));
1040 parse_group (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1042 const char *groupname
;
1044 if (collect_arg(argv
, arg_ptr
, &groupname
))
1047 struct predicate
*our_pred
;
1048 struct group
*cur_gr
= getgrnam(groupname
);
1052 gid
= cur_gr
->gr_gid
;
1056 const int gid_len
= strspn (groupname
, "0123456789");
1059 if (groupname
[gid_len
] == 0)
1061 gid
= safe_atoi (groupname
);
1065 /* XXX: no test in test suite for this */
1066 error(1, 0, _("%1$s is not the name of an existing group and"
1067 " it does not look like a numeric group ID "
1068 "because it has the unexpected suffix %2$s"),
1069 quotearg_n_style(0, options
.err_quoting_style
, groupname
),
1070 quotearg_n_style(1, options
.err_quoting_style
, groupname
+gid_len
));
1078 /* XXX: no test in test suite for this */
1079 error(1, 0, _("%s is not the name of an existing group"),
1080 quotearg_n_style(0, options
.err_quoting_style
, groupname
));
1084 error(1, 0, _("argument to -group is empty, but should be a group name"));
1089 our_pred
= insert_primary (entry
);
1090 our_pred
->args
.gid
= gid
;
1091 our_pred
->est_success_rate
= (our_pred
->args
.numinfo
.l_val
< 100) ? 0.99 : 0.2;
1098 parse_help (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1104 usage(stdout
, 0, NULL
);
1106 default path is the current directory; default expression is -print\n\
1107 expression may consist of: operators, options, tests, and actions:\n"));
1109 operators (decreasing precedence; -and is implicit where no others are given):\n\
1110 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
1111 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
1113 positional options (always true): -daystart -follow -regextype\n\n\
1114 normal options (always true, specified before other expressions):\n\
1115 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
1116 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
1118 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
1119 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
1120 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
1121 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
1123 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
1124 -readable -writable -executable\n\
1125 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
1126 -used N -user NAME -xtype [bcdpfls]\n"));
1128 actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
1129 -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
1130 -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
1131 -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
1133 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
1134 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
1135 email to <bug-findutils@gnu.org>."));
1140 estimate_pattern_match_rate(const char *pattern
, int is_regex
)
1142 if (strpbrk(pattern
, "*?[") || (is_regex
&& strpbrk(pattern
, ".")))
1144 /* A wildcard; assume the pattern matches most files. */
1154 parse_ilname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1157 if (collect_arg(argv
, arg_ptr
, &name
))
1159 struct predicate
*our_pred
= insert_primary (entry
);
1160 our_pred
->args
.str
= name
;
1161 /* Use the generic glob pattern estimator to figure out how many
1162 * links will match, but bear in mind that most files won't be links.
1164 our_pred
->est_success_rate
= 0.1 * estimate_pattern_match_rate(name
, 0);
1174 /* sanity check the fnmatch() function to make sure that case folding
1175 * is supported (as opposed to just having the flag ignored).
1178 fnmatch_sanitycheck(void)
1180 static boolean checked
= false;
1183 if (0 != fnmatch("foo", "foo", 0)
1184 || 0 == fnmatch("Foo", "foo", 0)
1185 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD
))
1187 error (1, 0, _("sanity check of the fnmatch() library function failed."));
1197 check_name_arg(const char *pred
, const char *arg
)
1199 if (options
.warnings
&& strchr(arg
, '/'))
1201 error(0, 0,_("warning: Unix filenames usually don't contain slashes "
1202 "(though pathnames do). That means that '%s %s' will "
1203 "probably evaluate to false all the time on this system. "
1204 "You might find the '-wholename' test more useful, or "
1205 "perhaps '-samefile'. Alternatively, if you are using "
1206 "GNU grep, you could "
1207 "use 'find ... -print0 | grep -FzZ %s'."),
1209 safely_quote_err_filename(0, arg
),
1210 safely_quote_err_filename(1, arg
));
1212 return true; /* allow it anyway */
1218 parse_iname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1221 fnmatch_sanitycheck();
1222 if (collect_arg(argv
, arg_ptr
, &name
))
1224 if (check_name_arg("-iname", name
))
1226 struct predicate
*our_pred
= insert_primary (entry
);
1227 our_pred
->need_stat
= our_pred
->need_type
= false;
1228 our_pred
->args
.str
= name
;
1229 our_pred
->est_success_rate
= estimate_pattern_match_rate(name
, 0);
1237 parse_inum (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1239 struct predicate
*p
= insert_num (argv
, arg_ptr
, entry
);
1242 /* inode number is exact match only, so very low proportions of
1245 p
->est_success_rate
= 1e-6;
1254 /* -ipath is deprecated (at RMS's request) in favour of
1255 * -iwholename. See the node "GNU Manuals" in standards.texi
1256 * for the rationale for this (basically, GNU prefers the use
1257 * of the phrase "file name" to "path name"
1260 parse_ipath (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1264 fnmatch_sanitycheck ();
1265 if (collect_arg (argv
, arg_ptr
, &name
))
1267 struct predicate
*our_pred
= insert_primary_withpred (entry
, pred_ipath
);
1268 our_pred
->need_stat
= our_pred
->need_type
= false;
1269 our_pred
->args
.str
= name
;
1270 our_pred
->est_success_rate
= estimate_pattern_match_rate (name
, 0);
1277 parse_iwholename (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1279 return parse_ipath (entry
, argv
, arg_ptr
);
1283 parse_iregex (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1285 return insert_regex (argv
, arg_ptr
, entry
, RE_ICASE
|options
.regex_options
);
1289 parse_links (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1291 struct predicate
*p
= insert_num (argv
, arg_ptr
, entry
);
1294 if (p
->args
.numinfo
.l_val
== 1)
1295 p
->est_success_rate
= 0.99;
1296 else if (p
->args
.numinfo
.l_val
== 2)
1297 p
->est_success_rate
= 0.01;
1299 p
->est_success_rate
= 1e-3;
1309 parse_lname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1312 fnmatch_sanitycheck();
1313 if (collect_arg(argv
, arg_ptr
, &name
))
1315 struct predicate
*our_pred
= insert_primary (entry
);
1316 our_pred
->args
.str
= name
;
1317 our_pred
->est_success_rate
= 0.1 * estimate_pattern_match_rate(name
, 0);
1324 parse_ls (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1328 return insert_fls(entry
, NULL
);
1332 insert_depthspec(const struct parser_table
* entry
, char **argv
, int *arg_ptr
,
1335 const char *depthstr
;
1337 const char *predicate
= argv
[(*arg_ptr
)-1];
1338 if (collect_arg(argv
, arg_ptr
, &depthstr
))
1340 depth_len
= strspn (depthstr
, "0123456789");
1341 if ((depth_len
> 0) && (depthstr
[depth_len
] == 0))
1343 (*limitptr
) = safe_atoi (depthstr
);
1346 return parse_noop(entry
, argv
, arg_ptr
);
1349 error(1, 0, _("Expected a positive decimal integer argument to %1$s, but got %2$s"),
1351 quotearg_n_style(0, options
.err_quoting_style
, depthstr
));
1354 /* missing argument */
1360 parse_maxdepth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1362 return insert_depthspec(entry
, argv
, arg_ptr
, &options
.maxdepth
);
1366 parse_mindepth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1368 return insert_depthspec(entry
, argv
, arg_ptr
, &options
.mindepth
);
1373 do_parse_xmin (const struct parser_table
* entry
,
1378 const char *minutes
;
1380 if (collect_arg(argv
, arg_ptr
, &minutes
))
1382 struct time_val tval
;
1384 if (get_relative_timestamp(minutes
, &tval
,
1385 options
.cur_day_start
+ DAYSECS
, 60,
1386 "arithmetic overflow while converting %s "
1387 "minutes to a number of seconds"))
1389 struct predicate
*our_pred
= insert_primary (entry
);
1390 our_pred
->args
.reftime
= tval
;
1391 our_pred
->est_success_rate
= estimate_timestamp_success_rate(tval
.ts
.tv_sec
);
1398 parse_amin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1400 return do_parse_xmin(entry
, argv
, arg_ptr
, XVAL_ATIME
);
1404 parse_cmin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1406 return do_parse_xmin(entry
, argv
, arg_ptr
, XVAL_CTIME
);
1411 parse_mmin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1413 return do_parse_xmin(entry
, argv
, arg_ptr
, XVAL_MTIME
);
1417 parse_name (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1420 if (collect_arg(argv
, arg_ptr
, &name
))
1422 fnmatch_sanitycheck();
1423 if (check_name_arg("-name", name
))
1425 struct predicate
*our_pred
= insert_primary (entry
);
1426 our_pred
->need_stat
= our_pred
->need_type
= false;
1427 our_pred
->args
.str
= name
;
1428 our_pred
->est_success_rate
= estimate_pattern_match_rate(name
, 0);
1436 parse_negate (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1438 struct predicate
*our_pred
;
1443 our_pred
= get_new_pred_chk_op (entry
);
1444 our_pred
->pred_func
= pred_negate
;
1445 our_pred
->p_type
= UNI_OP
;
1446 our_pred
->p_prec
= NEGATE_PREC
;
1447 our_pred
->need_stat
= our_pred
->need_type
= false;
1452 parse_newer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1454 struct predicate
*our_pred
;
1455 struct stat stat_newer
;
1457 set_stat_placeholders(&stat_newer
);
1458 if (collect_arg_stat_info(argv
, arg_ptr
, &stat_newer
))
1460 our_pred
= insert_primary (entry
);
1461 our_pred
->args
.reftime
.ts
= get_stat_mtime(&stat_newer
);
1462 our_pred
->args
.reftime
.xval
= XVAL_MTIME
;
1463 our_pred
->args
.reftime
.kind
= COMP_GT
;
1464 our_pred
->est_success_rate
= estimate_timestamp_success_rate(stat_newer
.st_mtime
);
1472 parse_newerXY (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1477 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1481 else if (8u != strlen(argv
[*arg_ptr
]))
1488 const char validchars
[] = "aBcmt";
1490 assert (0 == strncmp("-newer", argv
[*arg_ptr
], 6));
1491 x
= argv
[*arg_ptr
][6];
1492 y
= argv
[*arg_ptr
][7];
1495 #if !defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) && !defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC)
1496 if ('B' == x
|| 'B' == y
)
1499 _("This system does not provide a way to find the birth time of a file."));
1504 /* -newertY (for any Y) is invalid. */
1506 || 0 == strchr(validchars
, x
)
1507 || 0 == strchr( validchars
, y
))
1513 struct predicate
*our_pred
;
1515 /* Because this item is ARG_SPECIAL_PARSE, we have to advance arg_ptr
1516 * past the test name (for most other tests, this is already done)
1520 our_pred
= insert_primary (entry
);
1526 our_pred
->args
.reftime
.xval
= XVAL_ATIME
;
1529 our_pred
->args
.reftime
.xval
= XVAL_BIRTHTIME
;
1532 our_pred
->args
.reftime
.xval
= XVAL_CTIME
;
1535 our_pred
->args
.reftime
.xval
= XVAL_MTIME
;
1538 assert (strchr(validchars
, x
));
1544 if (!get_date(&our_pred
->args
.reftime
.ts
,
1546 &options
.start_time
))
1549 _("I cannot figure out how to interpret %s as a date or time"),
1550 quotearg_n_style(0, options
.err_quoting_style
, argv
[*arg_ptr
]));
1555 struct stat stat_newer
;
1557 /* Stat the named file. */
1558 set_stat_placeholders(&stat_newer
);
1559 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
1560 fatal_file_error(argv
[*arg_ptr
]);
1562 if (!get_stat_Ytime(&stat_newer
, y
, &our_pred
->args
.reftime
.ts
))
1564 /* We cannot extract a timestamp from the struct stat. */
1565 error(1, 0, _("Cannot obtain birth time of file %s"),
1566 safely_quote_err_filename(0, argv
[*arg_ptr
]));
1569 our_pred
->args
.reftime
.kind
= COMP_GT
;
1570 our_pred
->est_success_rate
= estimate_timestamp_success_rate(our_pred
->args
.reftime
.ts
.tv_sec
);
1573 assert (our_pred
->pred_func
!= NULL
);
1574 assert (our_pred
->pred_func
== pred_newerXY
);
1575 assert (our_pred
->need_stat
);
1583 parse_noleaf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1585 options
.no_leaf_check
= true;
1586 return parse_noop(entry
, argv
, arg_ptr
);
1590 /* Arbitrary amount by which to increase size
1591 of `uid_unused' and `gid_unused'. */
1592 #define ALLOC_STEP 2048
1594 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1595 char *uid_unused
= NULL
;
1597 /* Number of elements in `uid_unused'. */
1598 unsigned uid_allocated
;
1600 /* Similar for GIDs and group entries. */
1601 char *gid_unused
= NULL
;
1602 unsigned gid_allocated
;
1606 parse_nogroup (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1608 struct predicate
*our_pred
;
1613 our_pred
= insert_primary (entry
);
1614 our_pred
->est_success_rate
= 1e-4;
1616 if (gid_unused
== NULL
)
1620 gid_allocated
= ALLOC_STEP
;
1621 gid_unused
= xmalloc (gid_allocated
);
1622 memset (gid_unused
, 1, gid_allocated
);
1624 while ((gr
= getgrent ()) != NULL
)
1626 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
1628 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
1629 gid_unused
= xrealloc (gid_unused
, new_allocated
);
1630 memset (gid_unused
+ gid_allocated
, 1,
1631 new_allocated
- gid_allocated
);
1632 gid_allocated
= new_allocated
;
1634 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
1643 parse_nouser (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1645 struct predicate
*our_pred
;
1650 our_pred
= insert_primary (entry
);
1651 our_pred
->est_success_rate
= 1e-3;
1653 if (uid_unused
== NULL
)
1657 uid_allocated
= ALLOC_STEP
;
1658 uid_unused
= xmalloc (uid_allocated
);
1659 memset (uid_unused
, 1, uid_allocated
);
1661 while ((pw
= getpwent ()) != NULL
)
1663 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
1665 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
1666 uid_unused
= xrealloc (uid_unused
, new_allocated
);
1667 memset (uid_unused
+ uid_allocated
, 1,
1668 new_allocated
- uid_allocated
);
1669 uid_allocated
= new_allocated
;
1671 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
1680 parse_nowarn (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1682 options
.warnings
= false;
1683 return parse_noop(entry
, argv
, arg_ptr
);
1687 parse_ok (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1689 return insert_exec_ok ("-ok", entry
, get_start_dirfd(), argv
, arg_ptr
);
1693 parse_okdir (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1695 return insert_exec_ok ("-okdir", entry
, -1, argv
, arg_ptr
);
1699 parse_openparen (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1701 struct predicate
*our_pred
;
1706 our_pred
= get_new_pred_chk_op (entry
);
1707 our_pred
->pred_func
= pred_openparen
;
1708 our_pred
->p_type
= OPEN_PAREN
;
1709 our_pred
->p_prec
= NO_PREC
;
1710 our_pred
->need_stat
= our_pred
->need_type
= false;
1715 parse_or (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1717 struct predicate
*our_pred
;
1722 our_pred
= get_new_pred (entry
);
1723 our_pred
->pred_func
= pred_or
;
1724 our_pred
->p_type
= BI_OP
;
1725 our_pred
->p_prec
= OR_PREC
;
1726 our_pred
->need_stat
= our_pred
->need_type
= false;
1730 /* For some time, -path was deprecated (at RMS's request) in favour of
1731 * -iwholename. See the node "GNU Manuals" in standards.texi for the
1732 * rationale for this (basically, GNU prefers the use of the phrase
1733 * "file name" to "path name".
1735 * We do not issue a warning that this usage is deprecated
1737 * (a) HPUX find supports this predicate also and
1738 * (b) it will soon be in POSIX anyway.
1741 parse_path (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1744 if (collect_arg(argv
, arg_ptr
, &name
))
1746 struct predicate
*our_pred
= insert_primary_withpred (entry
, pred_path
);
1747 our_pred
->need_stat
= our_pred
->need_type
= false;
1748 our_pred
->args
.str
= name
;
1749 our_pred
->est_success_rate
= estimate_pattern_match_rate (name
, 0);
1756 parse_wholename (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1758 return parse_path (entry
, argv
, arg_ptr
);
1762 non_posix_mode(const char *mode
)
1764 if (options
.posixly_correct
)
1766 error (1, 0, _("Mode %s is not valid when POSIXLY_CORRECT is on."),
1767 quotearg_n_style(0, options
.err_quoting_style
, mode
));
1773 parse_perm (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1778 boolean havekind
= false;
1779 enum permissions_type kind
= PERM_EXACT
;
1780 struct mode_change
*change
= NULL
;
1781 struct predicate
*our_pred
;
1782 const char *perm_expr
;
1784 if (!collect_arg(argv
, arg_ptr
, &perm_expr
))
1787 switch (perm_expr
[0])
1791 kind
= PERM_AT_LEAST
;
1797 change
= mode_compile (perm_expr
);
1800 /* Most likely the caller is an old script that is still
1801 * using the obsolete GNU syntax '-perm +MODE'. This old
1802 * syntax was withdrawn in favor of '-perm /MODE' because
1803 * it is incompatible with POSIX in some cases, but we
1804 * still support uses of it that are not incompatible with
1807 * Example: POSIXLY_CORRECT=y find -perm +a+x
1809 non_posix_mode(perm_expr
);
1811 /* support the previous behaviour. */
1818 /* This is a POSIX-compatible usage */
1826 case '/': /* GNU extension */
1827 non_posix_mode(perm_expr
);
1835 /* For example, '-perm 0644', which is valid and matches
1836 * only files whose mode is exactly 0644.
1847 change
= mode_compile (perm_expr
+ mode_start
);
1849 error (1, 0, _("invalid mode %s"
1850 /* TRANSLATORS: the argument is a
1851 * file permission string like 'u=rw,go='
1853 quotearg_n_style(0, options
.err_quoting_style
, perm_expr
));
1855 perm_val
[0] = mode_adjust (0, false, 0, change
, NULL
);
1856 perm_val
[1] = mode_adjust (0, true, 0, change
, NULL
);
1859 if (('/' == perm_expr
[0]) && (0 == perm_val
[0]) && (0 == perm_val
[1]))
1861 /* The meaning of -perm /000 will change in the future. It
1862 * currently matches no files, but like -perm -000 it should
1865 * Starting in 2005, we used to issue a warning message
1866 * informing the user that the behaviour would change in the
1867 * future. We have now changed the behaviour and issue a
1868 * warning message that the behaviour recently changed.
1871 _("warning: you have specified a mode pattern %s (which is "
1872 "equivalent to /000). The meaning of -perm /000 has now been "
1873 "changed to be consistent with -perm -000; that is, while it "
1874 "used to match no files, it now matches all files."),
1877 kind
= PERM_AT_LEAST
;
1880 /* The "magic" number below is just the fraction of files on my
1881 * own system that "-type l -xtype l" fails for (i.e. unbroken symlinks).
1882 * Actual totals are 1472 and 1073833.
1884 rate
= 0.9986; /* probably matches anything but a broken symlink */
1887 our_pred
= insert_primary (entry
);
1888 our_pred
->est_success_rate
= rate
;
1891 our_pred
->args
.perm
.kind
= kind
;
1896 switch (perm_expr
[0])
1899 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
1902 our_pred
->args
.perm
.kind
= PERM_ANY
;
1905 our_pred
->args
.perm
.kind
= PERM_EXACT
;
1909 memcpy (our_pred
->args
.perm
.val
, perm_val
, sizeof perm_val
);
1914 parse_print (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1916 struct predicate
*our_pred
;
1921 our_pred
= insert_primary (entry
);
1922 /* -print has the side effect of printing. This prevents us
1923 from doing undesired multiple printing when the user has
1924 already specified -print. */
1925 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1926 our_pred
->need_stat
= our_pred
->need_type
= false;
1927 open_stdout(&our_pred
->args
.printf_vec
);
1932 parse_print0 (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1934 return insert_fprint(entry
, NULL
);
1938 parse_printf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1941 if (collect_arg(argv
, arg_ptr
, &format
))
1943 struct format_val fmt
;
1945 return insert_fprintf (&fmt
, entry
, pred_fprintf
, format
);
1951 parse_fprintf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1953 const char *format
, *filename
;
1954 if (collect_arg(argv
, arg_ptr
, &filename
))
1956 if (collect_arg(argv
, arg_ptr
, &format
))
1958 struct format_val fmt
;
1959 open_output_file (filename
, &fmt
);
1960 return insert_fprintf (&fmt
, entry
, pred_fprintf
, format
);
1967 parse_prune (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1969 struct predicate
*our_pred
;
1974 our_pred
= insert_primary (entry
);
1975 our_pred
->need_stat
= our_pred
->need_type
= false;
1976 /* -prune has a side effect that it does not descend into
1977 the current directory. */
1978 our_pred
->side_effects
= true;
1979 our_pred
->no_default_print
= false;
1984 parse_quit (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1986 struct predicate
*our_pred
= insert_primary (entry
);
1989 our_pred
->need_stat
= our_pred
->need_type
= false;
1990 our_pred
->side_effects
= true; /* Exiting is a side effect... */
1991 our_pred
->no_default_print
= false; /* Don't inhibit the default print, though. */
1992 our_pred
->est_success_rate
= 1.0f
;
1998 parse_regextype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2000 const char *type_name
;
2001 if (collect_arg(argv
, arg_ptr
, &type_name
))
2003 /* collect the regex type name */
2004 options
.regex_options
= get_regex_type(type_name
);
2005 return parse_noop(entry
, argv
, arg_ptr
);
2012 parse_regex (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2014 return insert_regex (argv
, arg_ptr
, entry
, options
.regex_options
);
2018 insert_regex (char **argv
,
2020 const struct parser_table
*entry
,
2024 if (collect_arg(argv
, arg_ptr
, &rx
))
2026 struct re_pattern_buffer
*re
;
2027 const char *error_message
;
2028 struct predicate
*our_pred
= insert_primary_withpred (entry
, pred_regex
);
2029 our_pred
->need_stat
= our_pred
->need_type
= false;
2030 re
= xmalloc (sizeof (struct re_pattern_buffer
));
2031 our_pred
->args
.regex
= re
;
2032 re
->allocated
= 100;
2033 re
->buffer
= xmalloc (re
->allocated
);
2036 re_set_syntax(regex_options
);
2037 re
->syntax
= regex_options
;
2038 re
->translate
= NULL
;
2040 error_message
= re_compile_pattern (rx
, strlen(rx
), re
);
2042 error (1, 0, "%s", error_message
);
2043 our_pred
->est_success_rate
= estimate_pattern_match_rate(rx
, 1);
2050 parse_size (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2052 struct predicate
*our_pred
;
2055 enum comparison_type c_type
;
2060 /* XXX: cannot (yet) convert to ue collect_arg() as this
2061 * function modifies the args in-place.
2063 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2066 len
= strlen (argv
[*arg_ptr
]);
2068 error (1, 0, _("invalid null argument to -size"));
2070 suffix
= argv
[*arg_ptr
][len
- 1];
2075 argv
[*arg_ptr
][len
- 1] = '\0';
2080 argv
[*arg_ptr
][len
- 1] = '\0';
2085 argv
[*arg_ptr
][len
- 1] = '\0';
2088 case 'M': /* Megabytes */
2089 blksize
= 1024*1024;
2090 argv
[*arg_ptr
][len
- 1] = '\0';
2093 case 'G': /* Gigabytes */
2094 blksize
= 1024*1024*1024;
2095 argv
[*arg_ptr
][len
- 1] = '\0';
2100 argv
[*arg_ptr
][len
- 1] = '\0';
2116 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
2118 /* TODO: accept fractional megabytes etc. ? */
2119 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
2122 _("Invalid argument `%s%c' to -size"),
2123 argv
[*arg_ptr
], (int)suffix
);
2126 our_pred
= insert_primary (entry
);
2127 our_pred
->args
.size
.kind
= c_type
;
2128 our_pred
->args
.size
.blocksize
= blksize
;
2129 our_pred
->args
.size
.size
= num
;
2130 our_pred
->need_stat
= true;
2131 our_pred
->need_type
= false;
2133 if (COMP_GT
== c_type
)
2134 our_pred
->est_success_rate
= (num
*blksize
> 20480) ? 0.1 : 0.9;
2135 else if (COMP_LT
== c_type
)
2136 our_pred
->est_success_rate
= (num
*blksize
> 20480) ? 0.9 : 0.1;
2138 our_pred
->est_success_rate
= 0.01;
2146 parse_samefile (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2148 /* General idea: stat the file, remember device and inode numbers.
2149 * If a candidate file matches those, it's the same file.
2151 struct predicate
*our_pred
;
2152 struct stat st
, fst
;
2155 set_stat_placeholders(&st
);
2156 if (!collect_arg_stat_info(argv
, arg_ptr
, &st
))
2159 set_stat_placeholders(&fst
);
2160 /* POSIX systems are free to re-use the inode number of a deleted
2161 * file. To ensure that we are not fooled by inode reuse, we hold
2162 * the file open if we can. This would prevent the system reusing
2165 fd
= -3; /* means, uninitialised */
2166 openflags
= O_RDONLY
;
2168 if (options
.symlink_handling
== SYMLINK_NEVER_DEREF
)
2170 if (options
.open_nofollow_available
)
2172 assert (O_NOFOLLOW
!= 0);
2173 openflags
|= O_NOFOLLOW
;
2174 fd
= -1; /* safe to open it. */
2178 if (S_ISLNK(st
.st_mode
))
2180 /* no way to ensure that a symlink will not be followed
2181 * by open(2), so fall back on using lstat(). Accept
2182 * the risk that the named file will be deleted and
2183 * replaced with another having the same inode.
2185 * Avoid opening the file.
2187 fd
= -2; /* Do not open it */
2192 /* Race condition here: the file might become a symlink here. */
2198 /* We want to dereference the symlink anyway */
2199 fd
= -1; /* safe to open it without O_NOFOLLOW */
2202 assert (fd
!= -3); /* check we made a decision */
2205 /* Race condition here. The file might become a
2206 * symbolic link in between out call to stat and
2209 fd
= open(argv
[*arg_ptr
], openflags
);
2213 /* We stat the file again here to prevent a race condition
2214 * between the first stat and the call to open(2).
2216 if (0 != fstat(fd
, &fst
))
2218 fatal_file_error(argv
[*arg_ptr
]);
2222 /* Worry about the race condition. If the file became a
2223 * symlink after our first stat and before our call to
2224 * open, fst may contain the stat information for the
2225 * destination of the link, not the link itself.
2227 if ((*options
.xstat
) (argv
[*arg_ptr
], &st
))
2228 fatal_file_error(argv
[*arg_ptr
]);
2230 if ((options
.symlink_handling
== SYMLINK_NEVER_DEREF
)
2231 && (!options
.open_nofollow_available
))
2233 if (S_ISLNK(st
.st_mode
))
2235 /* We lost the race. Leave the data in st. The
2236 * file descriptor points to the wrong thing.
2243 /* Several possibilities here:
2244 * 1. There was no race
2245 * 2. The file changed into a symlink after the stat and
2246 * before the open, and then back into a non-symlink
2247 * before the second stat.
2249 * In case (1) there is no problem. In case (2),
2250 * the stat() and fstat() calls will have returned
2251 * different data. O_NOFOLLOW was not available,
2252 * so the open() call may have followed a symlink
2253 * even if the -P option is in effect.
2255 if ((st
.st_dev
== fst
.st_dev
)
2256 && (st
.st_ino
== fst
.st_ino
))
2258 /* No race. No need to copy fst to st,
2259 * since they should be identical (modulo
2260 * differences in padding bytes).
2265 /* We lost the race. Leave the data in st. The
2266 * file descriptor points to the wrong thing.
2281 our_pred
= insert_primary (entry
);
2282 our_pred
->args
.samefileid
.ino
= st
.st_ino
;
2283 our_pred
->args
.samefileid
.dev
= st
.st_dev
;
2284 our_pred
->args
.samefileid
.fd
= fd
;
2285 our_pred
->need_type
= false;
2286 our_pred
->need_stat
= true;
2287 our_pred
->est_success_rate
= 0.01f
;
2292 /* This function is commented out partly because support for it is
2296 parse_show_control_chars (const struct parser_table
* entry
,
2301 const char *errmsg
= _("The -show-control-chars option takes "
2302 "a single argument which "
2303 "must be 'literal' or 'safe'");
2305 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2307 error (1, errno
, "%s", errmsg
);
2312 arg
= argv
[*arg_ptr
];
2314 if (0 == strcmp("literal", arg
))
2316 options
.literal_control_chars
= true;
2318 else if (0 == strcmp("safe", arg
))
2320 options
.literal_control_chars
= false;
2324 error (1, errno
, "%s", errmsg
);
2327 (*arg_ptr
)++; /* consume the argument. */
2335 parse_true (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2337 struct predicate
*our_pred
;
2342 our_pred
= insert_primary (entry
);
2343 our_pred
->need_stat
= our_pred
->need_type
= false;
2344 our_pred
->est_success_rate
= 1.0f
;
2349 parse_noop (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2352 return parse_true(get_noop(), argv
, arg_ptr
);
2356 parse_accesscheck (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2358 struct predicate
*our_pred
;
2361 our_pred
= insert_primary (entry
);
2362 our_pred
->need_stat
= our_pred
->need_type
= false;
2363 our_pred
->side_effects
= our_pred
->no_default_print
= false;
2364 if (pred_is(our_pred
, pred_executable
))
2365 our_pred
->est_success_rate
= 0.2;
2367 our_pred
->est_success_rate
= 0.9;
2372 parse_type (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2374 return insert_type (argv
, arg_ptr
, entry
, pred_type
);
2378 parse_uid (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2380 struct predicate
*p
= insert_num (argv
, arg_ptr
, entry
);
2383 p
->est_success_rate
= (p
->args
.numinfo
.l_val
< 100) ? 0.99 : 0.2;
2393 parse_used (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2395 struct predicate
*our_pred
;
2396 struct time_val tval
;
2397 const char *offset_str
;
2398 const char *errmsg
= "arithmetic overflow while converting %s days to a number of seconds";
2400 if (collect_arg(argv
, arg_ptr
, &offset_str
))
2402 /* The timespec is actually a delta value, so we use an origin of 0. */
2403 if (get_relative_timestamp(offset_str
, &tval
, 0, DAYSECS
, errmsg
))
2405 our_pred
= insert_primary (entry
);
2406 our_pred
->args
.reftime
= tval
;
2407 our_pred
->est_success_rate
= estimate_file_age_success_rate(tval
.ts
.tv_sec
/ DAYSECS
);
2412 error(1, 0, _("Invalid argument %s to -used"), offset_str
);
2418 return false; /* missing argument */
2423 parse_user (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2425 const char *username
;
2427 if (collect_arg(argv
, arg_ptr
, &username
))
2429 struct predicate
*our_pred
;
2431 struct passwd
*cur_pwd
= getpwnam(username
);
2433 if (cur_pwd
!= NULL
)
2435 uid
= cur_pwd
->pw_uid
;
2439 int uid_len
= strspn (username
, "0123456789");
2440 if (uid_len
&& (username
[uid_len
]==0))
2441 uid
= safe_atoi (username
);
2445 our_pred
= insert_primary (entry
);
2446 our_pred
->args
.uid
= uid
;
2447 our_pred
->est_success_rate
= (our_pred
->args
.uid
< 100) ? 0.99 : 0.2;
2454 parse_version (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2463 display_findutils_version("find");
2464 printf (_("Features enabled: "));
2467 printf("CACHE_IDS ");
2475 printf("DEBUG_STAT ");
2478 #if defined USE_STRUCT_DIRENT_D_TYPE && defined HAVE_STRUCT_DIRENT_D_TYPE
2482 #if defined O_NOFOLLOW
2483 printf("O_NOFOLLOW(%s) ",
2484 (options
.open_nofollow_available
? "enabled" : "disabled"));
2487 #if defined LEAF_OPTIMISATION
2488 printf("LEAF_OPTIMISATION ");
2493 if (is_fts_enabled(&flags
))
2499 if (flags
& FTS_CWDFD
)
2505 printf("FTS_CWDFD");
2511 printf("CBO(level=%d) ", (int)(options
.optimisation_level
));
2516 /* For the moment, leave this as English in case someone wants
2517 to parse these strings. */
2526 parse_xdev (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2528 options
.stay_on_filesystem
= true;
2529 return parse_noop(entry
, argv
, arg_ptr
);
2533 parse_ignore_race (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2535 options
.ignore_readdir_race
= true;
2536 return parse_noop(entry
, argv
, arg_ptr
);
2540 parse_noignore_race (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2542 options
.ignore_readdir_race
= false;
2543 return parse_noop(entry
, argv
, arg_ptr
);
2547 parse_warn (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2549 options
.warnings
= true;
2550 return parse_noop(entry
, argv
, arg_ptr
);
2554 parse_xtype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
2556 return insert_type (argv
, arg_ptr
, entry
, pred_xtype
);
2560 insert_type (char **argv
, int *arg_ptr
,
2561 const struct parser_table
*entry
,
2562 PRED_FUNC which_pred
)
2565 struct predicate
*our_pred
;
2567 const char *typeletter
;
2569 if (collect_arg(argv
, arg_ptr
, &typeletter
))
2571 if (strlen(typeletter
) != 1u)
2573 error(1, 0, _("Arguments to -type should contain only one letter"));
2577 switch (typeletter
[0])
2579 case 'b': /* block special */
2580 type_cell
= S_IFBLK
;
2583 case 'c': /* character special */
2584 type_cell
= S_IFCHR
;
2587 case 'd': /* directory */
2588 type_cell
= S_IFDIR
;
2591 case 'f': /* regular file */
2592 type_cell
= S_IFREG
;
2596 case 'l': /* symbolic link */
2597 type_cell
= S_IFLNK
;
2602 case 'p': /* pipe */
2603 type_cell
= S_IFIFO
;
2608 case 's': /* socket */
2609 type_cell
= S_IFSOCK
;
2614 case 'D': /* Solaris door */
2615 type_cell
= S_IFDOOR
;
2619 default: /* None of the above ... nuke 'em. */
2620 error(1, 0, _("Unknown argument to -type: %c"), (*typeletter
));
2623 our_pred
= insert_primary_withpred (entry
, which_pred
);
2624 our_pred
->est_success_rate
= rate
;
2626 /* Figure out if we will need to stat the file, because if we don't
2627 * need to follow symlinks, we can avoid a stat call by using
2628 * struct dirent.d_type.
2630 if (which_pred
== pred_xtype
)
2632 our_pred
->need_stat
= true;
2633 our_pred
->need_type
= false;
2637 our_pred
->need_stat
= false; /* struct dirent is enough */
2638 our_pred
->need_type
= true;
2640 our_pred
->args
.type
= type_cell
;
2647 /* Return true if the file accessed via FP is a terminal.
2650 stream_is_tty(FILE *fp
)
2652 int fd
= fileno(fp
);
2655 return false; /* not a valid stream */
2659 return isatty(fd
) ? true : false;
2667 /* XXX: do we need to pass FUNC to this function? */
2669 insert_fprintf (struct format_val
*vec
,
2670 const struct parser_table
*entry
, PRED_FUNC func
,
2671 const char *format_const
)
2673 char *format
= (char*)format_const
; /* XXX: casting away constness */
2674 register char *scan
; /* Current address in scanning `format'. */
2675 register char *scan2
; /* Address inside of element being scanned. */
2676 struct segment
**segmentp
; /* Address of current segment. */
2677 struct predicate
*our_pred
;
2679 our_pred
= insert_primary_withpred (entry
, func
);
2680 our_pred
->side_effects
= our_pred
->no_default_print
= true;
2681 our_pred
->args
.printf_vec
= *vec
;
2682 our_pred
->need_type
= false;
2683 our_pred
->need_stat
= false;
2684 our_pred
->p_cost
= NeedsNothing
;
2686 segmentp
= &our_pred
->args
.printf_vec
.segment
;
2689 for (scan
= format
; *scan
; scan
++)
2694 if (*scan2
>= '0' && *scan2
<= '7')
2698 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
2700 n
= 8 * n
+ *scan2
- '0';
2715 make_segment (segmentp
, format
, scan
- format
,
2718 if (our_pred
->need_stat
&& (our_pred
->p_cost
< NeedsStatInfo
))
2719 our_pred
->p_cost
= NeedsStatInfo
;
2737 /* *scan = '\\'; * it already is */
2741 _("warning: unrecognized escape `\\%c'"), *scan2
);
2746 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
2749 format
= scan2
+ 1; /* Move past the escape. */
2750 scan
= scan2
; /* Incremented immediately by `for'. */
2752 else if (*scan
== '%')
2756 /* Trailing %. We don't like those. */
2757 error (1, 0, _("error: %s at end of format string"), scan
);
2759 else if (scan
[1] == '%')
2761 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
2768 /* Scan past flags, width and precision, to verify kind. */
2769 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
2771 while (ISDIGIT (*scan2
))
2774 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
2776 if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2
))
2778 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
2779 KIND_FORMAT
, *scan2
, 0,
2784 else if (strchr ("ABCT", *scan2
) && scan2
[1])
2786 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
2787 KIND_FORMAT
, scan2
[0], scan2
[1],
2795 /* An unrecognized % escape. Print the char after the %. */
2796 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
2798 segmentp
= make_segment (segmentp
, format
, scan
- format
,
2808 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
, 0, 0,
2813 /* Create a new fprintf segment in *SEGMENT, with type KIND,
2814 from the text in FORMAT, which has length LEN.
2815 Return the address of the `next' pointer of the new segment. */
2817 static struct segment
**
2818 make_segment (struct segment
**segment
,
2823 char aux_format_char
,
2824 struct predicate
*pred
)
2826 enum EvaluationCost mycost
= NeedsNothing
;
2829 *segment
= xmalloc (sizeof (struct segment
));
2831 (*segment
)->segkind
= kind
;
2832 (*segment
)->format_char
[0] = format_char
;
2833 (*segment
)->format_char
[1] = aux_format_char
;
2834 (*segment
)->next
= NULL
;
2835 (*segment
)->text_len
= len
;
2837 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
2838 strncpy (fmt
, format
, len
);
2843 case KIND_PLAIN
: /* Plain text string, no % conversion. */
2844 case KIND_STOP
: /* Terminate argument, no newline. */
2845 assert (0 == format_char
);
2846 assert (0 == aux_format_char
);
2848 if (mycost
> pred
->p_cost
)
2849 pred
->p_cost
= NeedsNothing
;
2850 return &(*segment
)->next
;
2854 assert (kind
== KIND_FORMAT
);
2855 switch (format_char
)
2857 case 'l': /* object of symlink */
2858 pred
->need_stat
= true;
2859 mycost
= NeedsLinkName
;
2863 case 'y': /* file type */
2864 pred
->need_type
= true;
2869 case 'a': /* atime in `ctime' format */
2870 case 'A': /* atime in user-specified strftime format */
2871 case 'B': /* birth time in user-specified strftime format */
2872 case 'c': /* ctime in `ctime' format */
2873 case 'C': /* ctime in user-specified strftime format */
2874 case 'F': /* file system type */
2875 case 'g': /* group name */
2876 case 'i': /* inode number */
2877 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
2878 case 's': /* size in bytes */
2879 case 't': /* mtime in `ctime' format */
2880 case 'T': /* mtime in user-specified strftime format */
2881 case 'u': /* user name */
2882 pred
->need_stat
= true;
2883 mycost
= NeedsStatInfo
;
2887 case 'S': /* sparseness */
2888 pred
->need_stat
= true;
2889 mycost
= NeedsStatInfo
;
2893 case 'Y': /* symlink pointed file type */
2894 pred
->need_stat
= true;
2895 mycost
= NeedsType
; /* true for amortised effect */
2899 case 'f': /* basename of path */
2900 case 'h': /* leading directories part of path */
2901 case 'p': /* pathname */
2902 case 'P': /* pathname with ARGV element stripped */
2906 case 'H': /* ARGV element file was found under */
2910 /* Numeric items that one might expect to honour
2911 * #, 0, + flags but which do not.
2913 case 'G': /* GID number */
2914 case 'U': /* UID number */
2915 case 'b': /* size in 512-byte blocks (NOT birthtime in ctime fmt)*/
2916 case 'D': /* Filesystem device on which the file exits */
2917 case 'k': /* size in 1K blocks */
2918 case 'n': /* number of links */
2919 pred
->need_stat
= true;
2920 mycost
= NeedsStatInfo
;
2924 /* Numeric items that DO honour #, 0, + flags.
2926 case 'd': /* depth in search tree (0 = ARGV element) */
2930 case 'm': /* mode as octal number (perms only) */
2932 pred
->need_stat
= true;
2933 mycost
= NeedsStatInfo
;
2940 _("error: the format directive `%%%c' is reserved for future use"),
2947 if (mycost
> pred
->p_cost
)
2948 pred
->p_cost
= mycost
;
2949 return &(*segment
)->next
;
2953 check_path_safety(const char *action
, char **argv
)
2956 const char *path
= getenv("PATH");
2959 /* $PATH is not set. Assume the OS default is safe.
2960 * That may not be true on Windows, but I'm not aware
2961 * of a way to get Windows to avoid searching the
2962 * current directory anyway.
2969 s
= next_element(path
, 1);
2970 while ((s
= next_element ((char *) NULL
, 1)) != NULL
)
2972 if (0 == strcmp(s
, "."))
2974 error(1, 0, _("The current directory is included in the PATH "
2975 "environment variable, which is insecure in "
2976 "combination with the %s action of find. "
2977 "Please remove the current directory from your "
2978 "$PATH (that is, remove \".\" or leading or trailing "
2982 else if ('/' != s
[0])
2984 /* Relative paths are also dangerous in $PATH. */
2985 error(1, 0, _("The relative path %1$s is included in the PATH "
2986 "environment variable, which is insecure in "
2987 "combination with the %2$s action of find. "
2988 "Please remove that entry from $PATH"),
2989 safely_quote_err_filename(0, s
),
2996 /* handles both exec and ok predicate */
2998 new_insert_exec_ok (const char *action
,
2999 const struct parser_table
*entry
,
3004 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
3005 int i
; /* Index into cmd args */
3006 int saw_braces
; /* True if previous arg was '{}'. */
3007 boolean allow_plus
; /* True if + is a valid terminator */
3008 int brace_count
; /* Number of instances of {}. */
3009 PRED_FUNC func
= entry
->pred_func
;
3010 enum BC_INIT_STATUS bcstatus
;
3012 struct predicate
*our_pred
;
3013 struct exec_val
*execp
; /* Pointer for efficiency. */
3015 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
3018 our_pred
= insert_primary_withpred (entry
, func
);
3019 our_pred
->side_effects
= our_pred
->no_default_print
= true;
3020 our_pred
->need_type
= our_pred
->need_stat
= false;
3022 execp
= &our_pred
->args
.exec_vec
;
3024 if ((func
!= pred_okdir
) && (func
!= pred_ok
))
3027 execp
->close_stdin
= false;
3032 /* If find reads stdin (i.e. for -ok and similar), close stdin
3033 * in the child to prevent some script from consiming the output
3034 * intended for find.
3036 execp
->close_stdin
= true;
3040 if ((func
== pred_execdir
) || (func
== pred_okdir
))
3042 options
.ignore_readdir_race
= false;
3043 check_path_safety(action
, argv
);
3044 execp
->use_current_dir
= true;
3048 execp
->use_current_dir
= false;
3051 our_pred
->args
.exec_vec
.multiple
= 0;
3053 /* Count the number of args with path replacements, up until the ';'.
3054 * Also figure out if the command is terminated by ";" or by "+".
3057 for (end
= start
, saw_braces
=0, brace_count
=0;
3059 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
3062 /* For -exec and -execdir, "{} +" can terminate the command. */
3064 && argv
[end
][0] == '+' && argv
[end
][1] == 0
3067 our_pred
->args
.exec_vec
.multiple
= 1;
3072 if (mbsstr (argv
[end
], "{}"))
3077 if (0 == end
&& (func
== pred_execdir
|| func
== pred_okdir
))
3079 /* The POSIX standard says that {} replacement should
3080 * occur even in the utility name. This is insecure
3081 * since it means we will be executing a command whose
3082 * name is chosen according to whatever find finds in
3083 * the file system. That can be influenced by an
3084 * attacker. Hence for -execdir and -okdir this is not
3085 * allowed. We can specify this as those options are
3086 * not defined by POSIX.
3088 error(1, 0, _("You may not use {} within the utility name for "
3089 "-execdir and -okdir, because this is a potential "
3090 "security problem."));
3095 /* Fail if no command given or no semicolon found. */
3096 if ((end
== start
) || (argv
[end
] == NULL
))
3103 if (our_pred
->args
.exec_vec
.multiple
&& brace_count
> 1)
3107 if (func
== pred_execdir
)
3113 _("Only one instance of {} is supported with -exec%s ... +"),
3117 /* We use a switch statement here so that the compiler warns us when
3118 * we forget to handle a newly invented enum value.
3120 * Like xargs, we allow 2KiB of headroom for the launched utility to
3121 * export its own environment variables before calling something
3124 bcstatus
= bc_init_controlinfo(&execp
->ctl
, 2048u);
3127 case BC_INIT_ENV_TOO_BIG
:
3128 case BC_INIT_CANNOT_ACCOMODATE_HEADROOM
:
3130 _("The environment is too large for exec()."));
3133 /* Good news. Carry on. */
3136 bc_use_sensible_arg_max(&execp
->ctl
);
3139 execp
->ctl
.exec_callback
= launch
;
3141 if (our_pred
->args
.exec_vec
.multiple
)
3143 /* "+" terminator, so we can just append our arguments after the
3144 * command and initial arguments.
3146 execp
->replace_vec
= NULL
;
3147 execp
->ctl
.replace_pat
= NULL
;
3148 execp
->ctl
.rplen
= 0;
3149 execp
->ctl
.lines_per_exec
= 0; /* no limit */
3150 execp
->ctl
.args_per_exec
= 0; /* no limit */
3152 /* remember how many arguments there are */
3153 execp
->ctl
.initial_argc
= (end
-start
) - 1;
3155 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
3156 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
3158 /* Gather the initial arguments. Skip the {}. */
3159 for (i
=start
; i
<end
-1; ++i
)
3161 bc_push_arg(&execp
->ctl
, &execp
->state
,
3162 argv
[i
], strlen(argv
[i
])+1,
3169 /* Semicolon terminator - more than one {} is supported, so we
3170 * have to do brace-replacement.
3172 execp
->num_args
= end
- start
;
3174 execp
->ctl
.replace_pat
= "{}";
3175 execp
->ctl
.rplen
= strlen(execp
->ctl
.replace_pat
);
3176 execp
->ctl
.lines_per_exec
= 0; /* no limit */
3177 execp
->ctl
.args_per_exec
= 0; /* no limit */
3178 execp
->replace_vec
= xmalloc(sizeof(char*)*execp
->num_args
);
3181 /* execp->state = xmalloc(sizeof(*(execp->state))); */
3182 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
3184 /* Remember the (pre-replacement) arguments for later. */
3185 for (i
=0; i
<execp
->num_args
; ++i
)
3187 execp
->replace_vec
[i
] = argv
[i
+start
];
3191 if (argv
[end
] == NULL
)
3202 insert_exec_ok (const char *action
,
3203 const struct parser_table
*entry
,
3208 return new_insert_exec_ok(action
, entry
, dirfd
, argv
, arg_ptr
);
3213 /* Get a timestamp and comparison type.
3215 STR is the ASCII representation.
3216 Set *NUM_DAYS to the number of days/minutes/whatever, taken as being
3217 relative to ORIGIN (usually the current moment or midnight).
3218 Thus the sense of the comparison type appears to be reversed.
3219 Set *COMP_TYPE to the kind of comparison that is requested.
3220 Issue OVERFLOWMESSAGE if overflow occurs.
3221 Return true if all okay, false if input error.
3223 Used by -atime, -ctime and -mtime (parsers) to
3224 get the appropriate information for a time predicate processor. */
3227 get_relative_timestamp (const char *str
,
3228 struct time_val
*result
,
3230 double sec_per_unit
,
3231 const char *overflowmessage
)
3234 double offset
, seconds
, f
;
3236 if (get_comp_type(&str
, &result
->kind
))
3238 /* Invert the sense of the comparison */
3239 switch (result
->kind
)
3241 case COMP_LT
: result
->kind
= COMP_GT
; break;
3242 case COMP_GT
: result
->kind
= COMP_LT
; break;
3246 /* Convert the ASCII number into floating-point. */
3247 if (xstrtod(str
, NULL
, &offset
, strtod
))
3249 /* Separate the floating point number the user specified
3250 * (which is a number of days, or minutes, etc) into an
3251 * integral number of seconds (SECONDS) and a fraction (F).
3253 f
= modf(offset
* sec_per_unit
, &seconds
);
3255 result
->ts
.tv_sec
= origin
- seconds
;
3256 result
->ts
.tv_nsec
= fabs(f
* 1e9
);
3258 /* Check for overflow. */
3259 checkval
= (uintmax_t)origin
- seconds
;
3260 if (checkval
!= result
->ts
.tv_sec
)
3262 /* an overflow has occurred. */
3263 error (1, 0, overflowmessage
, str
);
3269 /* Conversion from ASCII to double failed. */
3279 /* Insert a time predicate based on the information in ENTRY.
3280 ARGV is a pointer to the argument array.
3281 ARG_PTR is a pointer to an index into the array, incremented if
3284 Return true if input is valid, false if not.
3286 A new predicate node is assigned, along with an argument node
3287 obtained with malloc.
3289 Used by -atime, -ctime, and -mtime parsers. */
3292 parse_time (const struct parser_table
* entry
, char *argv
[], int *arg_ptr
)
3294 struct predicate
*our_pred
;
3295 struct time_val tval
;
3296 enum comparison_type comp
;
3297 const char *timearg
, *orig_timearg
;
3298 const char *errmsg
= "arithmetic overflow while converting %s "
3299 "days to a number of seconds";
3302 if (!collect_arg(argv
, arg_ptr
, &timearg
))
3304 orig_timearg
= timearg
;
3306 /* Decide the origin by previewing the comparison type. */
3307 origin
= options
.cur_day_start
;
3309 if (get_comp_type(&timearg
, &comp
))
3311 /* Remember, we invert the sense of the comparison, so this tests
3312 * against COMP_LT instead of COMP_GT...
3314 if (COMP_LT
== comp
)
3316 uintmax_t expected
= origin
+ (DAYSECS
-1);
3317 origin
+= (DAYSECS
-1);
3318 if (origin
!= expected
)
3321 _("arithmetic overflow when trying to calculate the end of today"));
3325 /* We discard the value of comp here, as get_relative_timestamp
3326 * will set tval.kind. For that to work, we have to restore
3327 * timearg so that it points to the +/- prefix, if any. get_comp_type()
3328 * will have advanced timearg, so we restore it.
3330 timearg
= orig_timearg
;
3332 if (!get_relative_timestamp(timearg
, &tval
, origin
, DAYSECS
, errmsg
))
3335 our_pred
= insert_primary (entry
);
3336 our_pred
->args
.reftime
= tval
;
3337 our_pred
->est_success_rate
= estimate_timestamp_success_rate(tval
.ts
.tv_sec
);
3339 if (options
.debug_options
& DebugExpressionTree
)
3343 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
3344 fprintf (stderr
, " type: %s %s ",
3345 (tval
.kind
== COMP_GT
) ? "gt" :
3346 ((tval
.kind
== COMP_LT
) ? "lt" : ((tval
.kind
== COMP_EQ
) ? "eq" : "?")),
3347 (tval
.kind
== COMP_GT
) ? " >" :
3348 ((tval
.kind
== COMP_LT
) ? " <" : ((tval
.kind
== COMP_EQ
) ? ">=" : " ?")));
3349 t
= our_pred
->args
.reftime
.ts
.tv_sec
;
3350 fprintf (stderr
, "%ju %s",
3351 (uintmax_t) our_pred
->args
.reftime
.ts
.tv_sec
,
3353 if (tval
.kind
== COMP_EQ
)
3355 t
= our_pred
->args
.reftime
.ts
.tv_sec
+ DAYSECS
;
3356 fprintf (stderr
, " < %ju %s",
3357 (uintmax_t) t
, ctime (&t
));
3364 /* Get the comparison type prefix (if any) from a number argument.
3365 The prefix is at *STR.
3366 Set *COMP_TYPE to the kind of comparison that is requested.
3367 Advance *STR beyond any initial comparison prefix.
3369 Return true if all okay, false if input error. */
3371 get_comp_type(const char **str
, enum comparison_type
*comp_type
)
3376 *comp_type
= COMP_GT
;
3380 *comp_type
= COMP_LT
;
3384 *comp_type
= COMP_EQ
;
3394 /* Get a number with comparison information.
3395 The sense of the comparison information is 'normal'; that is,
3396 '+' looks for a count > than the number and '-' less than.
3398 STR is the ASCII representation of the number.
3399 Set *NUM to the number.
3400 Set *COMP_TYPE to the kind of comparison that is requested.
3402 Return true if all okay, false if input error. */
3405 get_num (const char *str
,
3407 enum comparison_type
*comp_type
)
3414 /* Figure out the comparison type if the caller accepts one. */
3417 if (!get_comp_type(&str
, comp_type
))
3421 return xstrtoumax (str
, &pend
, 10, num
, "") == LONGINT_OK
;
3424 /* Insert a number predicate.
3425 ARGV is a pointer to the argument array.
3426 *ARG_PTR is an index into ARGV, incremented if all went well.
3427 *PRED is the predicate processor to insert.
3429 Return true if input is valid, false if error.
3431 A new predicate node is assigned, along with an argument node
3432 obtained with malloc.
3434 Used by -inum and -links parsers. */
3436 static struct predicate
*
3437 insert_num (char **argv
, int *arg_ptr
, const struct parser_table
*entry
)
3441 if (collect_arg(argv
, arg_ptr
, &numstr
))
3444 enum comparison_type c_type
;
3446 if (get_num (numstr
, &num
, &c_type
))
3448 struct predicate
*our_pred
= insert_primary (entry
);
3449 our_pred
->args
.numinfo
.kind
= c_type
;
3450 our_pred
->args
.numinfo
.l_val
= num
;
3452 if (options
.debug_options
& DebugExpressionTree
)
3454 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
3455 fprintf (stderr
, " type: %s %s ",
3456 (c_type
== COMP_GT
) ? "gt" :
3457 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
3458 (c_type
== COMP_GT
) ? " >" :
3459 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
3460 fprintf (stderr
, "%ju\n", our_pred
->args
.numinfo
.l_val
);
3469 open_output_file (const char *path
, struct format_val
*p
)
3472 p
->quote_opts
= clone_quoting_options (NULL
);
3474 if (!strcmp (path
, "/dev/stderr"))
3477 p
->filename
= _("standard error");
3479 else if (!strcmp (path
, "/dev/stdout"))
3482 p
->filename
= _("standard output");
3486 p
->stream
= fopen_safer (path
, "w");
3489 if (p
->stream
== NULL
)
3491 fatal_file_error(path
);
3495 p
->dest_is_tty
= stream_is_tty(p
->stream
);
3499 open_stdout (struct format_val
*p
)
3501 open_output_file("/dev/stdout", p
);