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 enum permissions_type kind
= PERM_EXACT
;
1282 struct mode_change
*change
= NULL
;
1283 struct predicate
*our_pred
;
1285 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1288 switch (argv
[*arg_ptr
][0])
1292 kind
= PERM_AT_LEAST
;
1296 change
= mode_compile (argv
[*arg_ptr
]);
1299 /* Most likely the caller is an old script that is still
1300 * using the obsolete GNU syntax '-perm +MODE'. This old
1301 * syntax was withdrawn in favor of '-perm /MODE' because
1302 * it is incompatible with POSIX in some cases, but we
1303 * still support uses of it that are not incompatible with
1311 /* This is a POSIX-compatible usage */
1317 case '/': /* GNU extension */
1323 /* For example, '-perm 0644', which is valid and matches
1324 * only files whose mode is exactly 0644.
1326 * We do nothing here, because mode_start and kind are already
1334 change
= mode_compile (argv
[*arg_ptr
] + mode_start
);
1336 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
1338 perm_val
= mode_adjust (0, change
, 0);
1341 our_pred
= insert_primary (entry
);
1343 switch (argv
[*arg_ptr
][0])
1346 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
1349 our_pred
->args
.perm
.kind
= PERM_ANY
;
1352 our_pred
->args
.perm
.kind
= PERM_EXACT
;
1355 our_pred
->args
.perm
.val
= perm_val
& MODE_ALL
;
1361 parse_print (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1363 struct predicate
*our_pred
;
1368 our_pred
= insert_primary (entry
);
1369 /* -print has the side effect of printing. This prevents us
1370 from doing undesired multiple printing when the user has
1371 already specified -print. */
1372 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1373 our_pred
->need_stat
= our_pred
->need_type
= false;
1374 our_pred
->args
.printf_vec
.segment
= NULL
;
1375 our_pred
->args
.printf_vec
.stream
= stdout
;
1376 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(stdout
);
1377 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
1383 parse_print0 (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1385 struct predicate
*our_pred
;
1390 our_pred
= insert_primary (entry
);
1391 /* -print0 has the side effect of printing. This prevents us
1392 from doing undesired multiple printing when the user has
1393 already specified -print0. */
1394 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1395 our_pred
->need_stat
= our_pred
->need_type
= false;
1400 parse_printf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1402 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1404 return insert_fprintf (stdout
, entry
, pred_fprintf
, argv
, arg_ptr
);
1408 parse_prune (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1410 struct predicate
*our_pred
;
1415 our_pred
= insert_primary (entry
);
1416 our_pred
->need_stat
= our_pred
->need_type
= false;
1417 /* -prune has a side effect that it does not descend into
1418 the current directory. */
1419 our_pred
->side_effects
= true;
1420 our_pred
->no_default_print
= false;
1425 parse_quit (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1427 struct predicate
*our_pred
= insert_primary (entry
);
1430 our_pred
->need_stat
= our_pred
->need_type
= false;
1431 our_pred
->side_effects
= true; /* Exiting is a side effect... */
1432 our_pred
->no_default_print
= false; /* Don't inhibit the default print, though. */
1438 parse_regextype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1440 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1443 /* collect the regex type name */
1444 options
.regex_options
= get_regex_type(argv
[*arg_ptr
]);
1452 parse_regex (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1454 return insert_regex (argv
, arg_ptr
, entry
, options
.regex_options
);
1458 insert_regex (char **argv
, int *arg_ptr
, const struct parser_table
*entry
, int regex_options
)
1460 struct predicate
*our_pred
;
1461 struct re_pattern_buffer
*re
;
1462 const char *error_message
;
1464 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1466 our_pred
= insert_primary_withpred (entry
, pred_regex
);
1467 our_pred
->need_stat
= our_pred
->need_type
= false;
1468 re
= (struct re_pattern_buffer
*)
1469 xmalloc (sizeof (struct re_pattern_buffer
));
1470 our_pred
->args
.regex
= re
;
1471 re
->allocated
= 100;
1472 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1475 re_set_syntax(regex_options
);
1476 re
->syntax
= regex_options
;
1477 re
->translate
= NULL
;
1479 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1482 error (1, 0, "%s", error_message
);
1488 parse_size (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1490 struct predicate
*our_pred
;
1492 enum comparison_type c_type
;
1496 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1498 len
= strlen (argv
[*arg_ptr
]);
1500 error (1, 0, _("invalid null argument to -size"));
1501 switch (argv
[*arg_ptr
][len
- 1])
1505 argv
[*arg_ptr
][len
- 1] = '\0';
1510 argv
[*arg_ptr
][len
- 1] = '\0';
1515 argv
[*arg_ptr
][len
- 1] = '\0';
1518 case 'M': /* Megabytes */
1519 blksize
= 1024*1024;
1520 argv
[*arg_ptr
][len
- 1] = '\0';
1523 case 'G': /* Gigabytes */
1524 blksize
= 1024*1024*1024;
1525 argv
[*arg_ptr
][len
- 1] = '\0';
1530 argv
[*arg_ptr
][len
- 1] = '\0';
1546 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1548 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1550 our_pred
= insert_primary (entry
);
1551 our_pred
->args
.size
.kind
= c_type
;
1552 our_pred
->args
.size
.blocksize
= blksize
;
1553 our_pred
->args
.size
.size
= num
;
1560 parse_samefile (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1562 struct predicate
*our_pred
;
1565 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1567 if ((*options
.xstat
) (argv
[*arg_ptr
], &st
))
1568 error (1, errno
, "%s", argv
[*arg_ptr
]);
1570 our_pred
= insert_primary (entry
);
1571 our_pred
->args
.fileid
.ino
= st
.st_ino
;
1572 our_pred
->args
.fileid
.dev
= st
.st_dev
;
1573 our_pred
->need_type
= false;
1574 our_pred
->need_stat
= true;
1581 parse_true (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1583 struct predicate
*our_pred
;
1588 our_pred
= insert_primary (entry
);
1589 our_pred
->need_stat
= our_pred
->need_type
= false;
1594 parse_type (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1596 return insert_type (argv
, arg_ptr
, entry
, pred_type
);
1600 parse_uid (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1602 return insert_num (argv
, arg_ptr
, entry
);
1606 parse_used (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1608 struct predicate
*our_pred
;
1610 enum comparison_type c_type
;
1613 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1615 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1617 t
= num_days
* DAYSECS
;
1618 our_pred
= insert_primary (entry
);
1619 our_pred
->args
.info
.kind
= c_type
;
1620 our_pred
->args
.info
.negative
= t
< 0;
1621 our_pred
->args
.info
.l_val
= t
;
1627 parse_user (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1629 struct passwd
*cur_pwd
;
1630 struct predicate
*our_pred
;
1634 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1636 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1638 if (cur_pwd
!= NULL
)
1639 uid
= cur_pwd
->pw_uid
;
1642 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1643 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1645 uid
= atoi (argv
[*arg_ptr
]);
1647 our_pred
= insert_primary (entry
);
1648 our_pred
->args
.uid
= uid
;
1654 parse_version (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1656 extern char *version_string
;
1664 printf (_("GNU find version %s\n"), version_string
);
1665 printf (_("Features enabled: "));
1668 printf("CACHE_IDS ");
1676 printf("DEBUG_STAT ");
1679 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1683 #if defined(O_NOFOLLOW)
1684 printf("O_NOFOLLOW(%s) ",
1685 (options
.open_nofollow_available
? "enabled" : "disabled"));
1688 #if defined(LEAF_OPTIMISATION)
1689 printf("LEAF_OPTIMISATION ");
1694 /* For the moment, leave this as English in case someone wants
1695 to parse these strings. */
1704 parse_xdev (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1709 options
.stay_on_filesystem
= true;
1714 parse_ignore_race (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1719 options
.ignore_readdir_race
= true;
1724 parse_noignore_race (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1729 options
.ignore_readdir_race
= false;
1734 parse_warn (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1739 options
.warnings
= true;
1744 parse_xtype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1748 return insert_type (argv
, arg_ptr
, entry
, pred_xtype
);
1752 insert_type (char **argv
, int *arg_ptr
, const struct parser_table
*entry
, PRED_FUNC which_pred
)
1755 struct predicate
*our_pred
;
1757 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
1758 || (strlen (argv
[*arg_ptr
]) != 1))
1760 switch (argv
[*arg_ptr
][0])
1762 case 'b': /* block special */
1763 type_cell
= S_IFBLK
;
1765 case 'c': /* character special */
1766 type_cell
= S_IFCHR
;
1768 case 'd': /* directory */
1769 type_cell
= S_IFDIR
;
1771 case 'f': /* regular file */
1772 type_cell
= S_IFREG
;
1775 case 'l': /* symbolic link */
1776 type_cell
= S_IFLNK
;
1780 case 'p': /* pipe */
1781 type_cell
= S_IFIFO
;
1785 case 's': /* socket */
1786 type_cell
= S_IFSOCK
;
1790 case 'D': /* Solaris door */
1791 type_cell
= S_IFDOOR
;
1794 default: /* None of the above ... nuke 'em. */
1797 our_pred
= insert_primary_withpred (entry
, which_pred
);
1799 /* Figure out if we will need to stat the file, because if we don't
1800 * need to follow symlinks, we can avoid a stat call by using
1801 * struct dirent.d_type.
1803 if (which_pred
== pred_xtype
)
1805 our_pred
->need_stat
= true;
1806 our_pred
->need_type
= false;
1810 our_pred
->need_stat
= false; /* struct dirent is enough */
1811 our_pred
->need_type
= true;
1813 our_pred
->args
.type
= type_cell
;
1814 (*arg_ptr
)++; /* Move on to next argument. */
1819 /* Return true if the file accessed via FP is a terminal.
1822 stream_is_tty(FILE *fp
)
1824 int fd
= fileno(fp
);
1827 return false; /* not a valid stream */
1831 return isatty(fd
) ? true : false;
1838 /* If true, we've determined that the current fprintf predicate
1839 uses stat information. */
1840 static boolean fprintf_stat_needed
;
1842 /* XXX: do we need to pass FUNC to this function? */
1844 insert_fprintf (FILE *fp
, const struct parser_table
*entry
, PRED_FUNC func
, char **argv
, int *arg_ptr
)
1846 char *format
; /* Beginning of unprocessed format string. */
1847 register char *scan
; /* Current address in scanning `format'. */
1848 register char *scan2
; /* Address inside of element being scanned. */
1849 struct segment
**segmentp
; /* Address of current segment. */
1850 struct predicate
*our_pred
;
1852 format
= argv
[(*arg_ptr
)++];
1854 fprintf_stat_needed
= false; /* Might be overridden later. */
1855 our_pred
= insert_primary_withpred (entry
, func
);
1856 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1857 our_pred
->args
.printf_vec
.stream
= fp
;
1858 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(fp
);
1859 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
1860 segmentp
= &our_pred
->args
.printf_vec
.segment
;
1863 for (scan
= format
; *scan
; scan
++)
1868 if (*scan2
>= '0' && *scan2
<= '7')
1872 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
1874 n
= 8 * n
+ *scan2
- '0';
1889 make_segment (segmentp
, format
, scan
- format
, KIND_STOP
);
1890 our_pred
->need_stat
= fprintf_stat_needed
;
1908 /* *scan = '\\'; * it already is */
1912 _("warning: unrecognized escape `\\%c'"), *scan2
);
1917 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1919 format
= scan2
+ 1; /* Move past the escape. */
1920 scan
= scan2
; /* Incremented immediately by `for'. */
1922 else if (*scan
== '%')
1926 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1932 /* Scan past flags, width and precision, to verify kind. */
1933 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
1935 while (ISDIGIT (*scan2
))
1938 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
1940 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2
))
1942 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1947 else if (strchr ("ACT", *scan2
) && scan2
[1])
1949 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1950 *scan2
| (scan2
[1] << 8));
1957 /* An unrecognized % escape. Print the char after the %. */
1958 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1960 segmentp
= make_segment (segmentp
, format
, scan
- format
,
1969 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
);
1970 our_pred
->need_type
= false;
1971 our_pred
->need_stat
= fprintf_stat_needed
;
1975 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1976 from the text in FORMAT, which has length LEN.
1977 Return the address of the `next' pointer of the new segment. */
1979 static struct segment
**
1980 make_segment (struct segment
**segment
, char *format
, int len
, int kind
)
1984 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
1986 (*segment
)->kind
= kind
;
1987 (*segment
)->next
= NULL
;
1988 (*segment
)->text_len
= len
;
1990 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
1991 strncpy (fmt
, format
, len
);
1994 switch (kind
& 0xff)
1996 case KIND_PLAIN
: /* Plain text string, no % conversion. */
1997 case KIND_STOP
: /* Terminate argument, no newline. */
2000 case 'a': /* atime in `ctime' format */
2001 case 'A': /* atime in user-specified strftime format */
2002 case 'c': /* ctime in `ctime' format */
2003 case 'C': /* ctime in user-specified strftime format */
2004 case 'F': /* filesystem type */
2005 case 'g': /* group name */
2006 case 'i': /* inode number */
2007 case 'l': /* object of symlink */
2008 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
2009 case 's': /* size in bytes */
2010 case 't': /* mtime in `ctime' format */
2011 case 'T': /* mtime in user-specified strftime format */
2012 case 'u': /* user name */
2013 case 'y': /* file type */
2014 case 'Y': /* symlink pointed file type */
2015 fprintf_stat_needed
= true;
2017 case 'f': /* basename of path */
2018 case 'h': /* leading directories part of path */
2019 case 'H': /* ARGV element file was found under */
2020 case 'p': /* pathname */
2021 case 'P': /* pathname with ARGV element stripped */
2025 /* Numeric items that one might expect to honour
2026 * #, 0, + flags but which do not.
2028 case 'G': /* GID number */
2029 case 'U': /* UID number */
2030 case 'b': /* size in 512-byte blocks */
2031 case 'D': /* Filesystem device on which the file exits */
2032 case 'k': /* size in 1K blocks */
2033 case 'n': /* number of links */
2034 fprintf_stat_needed
= true;
2038 /* Numeric items that DO honour #, 0, + flags.
2040 case 'd': /* depth in search tree (0 = ARGV element) */
2044 case 'm': /* mode as octal number (perms only) */
2046 fprintf_stat_needed
= true;
2051 return &(*segment
)->next
;
2055 check_path_safety(const char *action
)
2057 const char *path
= getenv("PATH");
2059 s
= next_element(path
, 1);
2060 while ((s
= next_element ((char *) NULL
, 1)) != NULL
)
2062 if (0 == strcmp(s
, "."))
2064 error(1, 0, _("The current directory is included in the PATH environment variable, which is insecure in combination with the %s action of find. Please remove the current directory from your $PATH (that is, remove \".\" or leading or trailing colons)"),
2071 /* handles both exec and ok predicate */
2072 #if defined(NEW_EXEC)
2073 /* handles both exec and ok predicate */
2075 new_insert_exec_ok (const char *action
,
2076 const struct parser_table
*entry
,
2080 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2081 int i
; /* Index into cmd args */
2082 int saw_braces
; /* True if previous arg was '{}'. */
2083 boolean allow_plus
; /* True if + is a valid terminator */
2084 int brace_count
; /* Number of instances of {}. */
2085 PRED_FUNC func
= entry
->pred_func
;
2087 struct predicate
*our_pred
;
2088 struct exec_val
*execp
; /* Pointer for efficiency. */
2090 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2093 our_pred
= insert_primary_withpred (entry
, func
);
2094 our_pred
->side_effects
= our_pred
->no_default_print
= true;
2095 execp
= &our_pred
->args
.exec_vec
;
2097 if ((func
!= pred_okdir
) && (func
!= pred_ok
))
2100 execp
->close_stdin
= false;
2105 /* If find reads stdin (i.e. for -ok and similar), close stdin
2106 * in the child to prevent some script from consiming the output
2107 * intended for find.
2109 execp
->close_stdin
= true;
2113 if ((func
== pred_execdir
) || (func
== pred_okdir
))
2115 options
.ignore_readdir_race
= false;
2116 check_path_safety(action
);
2117 execp
->use_current_dir
= true;
2121 execp
->use_current_dir
= false;
2124 our_pred
->args
.exec_vec
.multiple
= 0;
2126 /* Count the number of args with path replacements, up until the ';'.
2127 * Also figure out if the command is terminated by ";" or by "+".
2130 for (end
= start
, saw_braces
=0, brace_count
=0;
2132 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2135 /* For -exec and -execdir, "{} +" can terminate the command. */
2137 && argv
[end
][0] == '+' && argv
[end
][1] == 0
2140 our_pred
->args
.exec_vec
.multiple
= 1;
2145 if (strstr (argv
[end
], "{}"))
2150 if (0 == end
&& (func
== pred_execdir
|| func
== pred_okdir
))
2152 /* The POSIX standard says that {} replacement should
2153 * occur even in the utility name. This is insecure
2154 * since it means we will be executing a command whose
2155 * name is chosen according to whatever find finds in
2156 * the filesystem. That can be influenced by an
2157 * attacker. Hence for -execdir and -okdir this is not
2158 * allowed. We can specify this as those options are
2159 * not defined by POSIX.
2161 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2166 /* Fail if no command given or no semicolon found. */
2167 if ((end
== start
) || (argv
[end
] == NULL
))
2174 if (our_pred
->args
.exec_vec
.multiple
&& brace_count
> 1)
2178 if (func
== pred_execdir
)
2184 _("Only one instance of {} is supported with -exec%s ... +"),
2188 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2189 bc_init_controlinfo(&execp
->ctl
);
2190 execp
->ctl
.exec_callback
= launch
;
2192 if (our_pred
->args
.exec_vec
.multiple
)
2194 /* "+" terminator, so we can just append our arguments after the
2195 * command and initial arguments.
2197 execp
->replace_vec
= NULL
;
2198 execp
->ctl
.replace_pat
= NULL
;
2199 execp
->ctl
.rplen
= 0;
2200 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2201 execp
->ctl
.args_per_exec
= 0; /* no limit */
2203 /* remember how many arguments there are */
2204 execp
->ctl
.initial_argc
= (end
-start
) - 1;
2206 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2207 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2209 /* Gather the initial arguments. Skip the {}. */
2210 for (i
=start
; i
<end
-1; ++i
)
2212 bc_push_arg(&execp
->ctl
, &execp
->state
,
2213 argv
[i
], strlen(argv
[i
])+1,
2220 /* Semicolon terminator - more than one {} is supported, so we
2221 * have to do brace-replacement.
2223 execp
->num_args
= end
- start
;
2225 execp
->ctl
.replace_pat
= "{}";
2226 execp
->ctl
.rplen
= strlen(execp
->ctl
.replace_pat
);
2227 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2228 execp
->ctl
.args_per_exec
= 0; /* no limit */
2229 execp
->replace_vec
= xmalloc(sizeof(char*)*execp
->num_args
);
2232 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2233 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2235 /* Remember the (pre-replacement) arguments for later. */
2236 for (i
=0; i
<execp
->num_args
; ++i
)
2238 execp
->replace_vec
[i
] = argv
[i
+start
];
2242 if (argv
[end
] == NULL
)
2250 /* handles both exec and ok predicate */
2252 old_insert_exec_ok (boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
2254 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2255 int num_paths
; /* Number of args with path replacements. */
2256 int path_pos
; /* Index in array of path replacements. */
2257 int vec_pos
; /* Index in array of args. */
2258 struct predicate
*our_pred
;
2259 struct exec_val
*execp
; /* Pointer for efficiency. */
2261 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2264 /* Count the number of args with path replacements, up until the ';'. */
2266 for (end
= start
, num_paths
= 0;
2268 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2270 if (strstr (argv
[end
], "{}"))
2272 /* Fail if no command given or no semicolon found. */
2273 if ((end
== start
) || (argv
[end
] == NULL
))
2279 our_pred
= insert_primary (func
);
2280 our_pred
->side_effects
= our_pred
->no_default_print
= true;
2281 execp
= &our_pred
->args
.exec_vec
;
2282 execp
->usercontext
= our_pred
;
2283 execp
->use_current_dir
= false;
2285 (struct path_arg
*) xmalloc (sizeof (struct path_arg
) * (num_paths
+ 1));
2286 execp
->vec
= (char **) xmalloc (sizeof (char *) * (end
- start
+ 1));
2287 /* Record the positions of all args, and the args with path replacements. */
2288 for (end
= start
, path_pos
= vec_pos
= 0;
2290 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2295 execp
->paths
[path_pos
].count
= 0;
2296 for (p
= argv
[end
]; *p
; ++p
)
2297 if (p
[0] == '{' && p
[1] == '}')
2299 execp
->paths
[path_pos
].count
++;
2302 if (execp
->paths
[path_pos
].count
)
2304 execp
->paths
[path_pos
].offset
= vec_pos
;
2305 execp
->paths
[path_pos
].origarg
= argv
[end
];
2308 execp
->vec
[vec_pos
++] = argv
[end
];
2310 execp
->paths
[path_pos
].offset
= -1;
2311 execp
->vec
[vec_pos
] = NULL
;
2313 if (argv
[end
] == NULL
)
2324 insert_exec_ok (const char *action
, const struct parser_table
*entry
, char **argv
, int *arg_ptr
)
2326 #if defined(NEW_EXEC)
2327 return new_insert_exec_ok(action
, entry
, argv
, arg_ptr
);
2329 return old_insert_exec_ok(func
, argv
, arg_ptr
);
2335 /* Get a number of days and comparison type.
2336 STR is the ASCII representation.
2337 Set *NUM_DAYS to the number of days, taken as being from
2338 the current moment (or possibly midnight). Thus the sense of the
2339 comparison type appears to be reversed.
2340 Set *COMP_TYPE to the kind of comparison that is requested.
2342 Return true if all okay, false if input error.
2344 Used by -atime, -ctime and -mtime (parsers) to
2345 get the appropriate information for a time predicate processor. */
2348 get_num_days (char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
)
2350 boolean r
= get_num (str
, num_days
, comp_type
);
2354 case COMP_LT
: *comp_type
= COMP_GT
; break;
2355 case COMP_GT
: *comp_type
= COMP_LT
; break;
2361 /* Insert a time predicate PRED.
2362 ARGV is a pointer to the argument array.
2363 ARG_PTR is a pointer to an index into the array, incremented if
2366 Return true if input is valid, false if not.
2368 A new predicate node is assigned, along with an argument node
2369 obtained with malloc.
2371 Used by -atime, -ctime, and -mtime parsers. */
2374 insert_time (char **argv
, int *arg_ptr
, const struct parser_table
* entry
, PRED_FUNC pred
)
2376 struct predicate
*our_pred
;
2378 enum comparison_type c_type
;
2381 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2383 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
2386 /* Figure out the timestamp value we are looking for. */
2387 t
= ( options
.cur_day_start
- num_days
* DAYSECS
2388 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2392 /* We introduce a scope in which 'val' can be declared, for the
2393 * benefit of compilers that are really C89 compilers
2394 * which support intmax_t because config.h #defines it
2396 intmax_t val
= ( (intmax_t)options
.cur_day_start
- num_days
* DAYSECS
2397 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2400 /* Check for possibility of an overflow */
2401 if ( (intmax_t)t
!= val
)
2403 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv
[*arg_ptr
]);
2407 our_pred
= insert_primary_withpred (entry
, pred
);
2408 our_pred
->args
.info
.kind
= c_type
;
2409 our_pred
->args
.info
.negative
= t
< 0;
2410 our_pred
->args
.info
.l_val
= t
;
2413 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2414 fprintf (stderr
, " type: %s %s ",
2415 (c_type
== COMP_GT
) ? "gt" :
2416 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2417 (c_type
== COMP_GT
) ? " >" :
2418 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
2419 t
= our_pred
->args
.info
.l_val
;
2420 fprintf (stderr
, "%ju %s", (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2421 if (c_type
== COMP_EQ
)
2423 t
= our_pred
->args
.info
.l_val
+= DAYSECS
;
2424 fprintf (stderr
, " < %ju %s",
2425 (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2426 our_pred
->args
.info
.l_val
-= DAYSECS
;
2432 /* Get a number with comparison information.
2433 The sense of the comparison information is 'normal'; that is,
2434 '+' looks for a count > than the number and '-' less than.
2436 STR is the ASCII representation of the number.
2437 Set *NUM to the number.
2438 Set *COMP_TYPE to the kind of comparison that is requested.
2440 Return true if all okay, false if input error. */
2443 get_num (char *str
, uintmax_t *num
, enum comparison_type
*comp_type
)
2450 *comp_type
= COMP_GT
;
2454 *comp_type
= COMP_LT
;
2458 *comp_type
= COMP_EQ
;
2462 return xstrtoumax (str
, NULL
, 10, num
, "") == LONGINT_OK
;
2465 /* Insert a number predicate.
2466 ARGV is a pointer to the argument array.
2467 *ARG_PTR is an index into ARGV, incremented if all went well.
2468 *PRED is the predicate processor to insert.
2470 Return true if input is valid, false if error.
2472 A new predicate node is assigned, along with an argument node
2473 obtained with malloc.
2475 Used by -inum and -links parsers. */
2478 insert_num (char **argv
, int *arg_ptr
, const struct parser_table
*entry
)
2480 struct predicate
*our_pred
;
2482 enum comparison_type c_type
;
2484 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2486 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
2488 our_pred
= insert_primary (entry
);
2489 our_pred
->args
.info
.kind
= c_type
;
2490 our_pred
->args
.info
.l_val
= num
;
2493 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2494 fprintf (stderr
, " type: %s %s ",
2495 (c_type
== COMP_GT
) ? "gt" :
2496 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2497 (c_type
== COMP_GT
) ? " >" :
2498 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
2499 fprintf (stderr
, "%ju\n", our_pred
->args
.info
.l_val
);
2505 open_output_file (char *path
)
2509 if (!strcmp (path
, "/dev/stderr"))
2511 else if (!strcmp (path
, "/dev/stdout"))
2513 f
= fopen_safer (path
, "w");
2515 error (1, errno
, "%s", path
);