1 /* parser.c -- convert the command line args into an expression tree.
2 Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2001, 2003,
3 2004, 2005 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
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,
27 #include "modechange.h"
35 #include "stdio-safer.h"
36 #include "regextype.h"
44 /* The presence of unistd.h is assumed by gnulib these days, so we
45 * might as well assume it too.
47 /* We need <unistd.h> for isatty(). */
52 # define _(Text) gettext (Text)
57 # define N_(String) gettext_noop (String)
59 /* See locate.c for explanation as to why not use (String) */
60 # define N_(String) String
63 #if !defined (isascii) || defined (STDC_HEADERS)
70 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
71 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
80 static boolean parse_amin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
81 static boolean parse_and
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
82 static boolean parse_anewer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
83 static boolean parse_atime
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
84 static boolean parse_cmin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
85 static boolean parse_cnewer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
86 static boolean parse_comma
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
87 static boolean parse_ctime
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
88 static boolean parse_daystart
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
89 static boolean parse_delete
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
90 static boolean parse_d
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
91 static boolean parse_depth
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
92 static boolean parse_empty
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
93 static boolean parse_exec
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
94 static boolean parse_execdir
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
95 static boolean parse_false
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
96 static boolean parse_fls
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
97 static boolean parse_fprintf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
98 static boolean parse_follow
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
99 static boolean parse_fprint
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
100 static boolean parse_fprint0
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
101 static boolean parse_fstype
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
102 static boolean parse_gid
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
103 static boolean parse_group
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
104 static boolean parse_help
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
105 static boolean parse_ilname
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
106 static boolean parse_iname
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
107 static boolean parse_inum
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
108 static boolean parse_ipath
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
109 static boolean parse_iregex
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
110 static boolean parse_iwholename
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
111 static boolean parse_links
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
112 static boolean parse_lname
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
113 static boolean parse_ls
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
114 static boolean parse_maxdepth
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
115 static boolean parse_mindepth
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
116 static boolean parse_mmin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
117 static boolean parse_mtime
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
118 static boolean parse_name
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
119 static boolean parse_negate
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
120 static boolean parse_newer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
121 static boolean parse_noleaf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
122 static boolean parse_nogroup
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
123 static boolean parse_nouser
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
124 static boolean parse_nowarn
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
125 static boolean parse_ok
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
126 static boolean parse_okdir
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
127 static boolean parse_or
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
128 static boolean parse_path
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
129 static boolean parse_perm
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
130 static boolean parse_print0
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
131 static boolean parse_printf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
132 static boolean parse_prune
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
133 static boolean parse_regex
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
134 static boolean parse_regextype
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
135 static boolean parse_samefile
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
136 static boolean parse_size
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
137 static boolean parse_true
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
138 static boolean parse_type
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
139 static boolean parse_uid
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
140 static boolean parse_used
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
141 static boolean parse_user
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
142 static boolean parse_version
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
143 static boolean parse_wholename
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
144 static boolean parse_xdev
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
145 static boolean parse_ignore_race
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
146 static boolean parse_noignore_race
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
147 static boolean parse_warn
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
148 static boolean parse_xtype
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
149 static boolean parse_quit
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
153 boolean parse_print
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
154 boolean parse_open
PARAMS((const struct parser_table
* entry
, char *argv
[], int *arg_ptr
));
155 boolean parse_close
PARAMS((const struct parser_table
* entry
, char *argv
[], int *arg_ptr
));
159 static boolean insert_type
PARAMS((char **argv
, int *arg_ptr
, const struct parser_table
*entry
, PRED_FUNC which_pred
));
160 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, const struct parser_table
*entry
, int regex_options
));
161 static boolean insert_fprintf
PARAMS((FILE *fp
, const struct parser_table
*entry
, PRED_FUNC func
, char *argv
[], int *arg_ptr
));
163 static struct segment
**make_segment
PARAMS((struct segment
**segment
, char *format
, int len
, int kind
));
164 static boolean insert_exec_ok
PARAMS((const char *action
, const struct parser_table
*entry
, char *argv
[], int *arg_ptr
));
165 static boolean get_num_days
PARAMS((char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
));
166 static boolean insert_time
PARAMS((char **argv
, int *arg_ptr
, const struct parser_table
* entry
, PRED_FUNC pred
));
167 static boolean get_num
PARAMS((char *str
, uintmax_t *num
, enum comparison_type
*comp_type
));
168 static boolean insert_num
PARAMS((char *argv
[], int *arg_ptr
, const struct parser_table
*entry
));
169 static FILE *open_output_file
PARAMS((char *path
));
170 static boolean
stream_is_tty(FILE *fp
);
173 char *find_pred_name
PARAMS((PRED_FUNC pred_func
));
177 #define PASTE(x,y) x##y
178 #define STRINGIFY(s) #s
180 #define PARSE_OPTION(what,suffix) \
181 { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL }
183 #define PARSE_POSOPT(what,suffix) \
184 { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL }
186 #define PARSE_TEST(what,suffix) \
187 { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
189 #define PARSE_TEST_NP(what,suffix) \
190 { (ARG_TEST), (what), PASTE(parse_,suffix), NULL }
192 #define PARSE_ACTION(what,suffix) \
193 { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
195 #define PARSE_ACTION_NP(what,suffix) \
196 { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL }
198 #define PARSE_PUNCTUATION(what,suffix) \
199 { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
202 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
203 If they are in some Unix versions of find, they are marked `Unix'. */
205 static struct parser_table
const parse_table
[] =
207 PARSE_PUNCTUATION("!", negate
),
208 PARSE_PUNCTUATION("not", negate
), /* GNU */
209 PARSE_PUNCTUATION("(", open
),
210 PARSE_PUNCTUATION(")", close
),
211 PARSE_PUNCTUATION(",", comma
), /* GNU */
212 PARSE_PUNCTUATION("a", and),
213 PARSE_TEST ("amin", amin
), /* GNU */
214 PARSE_PUNCTUATION("and", and), /* GNU */
215 PARSE_TEST ("anewer", anewer
), /* GNU */
216 PARSE_TEST ("atime", atime
),
217 PARSE_TEST ("cmin", cmin
), /* GNU */
218 PARSE_TEST ("cnewer", cnewer
), /* GNU */
219 PARSE_TEST ("ctime", ctime
),
220 PARSE_POSOPT ("daystart", daystart
), /* GNU */
221 PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
222 PARSE_OPTION ("d", d
), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
223 PARSE_OPTION ("depth", depth
),
224 PARSE_TEST ("empty", empty
), /* GNU */
225 PARSE_ACTION ("exec", exec
),
226 PARSE_ACTION ("execdir", execdir
), /* *BSD, GNU */
227 PARSE_ACTION ("fls", fls
), /* GNU */
228 PARSE_POSOPT ("follow", follow
), /* GNU, Unix */
229 PARSE_ACTION ("fprint", fprint
), /* GNU */
230 PARSE_ACTION ("fprint0", fprint0
), /* GNU */
231 PARSE_ACTION ("fprintf", fprintf
), /* GNU */
232 PARSE_TEST ("fstype", fstype
), /* GNU, Unix */
233 PARSE_TEST ("gid", gid
), /* GNU */
234 PARSE_TEST ("group", group
),
235 PARSE_OPTION ("ignore_readdir_race", ignore_race
), /* GNU */
236 PARSE_TEST ("ilname", ilname
), /* GNU */
237 PARSE_TEST ("iname", iname
), /* GNU */
238 PARSE_TEST ("inum", inum
), /* GNU, Unix */
239 PARSE_TEST ("ipath", ipath
), /* GNU, deprecated in favour of iwholename */
240 PARSE_TEST_NP ("iregex", iregex
), /* GNU */
241 PARSE_TEST_NP ("iwholename", iwholename
), /* GNU */
242 PARSE_TEST ("links", links
),
243 PARSE_TEST ("lname", lname
), /* GNU */
244 PARSE_ACTION ("ls", ls
), /* GNU, Unix */
245 PARSE_OPTION ("maxdepth", maxdepth
), /* GNU */
246 PARSE_OPTION ("mindepth", mindepth
), /* GNU */
247 PARSE_TEST ("mmin", mmin
), /* GNU */
248 PARSE_OPTION ("mount", xdev
), /* Unix */
249 PARSE_TEST ("mtime", mtime
),
250 PARSE_TEST ("name", name
),
251 #ifdef UNIMPLEMENTED_UNIX
252 PARSE(ARG_UNIMPLEMENTED
, "ncpio", ncpio
), /* Unix */
254 PARSE_TEST ("newer", newer
),
255 PARSE_OPTION ("noleaf", noleaf
), /* GNU */
256 PARSE_TEST ("nogroup", nogroup
),
257 PARSE_TEST ("nouser", nouser
),
258 PARSE_OPTION ("noignore_readdir_race", noignore_race
), /* GNU */
259 PARSE_OPTION ("nowarn", nowarn
), /* GNU */
260 PARSE_PUNCTUATION("o", or),
261 PARSE_PUNCTUATION("or", or), /* GNU */
262 PARSE_ACTION ("ok", ok
),
263 PARSE_ACTION ("okdir", okdir
), /* GNU (-execdir is BSD) */
264 PARSE_TEST ("path", path
), /* GNU, HP-UX, GNU prefers wholename */
265 PARSE_TEST ("perm", perm
),
266 PARSE_ACTION ("print", print
),
267 PARSE_ACTION ("print0", print0
), /* GNU */
268 PARSE_ACTION_NP ("printf", printf
), /* GNU */
269 PARSE_ACTION ("prune", prune
),
270 PARSE_ACTION ("quit", quit
), /* GNU */
271 PARSE_TEST ("regex", regex
), /* GNU */
272 PARSE_OPTION ("regextype", regextype
), /* GNU */
273 PARSE_TEST ("samefile", samefile
), /* GNU */
274 PARSE_TEST ("size", size
),
275 PARSE_TEST ("type", type
),
276 PARSE_TEST ("uid", uid
), /* GNU */
277 PARSE_TEST ("used", used
), /* GNU */
278 PARSE_TEST ("user", user
),
279 PARSE_OPTION ("warn", warn
), /* GNU */
280 PARSE_TEST_NP ("wholename", wholename
), /* GNU, replaces -path */
281 PARSE_OPTION ("xdev", xdev
),
282 PARSE_TEST ("xtype", xtype
), /* GNU */
283 #ifdef UNIMPLEMENTED_UNIX
284 /* It's pretty ugly for find to know about archive formats.
285 Plus what it could do with cpio archives is very limited.
286 Better to leave it out. */
287 PARSE(ARG_UNIMPLEMENTED
, "cpio", cpio
), /* Unix */
289 /* gnulib's stdbool.h might have made true and false into macros,
290 * so we can't leave named 'true' and 'false' tokens, so we have
291 * to expeant the relevant entries longhand.
293 {ARG_TEST
, "false", parse_false
, pred_false
}, /* GNU */
294 {ARG_TEST
, "true", parse_true
, pred_true
}, /* GNU */
296 /* Various other cases that don't fit neatly into our macro scheme. */
297 {ARG_TEST
, "help", parse_help
, NULL
}, /* GNU */
298 {ARG_TEST
, "-help", parse_help
, NULL
}, /* GNU */
299 {ARG_TEST
, "version", parse_version
, NULL
}, /* GNU */
300 {ARG_TEST
, "-version", parse_version
, NULL
}, /* GNU */
305 static const char *first_nonoption_arg
= NULL
;
310 parse_begin_user_args (char **args
, int argno
, const struct predicate
*last
, const struct predicate
*predicates
)
316 first_nonoption_arg
= NULL
;
320 parse_end_user_args (char **args
, int argno
, const struct predicate
*last
, const struct predicate
*predicates
)
332 /* Return a pointer to the parser function to invoke for predicate
334 Return NULL if SEARCH_NAME is not a valid predicate name. */
336 const struct parser_table
*
337 find_parser (char *search_name
)
340 const char *original_arg
= search_name
;
342 if (*search_name
== '-')
344 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
346 if (strcmp (parse_table
[i
].parser_name
, search_name
) == 0)
348 /* If this is an option, but we have already had a
349 * non-option argument, the user may be under the
350 * impression that the behaviour of the option
351 * argument is conditional on some preceding
352 * tests. This might typically be the case with,
353 * for example, -maxdepth.
355 * The options -daystart and -follow are exempt
356 * from this treatment, since their positioning
357 * in the command line does have an effect on
358 * subsequent tests but not previous ones. That
359 * might be intentional on the part of the user.
361 if (parse_table
[i
].type
!= ARG_POSITIONAL_OPTION
)
363 /* Something other than -follow/-daystart.
364 * If this is an option, check if it followed
365 * a non-option and if so, issue a warning.
367 if (parse_table
[i
].type
== ARG_OPTION
)
369 if ((first_nonoption_arg
!= NULL
)
370 && options
.warnings
)
372 /* option which follows a non-option */
374 _("warning: you have specified the %s "
375 "option after a non-option argument %s, "
376 "but options are not positional (%s affects "
377 "tests specified before it as well as those "
378 "specified after it). Please specify options "
379 "before other arguments.\n"),
387 /* Not an option or a positional option,
388 * so remember we've seen it in order to
389 * use it in a possible future warning message.
391 if (first_nonoption_arg
== NULL
)
393 first_nonoption_arg
= original_arg
;
398 return &parse_table
[i
];
404 /* The parsers are responsible to continue scanning ARGV for
405 their arguments. Each parser knows what is and isn't
408 ARGV is the argument array.
409 *ARG_PTR is the index to start at in ARGV,
410 updated to point beyond the last element consumed.
412 The predicate structure is updated with the new information. */
415 parse_amin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
417 struct predicate
*our_pred
;
419 enum comparison_type c_type
;
422 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
424 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
426 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
427 our_pred
= insert_primary (entry
);
428 our_pred
->args
.info
.kind
= c_type
;
429 our_pred
->args
.info
.negative
= t
< 0;
430 our_pred
->args
.info
.l_val
= t
;
436 parse_and (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
438 struct predicate
*our_pred
;
443 our_pred
= get_new_pred (entry
);
444 our_pred
->pred_func
= pred_and
;
446 our_pred
->p_name
= find_pred_name (pred_and
);
448 our_pred
->p_type
= BI_OP
;
449 our_pred
->p_prec
= AND_PREC
;
450 our_pred
->need_stat
= our_pred
->need_type
= false;
455 parse_anewer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
457 struct predicate
*our_pred
;
458 struct stat stat_newer
;
460 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
462 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
463 error (1, errno
, "%s", argv
[*arg_ptr
]);
464 our_pred
= insert_primary (entry
);
465 our_pred
->args
.time
= stat_newer
.st_mtime
;
471 parse_atime (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
473 return insert_time (argv
, arg_ptr
, entry
, pred_atime
);
477 parse_close (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
479 struct predicate
*our_pred
;
484 our_pred
= get_new_pred (entry
);
485 our_pred
->pred_func
= pred_close
;
487 our_pred
->p_name
= find_pred_name (pred_close
);
489 our_pred
->p_type
= CLOSE_PAREN
;
490 our_pred
->p_prec
= NO_PREC
;
491 our_pred
->need_stat
= our_pred
->need_type
= false;
496 parse_cmin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
498 struct predicate
*our_pred
;
500 enum comparison_type c_type
;
503 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
505 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
507 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
508 our_pred
= insert_primary (entry
);
509 our_pred
->args
.info
.kind
= c_type
;
510 our_pred
->args
.info
.negative
= t
< 0;
511 our_pred
->args
.info
.l_val
= t
;
517 parse_cnewer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
519 struct predicate
*our_pred
;
520 struct stat stat_newer
;
522 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
524 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
525 error (1, errno
, "%s", argv
[*arg_ptr
]);
526 our_pred
= insert_primary (entry
);
527 our_pred
->args
.time
= stat_newer
.st_mtime
;
533 parse_comma (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
535 struct predicate
*our_pred
;
540 our_pred
= get_new_pred (entry
);
541 our_pred
->pred_func
= pred_comma
;
543 our_pred
->p_name
= find_pred_name (pred_comma
);
545 our_pred
->p_type
= BI_OP
;
546 our_pred
->p_prec
= COMMA_PREC
;
547 our_pred
->need_stat
= our_pred
->need_type
= false;
552 parse_ctime (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
554 return insert_time (argv
, arg_ptr
, entry
, pred_ctime
);
558 parse_daystart (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
566 if (options
.full_days
== false)
568 options
.cur_day_start
+= DAYSECS
;
569 local
= localtime (&options
.cur_day_start
);
570 options
.cur_day_start
-= (local
571 ? (local
->tm_sec
+ local
->tm_min
* 60
572 + local
->tm_hour
* 3600)
573 : options
.cur_day_start
% DAYSECS
);
574 options
.full_days
= true;
580 parse_delete (const struct parser_table
* entry
, char *argv
[], int *arg_ptr
)
582 struct predicate
*our_pred
;
586 our_pred
= insert_primary (entry
);
587 our_pred
->side_effects
= our_pred
->no_default_print
= true;
588 /* -delete implies -depth */
589 options
.do_dir_first
= false;
594 parse_depth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
600 options
.do_dir_first
= false;
605 parse_d (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
610 if (options
.warnings
)
613 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
615 return parse_depth(entry
, argv
, arg_ptr
);
619 parse_empty (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
624 insert_primary (entry
);
629 parse_exec (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
631 return insert_exec_ok ("-exec", entry
, argv
, arg_ptr
);
635 parse_execdir (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
637 return insert_exec_ok ("-execdir", entry
, argv
, arg_ptr
);
641 parse_false (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
643 struct predicate
*our_pred
;
648 our_pred
= insert_primary (entry
);
649 our_pred
->need_stat
= our_pred
->need_type
= false;
650 our_pred
->side_effects
= our_pred
->no_default_print
= false;
655 parse_fls (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
657 struct predicate
*our_pred
;
659 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
661 our_pred
= insert_primary (entry
);
662 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
663 our_pred
->side_effects
= our_pred
->no_default_print
= true;
669 parse_fprintf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
673 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
675 if (argv
[*arg_ptr
+ 1] == NULL
)
677 /* Ensure we get "missing arg" message, not "invalid arg". */
681 fp
= open_output_file (argv
[*arg_ptr
]);
683 return insert_fprintf (fp
, entry
, pred_fprintf
, argv
, arg_ptr
);
687 parse_follow (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
693 set_follow_state(SYMLINK_ALWAYS_DEREF
);
698 parse_fprint (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
700 struct predicate
*our_pred
;
702 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
704 our_pred
= insert_primary (entry
);
705 our_pred
->args
.printf_vec
.segment
= NULL
;
706 our_pred
->args
.printf_vec
.stream
= open_output_file (argv
[*arg_ptr
]);
707 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(our_pred
->args
.printf_vec
.stream
);
708 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
709 our_pred
->side_effects
= our_pred
->no_default_print
= true;
710 our_pred
->need_stat
= our_pred
->need_type
= false;
716 parse_fprint0 (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
718 struct predicate
*our_pred
;
720 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
722 our_pred
= insert_primary (entry
);
723 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
724 our_pred
->side_effects
= our_pred
->no_default_print
= true;
725 our_pred
->need_stat
= our_pred
->need_type
= false;
731 parse_fstype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
733 struct predicate
*our_pred
;
735 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
737 our_pred
= insert_primary (entry
);
738 our_pred
->args
.str
= argv
[*arg_ptr
];
744 parse_gid (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
746 return insert_num (argv
, arg_ptr
, entry
);
750 parse_group (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
752 struct group
*cur_gr
;
753 struct predicate
*our_pred
;
757 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
759 cur_gr
= getgrnam (argv
[*arg_ptr
]);
762 gid
= cur_gr
->gr_gid
;
765 gid_len
= strspn (argv
[*arg_ptr
], "0123456789");
766 if ((gid_len
== 0) || (argv
[*arg_ptr
][gid_len
] != '\0'))
768 gid
= atoi (argv
[*arg_ptr
]);
770 our_pred
= insert_primary (entry
);
771 our_pred
->args
.gid
= gid
;
777 parse_help (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
784 Usage: %s [path...] [expression]\n"), program_name
);
786 default path is the current directory; default expression is -print\n\
787 expression may consist of: operators, options, tests, and actions:\n"));
789 operators (decreasing precedence; -and is implicit where no others are given):\n\
790 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
791 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
793 positional options (always true): -daystart -follow -regextype\n\n\
794 normal options (always true, specified before other expressions):\n\
795 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
796 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
798 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
799 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
800 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
801 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
803 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
804 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
805 -used N -user NAME -xtype [bcdpfls]\n"));
807 actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
808 -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
809 -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
810 -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
812 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
813 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
814 email to <bug-findutils@gnu.org>."));
819 parse_ilname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
821 struct predicate
*our_pred
;
823 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
825 our_pred
= insert_primary (entry
);
826 our_pred
->args
.str
= argv
[*arg_ptr
];
832 /* sanity check the fnmatch() function to make sure
833 * it really is the GNU version.
836 fnmatch_sanitycheck(void)
838 /* fprintf(stderr, "Performing find sanity check..."); */
839 if (0 != fnmatch("foo", "foo", 0)
840 || 0 == fnmatch("Foo", "foo", 0)
841 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD
))
843 error (1, 0, _("sanity check of the fnmatch() library function failed."));
844 /* fprintf(stderr, "FAILED\n"); */
848 /* fprintf(stderr, "OK\n"); */
854 check_name_arg(const char *pred
, const char *arg
)
856 if (strchr(arg
, '/'))
858 error(0, 0,_("warning: Unix filenames usually don't contain slashes (though pathnames do). That means that '%s %s' will probably evaluate to false all the time on this system. You might find the '-wholename' test more useful, or perhaps '-samefile'. Alternatively, if you are using GNU grep, you could use 'find ... -print0 | grep -FzZ %s'."),
861 return true; /* allow it anyway */
867 parse_iname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
869 struct predicate
*our_pred
;
871 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
873 if (!check_name_arg("-iname", argv
[*arg_ptr
]))
876 fnmatch_sanitycheck();
878 our_pred
= insert_primary (entry
);
879 our_pred
->need_stat
= our_pred
->need_type
= false;
880 our_pred
->args
.str
= argv
[*arg_ptr
];
886 parse_inum (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
888 return insert_num (argv
, arg_ptr
, entry
);
891 /* -ipath is deprecated (at RMS's request) in favour of
892 * -iwholename. See the node "GNU Manuals" in standards.texi
893 * for the rationale for this (basically, GNU prefers the use
894 * of the phrase "file name" to "path name"
897 parse_ipath (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
900 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
902 return parse_iwholename(entry
, argv
, arg_ptr
);
906 parse_iwholename (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
908 struct predicate
*our_pred
;
910 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
913 fnmatch_sanitycheck();
915 our_pred
= insert_primary_withpred (entry
, pred_ipath
);
916 our_pred
->need_stat
= our_pred
->need_type
= false;
917 our_pred
->args
.str
= argv
[*arg_ptr
];
923 parse_iregex (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
925 return insert_regex (argv
, arg_ptr
, entry
, RE_ICASE
|options
.regex_options
);
929 parse_links (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
931 return insert_num (argv
, arg_ptr
, entry
);
935 parse_lname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
937 struct predicate
*our_pred
;
942 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
945 fnmatch_sanitycheck();
947 our_pred
= insert_primary (entry
);
948 our_pred
->args
.str
= argv
[*arg_ptr
];
954 parse_ls (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
956 struct predicate
*our_pred
;
961 our_pred
= insert_primary (entry
);
962 our_pred
->side_effects
= our_pred
->no_default_print
= true;
967 parse_maxdepth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
972 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
974 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
975 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
977 options
.maxdepth
= atoi (argv
[*arg_ptr
]);
978 if (options
.maxdepth
< 0)
985 parse_mindepth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
990 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
992 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
993 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
995 options
.mindepth
= atoi (argv
[*arg_ptr
]);
996 if (options
.mindepth
< 0)
1003 parse_mmin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1005 struct predicate
*our_pred
;
1007 enum comparison_type c_type
;
1010 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1012 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
1014 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
1015 our_pred
= insert_primary (entry
);
1016 our_pred
->args
.info
.kind
= c_type
;
1017 our_pred
->args
.info
.negative
= t
< 0;
1018 our_pred
->args
.info
.l_val
= t
;
1024 parse_mtime (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1026 return insert_time (argv
, arg_ptr
, entry
, pred_mtime
);
1030 parse_name (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1032 struct predicate
*our_pred
;
1037 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1039 if (!check_name_arg("-name", argv
[*arg_ptr
]))
1041 fnmatch_sanitycheck();
1043 our_pred
= insert_primary (entry
);
1044 our_pred
->need_stat
= our_pred
->need_type
= false;
1045 our_pred
->args
.str
= argv
[*arg_ptr
];
1051 parse_negate (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1053 struct predicate
*our_pred
;
1058 our_pred
= get_new_pred_chk_op (entry
);
1059 our_pred
->pred_func
= pred_negate
;
1061 our_pred
->p_name
= find_pred_name (pred_negate
);
1063 our_pred
->p_type
= UNI_OP
;
1064 our_pred
->p_prec
= NEGATE_PREC
;
1065 our_pred
->need_stat
= our_pred
->need_type
= false;
1070 parse_newer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1072 struct predicate
*our_pred
;
1073 struct stat stat_newer
;
1078 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1080 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
1081 error (1, errno
, "%s", argv
[*arg_ptr
]);
1082 our_pred
= insert_primary (entry
);
1083 our_pred
->args
.time
= stat_newer
.st_mtime
;
1089 parse_noleaf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1095 options
.no_leaf_check
= true;
1100 /* Arbitrary amount by which to increase size
1101 of `uid_unused' and `gid_unused'. */
1102 #define ALLOC_STEP 2048
1104 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1105 char *uid_unused
= NULL
;
1107 /* Number of elements in `uid_unused'. */
1108 unsigned uid_allocated
;
1110 /* Similar for GIDs and group entries. */
1111 char *gid_unused
= NULL
;
1112 unsigned gid_allocated
;
1116 parse_nogroup (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1118 struct predicate
*our_pred
;
1123 our_pred
= insert_primary (entry
);
1125 if (gid_unused
== NULL
)
1129 gid_allocated
= ALLOC_STEP
;
1130 gid_unused
= xmalloc (gid_allocated
);
1131 memset (gid_unused
, 1, gid_allocated
);
1133 while ((gr
= getgrent ()) != NULL
)
1135 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
1137 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
1138 gid_unused
= xrealloc (gid_unused
, new_allocated
);
1139 memset (gid_unused
+ gid_allocated
, 1,
1140 new_allocated
- gid_allocated
);
1141 gid_allocated
= new_allocated
;
1143 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
1152 parse_nouser (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1154 struct predicate
*our_pred
;
1159 our_pred
= insert_primary (entry
);
1161 if (uid_unused
== NULL
)
1165 uid_allocated
= ALLOC_STEP
;
1166 uid_unused
= xmalloc (uid_allocated
);
1167 memset (uid_unused
, 1, uid_allocated
);
1169 while ((pw
= getpwent ()) != NULL
)
1171 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
1173 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
1174 uid_unused
= xrealloc (uid_unused
, new_allocated
);
1175 memset (uid_unused
+ uid_allocated
, 1,
1176 new_allocated
- uid_allocated
);
1177 uid_allocated
= new_allocated
;
1179 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
1188 parse_nowarn (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1194 options
.warnings
= false;
1199 parse_ok (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1201 return insert_exec_ok ("-ok", entry
, argv
, arg_ptr
);
1205 parse_okdir (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1207 return insert_exec_ok ("-okdir", entry
, argv
, arg_ptr
);
1211 parse_open (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1213 struct predicate
*our_pred
;
1218 our_pred
= get_new_pred_chk_op (entry
);
1219 our_pred
->pred_func
= pred_open
;
1221 our_pred
->p_name
= find_pred_name (pred_open
);
1223 our_pred
->p_type
= OPEN_PAREN
;
1224 our_pred
->p_prec
= NO_PREC
;
1225 our_pred
->need_stat
= our_pred
->need_type
= false;
1230 parse_or (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1232 struct predicate
*our_pred
;
1237 our_pred
= get_new_pred (entry
);
1238 our_pred
->pred_func
= pred_or
;
1240 our_pred
->p_name
= find_pred_name (pred_or
);
1242 our_pred
->p_type
= BI_OP
;
1243 our_pred
->p_prec
= OR_PREC
;
1244 our_pred
->need_stat
= our_pred
->need_type
= false;
1248 /* -path is deprecated (at RMS's request) in favour of
1249 * -iwholename. See the node "GNU Manuals" in standards.texi
1250 * for the rationale for this (basically, GNU prefers the use
1251 * of the phrase "file name" to "path name".
1253 * We do not issue a warning that this usage is deprecated
1254 * since HPUX find supports this predicate also.
1257 parse_path (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1259 return parse_wholename(entry
, argv
, arg_ptr
);
1263 parse_wholename (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1265 struct predicate
*our_pred
;
1267 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1269 our_pred
= insert_primary_withpred (entry
, pred_path
);
1270 our_pred
->need_stat
= our_pred
->need_type
= false;
1271 our_pred
->args
.str
= argv
[*arg_ptr
];
1277 parse_perm (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1281 boolean havekind
= false;
1282 enum permissions_type kind
= PERM_EXACT
;
1283 struct mode_change
*change
= NULL
;
1284 struct predicate
*our_pred
;
1286 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1289 switch (argv
[*arg_ptr
][0])
1293 kind
= PERM_AT_LEAST
;
1298 change
= mode_compile (argv
[*arg_ptr
]);
1301 /* Most likely the caller is an old script that is still
1302 * using the obsolete GNU syntax '-perm +MODE'. This old
1303 * syntax was withdrawn in favor of '-perm /MODE' because
1304 * it is incompatible with POSIX in some cases, but we
1305 * still support uses of it that are not incompatible with
1313 /* This is a POSIX-compatible usage */
1320 case '/': /* GNU extension */
1327 /* For example, '-perm 0644', which is valid and matches
1328 * only files whose mode is exactly 0644.
1330 * We do nothing here, because mode_start and kind are already
1338 change
= mode_compile (argv
[*arg_ptr
] + mode_start
);
1340 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
1342 perm_val
= mode_adjust (0, change
, 0);
1345 our_pred
= insert_primary (entry
);
1349 our_pred
->args
.perm
.kind
= kind
;
1354 switch (argv
[*arg_ptr
][0])
1357 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
1360 our_pred
->args
.perm
.kind
= PERM_ANY
;
1363 our_pred
->args
.perm
.kind
= PERM_EXACT
;
1367 our_pred
->args
.perm
.val
= perm_val
& MODE_ALL
;
1373 parse_print (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1375 struct predicate
*our_pred
;
1380 our_pred
= insert_primary (entry
);
1381 /* -print has the side effect of printing. This prevents us
1382 from doing undesired multiple printing when the user has
1383 already specified -print. */
1384 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1385 our_pred
->need_stat
= our_pred
->need_type
= false;
1386 our_pred
->args
.printf_vec
.segment
= NULL
;
1387 our_pred
->args
.printf_vec
.stream
= stdout
;
1388 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(stdout
);
1389 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
1395 parse_print0 (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1397 struct predicate
*our_pred
;
1402 our_pred
= insert_primary (entry
);
1403 /* -print0 has the side effect of printing. This prevents us
1404 from doing undesired multiple printing when the user has
1405 already specified -print0. */
1406 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1407 our_pred
->need_stat
= our_pred
->need_type
= false;
1412 parse_printf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1414 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1416 return insert_fprintf (stdout
, entry
, pred_fprintf
, argv
, arg_ptr
);
1420 parse_prune (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1422 struct predicate
*our_pred
;
1427 our_pred
= insert_primary (entry
);
1428 our_pred
->need_stat
= our_pred
->need_type
= false;
1429 /* -prune has a side effect that it does not descend into
1430 the current directory. */
1431 our_pred
->side_effects
= true;
1432 our_pred
->no_default_print
= false;
1437 parse_quit (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1439 struct predicate
*our_pred
= insert_primary (entry
);
1442 our_pred
->need_stat
= our_pred
->need_type
= false;
1443 our_pred
->side_effects
= true; /* Exiting is a side effect... */
1444 our_pred
->no_default_print
= false; /* Don't inhibit the default print, though. */
1450 parse_regextype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1452 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1455 /* collect the regex type name */
1456 options
.regex_options
= get_regex_type(argv
[*arg_ptr
]);
1464 parse_regex (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1466 return insert_regex (argv
, arg_ptr
, entry
, options
.regex_options
);
1470 insert_regex (char **argv
, int *arg_ptr
, const struct parser_table
*entry
, int regex_options
)
1472 struct predicate
*our_pred
;
1473 struct re_pattern_buffer
*re
;
1474 const char *error_message
;
1476 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1478 our_pred
= insert_primary_withpred (entry
, pred_regex
);
1479 our_pred
->need_stat
= our_pred
->need_type
= false;
1480 re
= (struct re_pattern_buffer
*)
1481 xmalloc (sizeof (struct re_pattern_buffer
));
1482 our_pred
->args
.regex
= re
;
1483 re
->allocated
= 100;
1484 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1487 re_set_syntax(regex_options
);
1488 re
->syntax
= regex_options
;
1489 re
->translate
= NULL
;
1491 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1494 error (1, 0, "%s", error_message
);
1500 parse_size (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1502 struct predicate
*our_pred
;
1504 enum comparison_type c_type
;
1508 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1510 len
= strlen (argv
[*arg_ptr
]);
1512 error (1, 0, _("invalid null argument to -size"));
1513 switch (argv
[*arg_ptr
][len
- 1])
1517 argv
[*arg_ptr
][len
- 1] = '\0';
1522 argv
[*arg_ptr
][len
- 1] = '\0';
1527 argv
[*arg_ptr
][len
- 1] = '\0';
1530 case 'M': /* Megabytes */
1531 blksize
= 1024*1024;
1532 argv
[*arg_ptr
][len
- 1] = '\0';
1535 case 'G': /* Gigabytes */
1536 blksize
= 1024*1024*1024;
1537 argv
[*arg_ptr
][len
- 1] = '\0';
1542 argv
[*arg_ptr
][len
- 1] = '\0';
1558 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1560 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1562 our_pred
= insert_primary (entry
);
1563 our_pred
->args
.size
.kind
= c_type
;
1564 our_pred
->args
.size
.blocksize
= blksize
;
1565 our_pred
->args
.size
.size
= num
;
1572 parse_samefile (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1574 struct predicate
*our_pred
;
1577 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1579 if ((*options
.xstat
) (argv
[*arg_ptr
], &st
))
1580 error (1, errno
, "%s", argv
[*arg_ptr
]);
1582 our_pred
= insert_primary (entry
);
1583 our_pred
->args
.fileid
.ino
= st
.st_ino
;
1584 our_pred
->args
.fileid
.dev
= st
.st_dev
;
1585 our_pred
->need_type
= false;
1586 our_pred
->need_stat
= true;
1593 parse_true (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1595 struct predicate
*our_pred
;
1600 our_pred
= insert_primary (entry
);
1601 our_pred
->need_stat
= our_pred
->need_type
= false;
1606 parse_type (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1608 return insert_type (argv
, arg_ptr
, entry
, pred_type
);
1612 parse_uid (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1614 return insert_num (argv
, arg_ptr
, entry
);
1618 parse_used (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1620 struct predicate
*our_pred
;
1622 enum comparison_type c_type
;
1625 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1627 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1629 t
= num_days
* DAYSECS
;
1630 our_pred
= insert_primary (entry
);
1631 our_pred
->args
.info
.kind
= c_type
;
1632 our_pred
->args
.info
.negative
= t
< 0;
1633 our_pred
->args
.info
.l_val
= t
;
1639 parse_user (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1641 struct passwd
*cur_pwd
;
1642 struct predicate
*our_pred
;
1646 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1648 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1650 if (cur_pwd
!= NULL
)
1651 uid
= cur_pwd
->pw_uid
;
1654 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1655 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1657 uid
= atoi (argv
[*arg_ptr
]);
1659 our_pred
= insert_primary (entry
);
1660 our_pred
->args
.uid
= uid
;
1666 parse_version (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1668 extern char *version_string
;
1676 printf (_("GNU find version %s\n"), version_string
);
1677 printf (_("Features enabled: "));
1680 printf("CACHE_IDS ");
1688 printf("DEBUG_STAT ");
1691 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1695 #if defined(O_NOFOLLOW)
1696 printf("O_NOFOLLOW(%s) ",
1697 (options
.open_nofollow_available
? "enabled" : "disabled"));
1700 #if defined(LEAF_OPTIMISATION)
1701 printf("LEAF_OPTIMISATION ");
1706 /* For the moment, leave this as English in case someone wants
1707 to parse these strings. */
1716 parse_xdev (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1721 options
.stay_on_filesystem
= true;
1726 parse_ignore_race (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1731 options
.ignore_readdir_race
= true;
1736 parse_noignore_race (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1741 options
.ignore_readdir_race
= false;
1746 parse_warn (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1751 options
.warnings
= true;
1756 parse_xtype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1760 return insert_type (argv
, arg_ptr
, entry
, pred_xtype
);
1764 insert_type (char **argv
, int *arg_ptr
, const struct parser_table
*entry
, PRED_FUNC which_pred
)
1767 struct predicate
*our_pred
;
1769 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
1770 || (strlen (argv
[*arg_ptr
]) != 1))
1772 switch (argv
[*arg_ptr
][0])
1774 case 'b': /* block special */
1775 type_cell
= S_IFBLK
;
1777 case 'c': /* character special */
1778 type_cell
= S_IFCHR
;
1780 case 'd': /* directory */
1781 type_cell
= S_IFDIR
;
1783 case 'f': /* regular file */
1784 type_cell
= S_IFREG
;
1787 case 'l': /* symbolic link */
1788 type_cell
= S_IFLNK
;
1792 case 'p': /* pipe */
1793 type_cell
= S_IFIFO
;
1797 case 's': /* socket */
1798 type_cell
= S_IFSOCK
;
1802 case 'D': /* Solaris door */
1803 type_cell
= S_IFDOOR
;
1806 default: /* None of the above ... nuke 'em. */
1809 our_pred
= insert_primary_withpred (entry
, which_pred
);
1811 /* Figure out if we will need to stat the file, because if we don't
1812 * need to follow symlinks, we can avoid a stat call by using
1813 * struct dirent.d_type.
1815 if (which_pred
== pred_xtype
)
1817 our_pred
->need_stat
= true;
1818 our_pred
->need_type
= false;
1822 our_pred
->need_stat
= false; /* struct dirent is enough */
1823 our_pred
->need_type
= true;
1825 our_pred
->args
.type
= type_cell
;
1826 (*arg_ptr
)++; /* Move on to next argument. */
1831 /* Return true if the file accessed via FP is a terminal.
1834 stream_is_tty(FILE *fp
)
1836 int fd
= fileno(fp
);
1839 return false; /* not a valid stream */
1843 return isatty(fd
) ? true : false;
1850 /* If true, we've determined that the current fprintf predicate
1851 uses stat information. */
1852 static boolean fprintf_stat_needed
;
1854 /* XXX: do we need to pass FUNC to this function? */
1856 insert_fprintf (FILE *fp
, const struct parser_table
*entry
, PRED_FUNC func
, char **argv
, int *arg_ptr
)
1858 char *format
; /* Beginning of unprocessed format string. */
1859 register char *scan
; /* Current address in scanning `format'. */
1860 register char *scan2
; /* Address inside of element being scanned. */
1861 struct segment
**segmentp
; /* Address of current segment. */
1862 struct predicate
*our_pred
;
1864 format
= argv
[(*arg_ptr
)++];
1866 fprintf_stat_needed
= false; /* Might be overridden later. */
1867 our_pred
= insert_primary_withpred (entry
, func
);
1868 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1869 our_pred
->args
.printf_vec
.stream
= fp
;
1870 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(fp
);
1871 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
1872 segmentp
= &our_pred
->args
.printf_vec
.segment
;
1875 for (scan
= format
; *scan
; scan
++)
1880 if (*scan2
>= '0' && *scan2
<= '7')
1884 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
1886 n
= 8 * n
+ *scan2
- '0';
1901 make_segment (segmentp
, format
, scan
- format
, KIND_STOP
);
1902 our_pred
->need_stat
= fprintf_stat_needed
;
1920 /* *scan = '\\'; * it already is */
1924 _("warning: unrecognized escape `\\%c'"), *scan2
);
1929 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1931 format
= scan2
+ 1; /* Move past the escape. */
1932 scan
= scan2
; /* Incremented immediately by `for'. */
1934 else if (*scan
== '%')
1938 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1944 /* Scan past flags, width and precision, to verify kind. */
1945 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
1947 while (ISDIGIT (*scan2
))
1950 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
1952 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2
))
1954 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1959 else if (strchr ("ACT", *scan2
) && scan2
[1])
1961 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1962 *scan2
| (scan2
[1] << 8));
1969 /* An unrecognized % escape. Print the char after the %. */
1970 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1972 segmentp
= make_segment (segmentp
, format
, scan
- format
,
1981 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
);
1982 our_pred
->need_type
= false;
1983 our_pred
->need_stat
= fprintf_stat_needed
;
1987 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1988 from the text in FORMAT, which has length LEN.
1989 Return the address of the `next' pointer of the new segment. */
1991 static struct segment
**
1992 make_segment (struct segment
**segment
, char *format
, int len
, int kind
)
1996 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
1998 (*segment
)->kind
= kind
;
1999 (*segment
)->next
= NULL
;
2000 (*segment
)->text_len
= len
;
2002 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
2003 strncpy (fmt
, format
, len
);
2006 switch (kind
& 0xff)
2008 case KIND_PLAIN
: /* Plain text string, no % conversion. */
2009 case KIND_STOP
: /* Terminate argument, no newline. */
2012 case 'a': /* atime in `ctime' format */
2013 case 'A': /* atime in user-specified strftime format */
2014 case 'c': /* ctime in `ctime' format */
2015 case 'C': /* ctime in user-specified strftime format */
2016 case 'F': /* filesystem type */
2017 case 'g': /* group name */
2018 case 'i': /* inode number */
2019 case 'l': /* object of symlink */
2020 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
2021 case 's': /* size in bytes */
2022 case 't': /* mtime in `ctime' format */
2023 case 'T': /* mtime in user-specified strftime format */
2024 case 'u': /* user name */
2025 case 'y': /* file type */
2026 case 'Y': /* symlink pointed file type */
2027 fprintf_stat_needed
= true;
2029 case 'f': /* basename of path */
2030 case 'h': /* leading directories part of path */
2031 case 'H': /* ARGV element file was found under */
2032 case 'p': /* pathname */
2033 case 'P': /* pathname with ARGV element stripped */
2037 /* Numeric items that one might expect to honour
2038 * #, 0, + flags but which do not.
2040 case 'G': /* GID number */
2041 case 'U': /* UID number */
2042 case 'b': /* size in 512-byte blocks */
2043 case 'D': /* Filesystem device on which the file exits */
2044 case 'k': /* size in 1K blocks */
2045 case 'n': /* number of links */
2046 fprintf_stat_needed
= true;
2050 /* Numeric items that DO honour #, 0, + flags.
2052 case 'd': /* depth in search tree (0 = ARGV element) */
2056 case 'm': /* mode as octal number (perms only) */
2058 fprintf_stat_needed
= true;
2063 return &(*segment
)->next
;
2067 check_path_safety(const char *action
)
2069 const char *path
= getenv("PATH");
2071 s
= next_element(path
, 1);
2072 while ((s
= next_element ((char *) NULL
, 1)) != NULL
)
2074 if (0 == strcmp(s
, "."))
2076 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)"),
2083 /* handles both exec and ok predicate */
2084 #if defined(NEW_EXEC)
2085 /* handles both exec and ok predicate */
2087 new_insert_exec_ok (const char *action
,
2088 const struct parser_table
*entry
,
2092 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2093 int i
; /* Index into cmd args */
2094 int saw_braces
; /* True if previous arg was '{}'. */
2095 boolean allow_plus
; /* True if + is a valid terminator */
2096 int brace_count
; /* Number of instances of {}. */
2097 PRED_FUNC func
= entry
->pred_func
;
2099 struct predicate
*our_pred
;
2100 struct exec_val
*execp
; /* Pointer for efficiency. */
2102 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2105 our_pred
= insert_primary_withpred (entry
, func
);
2106 our_pred
->side_effects
= our_pred
->no_default_print
= true;
2107 execp
= &our_pred
->args
.exec_vec
;
2109 if ((func
!= pred_okdir
) && (func
!= pred_ok
))
2112 execp
->close_stdin
= false;
2117 /* If find reads stdin (i.e. for -ok and similar), close stdin
2118 * in the child to prevent some script from consiming the output
2119 * intended for find.
2121 execp
->close_stdin
= true;
2125 if ((func
== pred_execdir
) || (func
== pred_okdir
))
2127 options
.ignore_readdir_race
= false;
2128 check_path_safety(action
);
2129 execp
->use_current_dir
= true;
2133 execp
->use_current_dir
= false;
2136 our_pred
->args
.exec_vec
.multiple
= 0;
2138 /* Count the number of args with path replacements, up until the ';'.
2139 * Also figure out if the command is terminated by ";" or by "+".
2142 for (end
= start
, saw_braces
=0, brace_count
=0;
2144 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2147 /* For -exec and -execdir, "{} +" can terminate the command. */
2149 && argv
[end
][0] == '+' && argv
[end
][1] == 0
2152 our_pred
->args
.exec_vec
.multiple
= 1;
2157 if (strstr (argv
[end
], "{}"))
2162 if (0 == end
&& (func
== pred_execdir
|| func
== pred_okdir
))
2164 /* The POSIX standard says that {} replacement should
2165 * occur even in the utility name. This is insecure
2166 * since it means we will be executing a command whose
2167 * name is chosen according to whatever find finds in
2168 * the filesystem. That can be influenced by an
2169 * attacker. Hence for -execdir and -okdir this is not
2170 * allowed. We can specify this as those options are
2171 * not defined by POSIX.
2173 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2178 /* Fail if no command given or no semicolon found. */
2179 if ((end
== start
) || (argv
[end
] == NULL
))
2186 if (our_pred
->args
.exec_vec
.multiple
&& brace_count
> 1)
2190 if (func
== pred_execdir
)
2196 _("Only one instance of {} is supported with -exec%s ... +"),
2200 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2201 bc_init_controlinfo(&execp
->ctl
);
2202 execp
->ctl
.exec_callback
= launch
;
2204 if (our_pred
->args
.exec_vec
.multiple
)
2206 /* "+" terminator, so we can just append our arguments after the
2207 * command and initial arguments.
2209 execp
->replace_vec
= NULL
;
2210 execp
->ctl
.replace_pat
= NULL
;
2211 execp
->ctl
.rplen
= 0;
2212 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2213 execp
->ctl
.args_per_exec
= 0; /* no limit */
2215 /* remember how many arguments there are */
2216 execp
->ctl
.initial_argc
= (end
-start
) - 1;
2218 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2219 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2221 /* Gather the initial arguments. Skip the {}. */
2222 for (i
=start
; i
<end
-1; ++i
)
2224 bc_push_arg(&execp
->ctl
, &execp
->state
,
2225 argv
[i
], strlen(argv
[i
])+1,
2232 /* Semicolon terminator - more than one {} is supported, so we
2233 * have to do brace-replacement.
2235 execp
->num_args
= end
- start
;
2237 execp
->ctl
.replace_pat
= "{}";
2238 execp
->ctl
.rplen
= strlen(execp
->ctl
.replace_pat
);
2239 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2240 execp
->ctl
.args_per_exec
= 0; /* no limit */
2241 execp
->replace_vec
= xmalloc(sizeof(char*)*execp
->num_args
);
2244 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2245 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2247 /* Remember the (pre-replacement) arguments for later. */
2248 for (i
=0; i
<execp
->num_args
; ++i
)
2250 execp
->replace_vec
[i
] = argv
[i
+start
];
2254 if (argv
[end
] == NULL
)
2262 /* handles both exec and ok predicate */
2264 old_insert_exec_ok (boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
2266 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2267 int num_paths
; /* Number of args with path replacements. */
2268 int path_pos
; /* Index in array of path replacements. */
2269 int vec_pos
; /* Index in array of args. */
2270 struct predicate
*our_pred
;
2271 struct exec_val
*execp
; /* Pointer for efficiency. */
2273 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2276 /* Count the number of args with path replacements, up until the ';'. */
2278 for (end
= start
, num_paths
= 0;
2280 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2282 if (strstr (argv
[end
], "{}"))
2284 /* Fail if no command given or no semicolon found. */
2285 if ((end
== start
) || (argv
[end
] == NULL
))
2291 our_pred
= insert_primary (func
);
2292 our_pred
->side_effects
= our_pred
->no_default_print
= true;
2293 execp
= &our_pred
->args
.exec_vec
;
2294 execp
->usercontext
= our_pred
;
2295 execp
->use_current_dir
= false;
2297 (struct path_arg
*) xmalloc (sizeof (struct path_arg
) * (num_paths
+ 1));
2298 execp
->vec
= (char **) xmalloc (sizeof (char *) * (end
- start
+ 1));
2299 /* Record the positions of all args, and the args with path replacements. */
2300 for (end
= start
, path_pos
= vec_pos
= 0;
2302 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2307 execp
->paths
[path_pos
].count
= 0;
2308 for (p
= argv
[end
]; *p
; ++p
)
2309 if (p
[0] == '{' && p
[1] == '}')
2311 execp
->paths
[path_pos
].count
++;
2314 if (execp
->paths
[path_pos
].count
)
2316 execp
->paths
[path_pos
].offset
= vec_pos
;
2317 execp
->paths
[path_pos
].origarg
= argv
[end
];
2320 execp
->vec
[vec_pos
++] = argv
[end
];
2322 execp
->paths
[path_pos
].offset
= -1;
2323 execp
->vec
[vec_pos
] = NULL
;
2325 if (argv
[end
] == NULL
)
2336 insert_exec_ok (const char *action
, const struct parser_table
*entry
, char **argv
, int *arg_ptr
)
2338 #if defined(NEW_EXEC)
2339 return new_insert_exec_ok(action
, entry
, argv
, arg_ptr
);
2341 return old_insert_exec_ok(func
, argv
, arg_ptr
);
2347 /* Get a number of days and comparison type.
2348 STR is the ASCII representation.
2349 Set *NUM_DAYS to the number of days, taken as being from
2350 the current moment (or possibly midnight). Thus the sense of the
2351 comparison type appears to be reversed.
2352 Set *COMP_TYPE to the kind of comparison that is requested.
2354 Return true if all okay, false if input error.
2356 Used by -atime, -ctime and -mtime (parsers) to
2357 get the appropriate information for a time predicate processor. */
2360 get_num_days (char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
)
2362 boolean r
= get_num (str
, num_days
, comp_type
);
2366 case COMP_LT
: *comp_type
= COMP_GT
; break;
2367 case COMP_GT
: *comp_type
= COMP_LT
; break;
2373 /* Insert a time predicate PRED.
2374 ARGV is a pointer to the argument array.
2375 ARG_PTR is a pointer to an index into the array, incremented if
2378 Return true if input is valid, false if not.
2380 A new predicate node is assigned, along with an argument node
2381 obtained with malloc.
2383 Used by -atime, -ctime, and -mtime parsers. */
2386 insert_time (char **argv
, int *arg_ptr
, const struct parser_table
* entry
, PRED_FUNC pred
)
2388 struct predicate
*our_pred
;
2390 enum comparison_type c_type
;
2393 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2395 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
2398 /* Figure out the timestamp value we are looking for. */
2399 t
= ( options
.cur_day_start
- num_days
* DAYSECS
2400 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2404 /* We introduce a scope in which 'val' can be declared, for the
2405 * benefit of compilers that are really C89 compilers
2406 * which support intmax_t because config.h #defines it
2408 intmax_t val
= ( (intmax_t)options
.cur_day_start
- num_days
* DAYSECS
2409 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2412 /* Check for possibility of an overflow */
2413 if ( (intmax_t)t
!= val
)
2415 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv
[*arg_ptr
]);
2419 our_pred
= insert_primary_withpred (entry
, pred
);
2420 our_pred
->args
.info
.kind
= c_type
;
2421 our_pred
->args
.info
.negative
= t
< 0;
2422 our_pred
->args
.info
.l_val
= t
;
2425 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2426 fprintf (stderr
, " type: %s %s ",
2427 (c_type
== COMP_GT
) ? "gt" :
2428 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2429 (c_type
== COMP_GT
) ? " >" :
2430 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
2431 t
= our_pred
->args
.info
.l_val
;
2432 fprintf (stderr
, "%ju %s", (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2433 if (c_type
== COMP_EQ
)
2435 t
= our_pred
->args
.info
.l_val
+= DAYSECS
;
2436 fprintf (stderr
, " < %ju %s",
2437 (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2438 our_pred
->args
.info
.l_val
-= DAYSECS
;
2444 /* Get a number with comparison information.
2445 The sense of the comparison information is 'normal'; that is,
2446 '+' looks for a count > than the number and '-' less than.
2448 STR is the ASCII representation of the number.
2449 Set *NUM to the number.
2450 Set *COMP_TYPE to the kind of comparison that is requested.
2452 Return true if all okay, false if input error. */
2455 get_num (char *str
, uintmax_t *num
, enum comparison_type
*comp_type
)
2462 *comp_type
= COMP_GT
;
2466 *comp_type
= COMP_LT
;
2470 *comp_type
= COMP_EQ
;
2474 return xstrtoumax (str
, NULL
, 10, num
, "") == LONGINT_OK
;
2477 /* Insert a number predicate.
2478 ARGV is a pointer to the argument array.
2479 *ARG_PTR is an index into ARGV, incremented if all went well.
2480 *PRED is the predicate processor to insert.
2482 Return true if input is valid, false if error.
2484 A new predicate node is assigned, along with an argument node
2485 obtained with malloc.
2487 Used by -inum and -links parsers. */
2490 insert_num (char **argv
, int *arg_ptr
, const struct parser_table
*entry
)
2492 struct predicate
*our_pred
;
2494 enum comparison_type c_type
;
2496 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2498 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
2500 our_pred
= insert_primary (entry
);
2501 our_pred
->args
.info
.kind
= c_type
;
2502 our_pred
->args
.info
.l_val
= num
;
2505 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2506 fprintf (stderr
, " type: %s %s ",
2507 (c_type
== COMP_GT
) ? "gt" :
2508 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2509 (c_type
== COMP_GT
) ? " >" :
2510 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
2511 fprintf (stderr
, "%ju\n", our_pred
->args
.info
.l_val
);
2517 open_output_file (char *path
)
2521 if (!strcmp (path
, "/dev/stderr"))
2523 else if (!strcmp (path
, "/dev/stdout"))
2525 f
= fopen_safer (path
, "w");
2527 error (1, errno
, "%s", path
);