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"
43 /* The presence of unistd.h is assumed by gnulib these days, so we
44 * might as well assume it too.
46 /* We need <unistd.h> for isatty(). */
51 # define _(Text) gettext (Text)
56 # define N_(String) gettext_noop (String)
58 /* See locate.c for explanation as to why not use (String) */
59 # define N_(String) String
62 #if !defined (isascii) || defined (STDC_HEADERS)
69 #define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
70 #define ISUPPER(c) (isascii ((unsigned char)c) && isupper ((unsigned char)c))
79 static boolean parse_amin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
80 static boolean parse_and
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
81 static boolean parse_anewer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
82 static boolean parse_atime
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
83 static boolean parse_cmin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
84 static boolean parse_cnewer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
85 static boolean parse_comma
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
86 static boolean parse_ctime
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
87 static boolean parse_daystart
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
88 static boolean parse_delete
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
89 static boolean parse_d
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
90 static boolean parse_depth
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
91 static boolean parse_empty
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
92 static boolean parse_exec
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
93 static boolean parse_execdir
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
94 static boolean parse_false
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
95 static boolean parse_fls
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
96 static boolean parse_fprintf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
97 static boolean parse_follow
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
98 static boolean parse_fprint
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
99 static boolean parse_fprint0
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
100 static boolean parse_fstype
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
101 static boolean parse_gid
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
102 static boolean parse_group
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
103 static boolean parse_help
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
104 static boolean parse_ilname
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
105 static boolean parse_iname
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
106 static boolean parse_inum
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
107 static boolean parse_ipath
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
108 static boolean parse_iregex
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
109 static boolean parse_iwholename
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
110 static boolean parse_links
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
111 static boolean parse_lname
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
112 static boolean parse_ls
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
113 static boolean parse_maxdepth
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
114 static boolean parse_mindepth
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
115 static boolean parse_mmin
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
116 static boolean parse_mtime
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
117 static boolean parse_name
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
118 static boolean parse_negate
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
119 static boolean parse_newer
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
120 static boolean parse_noleaf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
121 static boolean parse_nogroup
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
122 static boolean parse_nouser
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
123 static boolean parse_nowarn
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
124 static boolean parse_ok
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
125 static boolean parse_okdir
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
126 static boolean parse_or
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
127 static boolean parse_path
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
128 static boolean parse_perm
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
129 static boolean parse_print0
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
130 static boolean parse_printf
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
131 static boolean parse_prune
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
132 static boolean parse_regex
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
133 static boolean parse_samefile
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
134 static boolean parse_size
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
135 static boolean parse_true
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
136 static boolean parse_type
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
137 static boolean parse_uid
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
138 static boolean parse_used
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
139 static boolean parse_user
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
140 static boolean parse_version
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
141 static boolean parse_wholename
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
142 static boolean parse_xdev
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
143 static boolean parse_ignore_race
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
144 static boolean parse_noignore_race
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
145 static boolean parse_warn
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
146 static boolean parse_xtype
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
147 static boolean parse_quit
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
151 boolean parse_print
PARAMS((const struct parser_table
*, char *argv
[], int *arg_ptr
));
152 boolean parse_open
PARAMS((const struct parser_table
* entry
, char *argv
[], int *arg_ptr
));
153 boolean parse_close
PARAMS((const struct parser_table
* entry
, char *argv
[], int *arg_ptr
));
157 static boolean insert_type
PARAMS((char **argv
, int *arg_ptr
, const struct parser_table
*entry
, PRED_FUNC which_pred
));
158 static boolean insert_regex
PARAMS((char *argv
[], int *arg_ptr
, const struct parser_table
*entry
, int regex_options
));
159 static boolean insert_fprintf
PARAMS((FILE *fp
, const struct parser_table
*entry
, PRED_FUNC func
, char *argv
[], int *arg_ptr
));
160 static struct segment
**make_segment
PARAMS((struct segment
**segment
, char *format
, int len
, int kind
));
161 static boolean insert_exec_ok
PARAMS((const char *action
, const struct parser_table
*entry
, char *argv
[], int *arg_ptr
));
162 static boolean get_num_days
PARAMS((char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
));
163 static boolean insert_time
PARAMS((char **argv
, int *arg_ptr
, const struct parser_table
* entry
, PRED_FUNC pred
));
164 static boolean get_num
PARAMS((char *str
, uintmax_t *num
, enum comparison_type
*comp_type
));
165 static boolean insert_num
PARAMS((char *argv
[], int *arg_ptr
, const struct parser_table
*entry
));
166 static FILE *open_output_file
PARAMS((char *path
));
167 static boolean
stream_is_tty(FILE *fp
);
170 char *find_pred_name
PARAMS((PRED_FUNC pred_func
));
174 #define PASTE(x,y) x##y
175 #define STRINGIFY(s) #s
177 #define PARSE_OPTION(what,suffix) \
178 { (ARG_OPTION), (what), PASTE(parse_,suffix), NULL }
180 #define PARSE_POSOPT(what,suffix) \
181 { (ARG_POSITIONAL_OPTION), (what), PASTE(parse_,suffix), NULL }
183 #define PARSE_TEST(what,suffix) \
184 { (ARG_TEST), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
186 #define PARSE_TEST_NP(what,suffix) \
187 { (ARG_TEST), (what), PASTE(parse_,suffix), NULL }
189 #define PARSE_ACTION(what,suffix) \
190 { (ARG_ACTION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
192 #define PARSE_ACTION_NP(what,suffix) \
193 { (ARG_ACTION), (what), PASTE(parse_,suffix), NULL }
195 #define PARSE_PUNCTUATION(what,suffix) \
196 { (ARG_PUNCTUATION), (what), PASTE(parse_,suffix), PASTE(pred_,suffix) }
199 /* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'.
200 If they are in some Unix versions of find, they are marked `Unix'. */
202 static struct parser_table
const parse_table
[] =
204 PARSE_PUNCTUATION("!", negate
),
205 PARSE_PUNCTUATION("not", negate
), /* GNU */
206 PARSE_PUNCTUATION("(", open
),
207 PARSE_PUNCTUATION(")", close
),
208 PARSE_PUNCTUATION(",", comma
), /* GNU */
209 PARSE_PUNCTUATION("a", and),
210 PARSE_TEST ("amin", amin
), /* GNU */
211 PARSE_PUNCTUATION("and", and), /* GNU */
212 PARSE_TEST ("anewer", anewer
), /* GNU */
213 PARSE_TEST ("atime", atime
),
214 PARSE_TEST ("cmin", cmin
), /* GNU */
215 PARSE_TEST ("cnewer", cnewer
), /* GNU */
216 PARSE_TEST ("ctime", ctime
),
217 PARSE_POSOPT ("daystart", daystart
), /* GNU */
218 PARSE_ACTION ("delete", delete), /* GNU, Mac OS, FreeBSD */
219 PARSE_OPTION ("d", d
), /* Mac OS X, FreeBSD, NetBSD, OpenBSD, but deprecated in favour of -depth */
220 PARSE_OPTION ("depth", depth
),
221 PARSE_TEST ("empty", empty
), /* GNU */
222 PARSE_ACTION ("exec", exec
),
223 PARSE_ACTION ("execdir", execdir
), /* *BSD, GNU */
224 PARSE_ACTION ("fls", fls
), /* GNU */
225 PARSE_POSOPT ("follow", follow
), /* GNU, Unix */
226 PARSE_ACTION ("fprint", fprint
), /* GNU */
227 PARSE_ACTION ("fprint0", fprint0
), /* GNU */
228 PARSE_ACTION ("fprintf", fprintf
), /* GNU */
229 PARSE_TEST ("fstype", fstype
), /* GNU, Unix */
230 PARSE_TEST ("gid", gid
), /* GNU */
231 PARSE_TEST ("group", group
),
232 PARSE_OPTION ("ignore_readdir_race", ignore_race
), /* GNU */
233 PARSE_TEST ("ilname", ilname
), /* GNU */
234 PARSE_TEST ("iname", iname
), /* GNU */
235 PARSE_TEST ("inum", inum
), /* GNU, Unix */
236 PARSE_TEST ("ipath", ipath
), /* GNU, deprecated in favour of iwholename */
237 PARSE_TEST_NP ("iregex", iregex
), /* GNU */
238 PARSE_TEST_NP ("iwholename", iwholename
), /* GNU */
239 PARSE_TEST ("links", links
),
240 PARSE_TEST ("lname", lname
), /* GNU */
241 PARSE_ACTION ("ls", ls
), /* GNU, Unix */
242 PARSE_OPTION ("maxdepth", maxdepth
), /* GNU */
243 PARSE_OPTION ("mindepth", mindepth
), /* GNU */
244 PARSE_TEST ("mmin", mmin
), /* GNU */
245 PARSE_OPTION ("mount", xdev
), /* Unix */
246 PARSE_TEST ("mtime", mtime
),
247 PARSE_TEST ("name", name
),
248 #ifdef UNIMPLEMENTED_UNIX
249 PARSE(ARG_UNIMPLEMENTED
, "ncpio", ncpio
), /* Unix */
251 PARSE_TEST ("newer", newer
),
252 PARSE_OPTION ("noleaf", noleaf
), /* GNU */
253 PARSE_TEST ("nogroup", nogroup
),
254 PARSE_TEST ("nouser", nouser
),
255 PARSE_OPTION ("noignore_readdir_race", noignore_race
), /* GNU */
256 PARSE_OPTION ("nowarn", nowarn
), /* GNU */
257 PARSE_PUNCTUATION("o", or),
258 PARSE_PUNCTUATION("or", or), /* GNU */
259 PARSE_ACTION ("ok", ok
),
260 PARSE_ACTION ("okdir", okdir
), /* GNU (-execdir is BSD) */
261 PARSE_TEST ("path", path
), /* GNU, HP-UX, GNU prefers wholename */
262 PARSE_TEST ("perm", perm
),
263 PARSE_ACTION ("print", print
),
264 PARSE_ACTION ("print0", print0
), /* GNU */
265 PARSE_ACTION_NP ("printf", printf
), /* GNU */
266 PARSE_ACTION ("prune", prune
),
267 PARSE_ACTION ("quit", quit
), /* GNU */
268 PARSE_TEST ("regex", regex
), /* GNU */
269 PARSE_TEST ("samefile", samefile
), /* GNU */
270 PARSE_TEST ("size", size
),
271 PARSE_TEST ("type", type
),
272 PARSE_TEST ("uid", uid
), /* GNU */
273 PARSE_TEST ("used", used
), /* GNU */
274 PARSE_TEST ("user", user
),
275 PARSE_OPTION ("warn", warn
), /* GNU */
276 PARSE_TEST_NP ("wholename", wholename
), /* GNU, replaces -path */
277 PARSE_OPTION ("xdev", xdev
),
278 PARSE_TEST ("xtype", xtype
), /* GNU */
279 #ifdef UNIMPLEMENTED_UNIX
280 /* It's pretty ugly for find to know about archive formats.
281 Plus what it could do with cpio archives is very limited.
282 Better to leave it out. */
283 PARSE(ARG_UNIMPLEMENTED
, "cpio", cpio
), /* Unix */
285 /* gnulib's stdbool.h might have made true and false into macros,
286 * so we can't leave named 'true' and 'false' tokens, so we have
287 * to expeant the relevant entries longhand.
289 {ARG_TEST
, "false", parse_false
, pred_false
}, /* GNU */
290 {ARG_TEST
, "true", parse_true
, pred_true
}, /* GNU */
292 /* Various other cases that don't fit neatly into our macro scheme. */
293 {ARG_TEST
, "help", parse_help
, NULL
}, /* GNU */
294 {ARG_TEST
, "-help", parse_help
, NULL
}, /* GNU */
295 {ARG_TEST
, "version", parse_version
, NULL
}, /* GNU */
296 {ARG_TEST
, "-version", parse_version
, NULL
}, /* GNU */
301 static const char *first_nonoption_arg
= NULL
;
303 /* Return a pointer to the parser function to invoke for predicate
305 Return NULL if SEARCH_NAME is not a valid predicate name. */
307 const struct parser_table
*
308 find_parser (char *search_name
)
311 const char *original_arg
= search_name
;
313 if (*search_name
== '-')
315 for (i
= 0; parse_table
[i
].parser_name
!= 0; i
++)
317 if (strcmp (parse_table
[i
].parser_name
, search_name
) == 0)
319 /* If this is an option, but we have already had a
320 * non-option argument, the user may be under the
321 * impression that the behaviour of the option
322 * argument is conditional on some preceding
323 * tests. This might typically be the case with,
324 * for example, -maxdepth.
326 * The options -daystart and -follow are exempt
327 * from this treatment, since their positioning
328 * in the command line does have an effect on
329 * subsequent tests but not previous ones. That
330 * might be intentional on the part of the user.
332 if (parse_table
[i
].type
!= ARG_POSITIONAL_OPTION
)
334 /* Something other than -follow/-daystart.
335 * If this is an option, check if it followed
336 * a non-option and if so, issue a warning.
338 if (parse_table
[i
].type
== ARG_OPTION
)
340 if ((first_nonoption_arg
!= NULL
)
341 && options
.warnings
)
343 /* option which follows a non-option */
345 _("warning: you have specified the %s "
346 "option after a non-option argument %s, "
347 "but options are not positional (%s affects "
348 "tests specified before it as well as those "
349 "specified after it). Please specify options "
350 "before other arguments.\n"),
358 /* Not an option or a positional option,
359 * so remember we've seen it in order to
360 * use it in a possible future warning message.
362 if (first_nonoption_arg
== NULL
)
364 first_nonoption_arg
= original_arg
;
369 return &parse_table
[i
];
375 /* The parsers are responsible to continue scanning ARGV for
376 their arguments. Each parser knows what is and isn't
379 ARGV is the argument array.
380 *ARG_PTR is the index to start at in ARGV,
381 updated to point beyond the last element consumed.
383 The predicate structure is updated with the new information. */
386 parse_amin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
388 struct predicate
*our_pred
;
390 enum comparison_type c_type
;
393 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
395 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
397 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
398 our_pred
= insert_primary (entry
);
399 our_pred
->args
.info
.kind
= c_type
;
400 our_pred
->args
.info
.negative
= t
< 0;
401 our_pred
->args
.info
.l_val
= t
;
407 parse_and (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
409 struct predicate
*our_pred
;
414 our_pred
= get_new_pred (entry
);
415 our_pred
->pred_func
= pred_and
;
417 our_pred
->p_name
= find_pred_name (pred_and
);
419 our_pred
->p_type
= BI_OP
;
420 our_pred
->p_prec
= AND_PREC
;
421 our_pred
->need_stat
= our_pred
->need_type
= false;
426 parse_anewer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
428 struct predicate
*our_pred
;
429 struct stat stat_newer
;
431 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
433 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
434 error (1, errno
, "%s", argv
[*arg_ptr
]);
435 our_pred
= insert_primary (entry
);
436 our_pred
->args
.time
= stat_newer
.st_mtime
;
442 parse_atime (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
444 return insert_time (argv
, arg_ptr
, entry
, pred_atime
);
448 parse_close (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
450 struct predicate
*our_pred
;
455 our_pred
= get_new_pred (entry
);
456 our_pred
->pred_func
= pred_close
;
458 our_pred
->p_name
= find_pred_name (pred_close
);
460 our_pred
->p_type
= CLOSE_PAREN
;
461 our_pred
->p_prec
= NO_PREC
;
462 our_pred
->need_stat
= our_pred
->need_type
= false;
467 parse_cmin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
469 struct predicate
*our_pred
;
471 enum comparison_type c_type
;
474 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
476 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
478 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
479 our_pred
= insert_primary (entry
);
480 our_pred
->args
.info
.kind
= c_type
;
481 our_pred
->args
.info
.negative
= t
< 0;
482 our_pred
->args
.info
.l_val
= t
;
488 parse_cnewer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
490 struct predicate
*our_pred
;
491 struct stat stat_newer
;
493 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
495 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
496 error (1, errno
, "%s", argv
[*arg_ptr
]);
497 our_pred
= insert_primary (entry
);
498 our_pred
->args
.time
= stat_newer
.st_mtime
;
504 parse_comma (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
506 struct predicate
*our_pred
;
511 our_pred
= get_new_pred (entry
);
512 our_pred
->pred_func
= pred_comma
;
514 our_pred
->p_name
= find_pred_name (pred_comma
);
516 our_pred
->p_type
= BI_OP
;
517 our_pred
->p_prec
= COMMA_PREC
;
518 our_pred
->need_stat
= our_pred
->need_type
= false;
523 parse_ctime (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
525 return insert_time (argv
, arg_ptr
, entry
, pred_ctime
);
529 parse_daystart (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
537 if (options
.full_days
== false)
539 options
.cur_day_start
+= DAYSECS
;
540 local
= localtime (&options
.cur_day_start
);
541 options
.cur_day_start
-= (local
542 ? (local
->tm_sec
+ local
->tm_min
* 60
543 + local
->tm_hour
* 3600)
544 : options
.cur_day_start
% DAYSECS
);
545 options
.full_days
= true;
551 parse_delete (const struct parser_table
* entry
, char *argv
[], int *arg_ptr
)
553 struct predicate
*our_pred
;
557 our_pred
= insert_primary (entry
);
558 our_pred
->side_effects
= our_pred
->no_default_print
= true;
559 /* -delete implies -depth */
560 options
.do_dir_first
= false;
565 parse_depth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
571 options
.do_dir_first
= false;
576 parse_d (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
581 if (options
.warnings
)
584 _("warning: the -d option is deprecated; please use -depth instead, because the latter is a POSIX-compliant feature."));
586 return parse_depth(entry
, argv
, arg_ptr
);
590 parse_empty (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
595 insert_primary (entry
);
600 parse_exec (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
602 return insert_exec_ok ("-exec", entry
, argv
, arg_ptr
);
606 parse_execdir (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
608 return insert_exec_ok ("-execdir", entry
, argv
, arg_ptr
);
612 parse_false (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
614 struct predicate
*our_pred
;
619 our_pred
= insert_primary (entry
);
620 our_pred
->need_stat
= our_pred
->need_type
= false;
621 our_pred
->side_effects
= our_pred
->no_default_print
= false;
626 parse_fls (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
628 struct predicate
*our_pred
;
630 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
632 our_pred
= insert_primary (entry
);
633 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
634 our_pred
->side_effects
= our_pred
->no_default_print
= true;
640 parse_fprintf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
644 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
646 if (argv
[*arg_ptr
+ 1] == NULL
)
648 /* Ensure we get "missing arg" message, not "invalid arg". */
652 fp
= open_output_file (argv
[*arg_ptr
]);
654 return insert_fprintf (fp
, entry
, pred_fprintf
, argv
, arg_ptr
);
658 parse_follow (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
664 set_follow_state(SYMLINK_ALWAYS_DEREF
);
669 parse_fprint (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
671 struct predicate
*our_pred
;
673 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
675 our_pred
= insert_primary (entry
);
676 our_pred
->args
.printf_vec
.segment
= NULL
;
677 our_pred
->args
.printf_vec
.stream
= open_output_file (argv
[*arg_ptr
]);
678 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(our_pred
->args
.printf_vec
.stream
);
679 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
680 our_pred
->side_effects
= our_pred
->no_default_print
= true;
681 our_pred
->need_stat
= our_pred
->need_type
= false;
687 parse_fprint0 (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
689 struct predicate
*our_pred
;
691 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
693 our_pred
= insert_primary (entry
);
694 our_pred
->args
.stream
= open_output_file (argv
[*arg_ptr
]);
695 our_pred
->side_effects
= our_pred
->no_default_print
= true;
696 our_pred
->need_stat
= our_pred
->need_type
= false;
702 parse_fstype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
704 struct predicate
*our_pred
;
706 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
708 our_pred
= insert_primary (entry
);
709 our_pred
->args
.str
= argv
[*arg_ptr
];
715 parse_gid (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
717 return insert_num (argv
, arg_ptr
, entry
);
721 parse_group (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
723 struct group
*cur_gr
;
724 struct predicate
*our_pred
;
728 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
730 cur_gr
= getgrnam (argv
[*arg_ptr
]);
733 gid
= cur_gr
->gr_gid
;
736 gid_len
= strspn (argv
[*arg_ptr
], "0123456789");
737 if ((gid_len
== 0) || (argv
[*arg_ptr
][gid_len
] != '\0'))
739 gid
= atoi (argv
[*arg_ptr
]);
741 our_pred
= insert_primary (entry
);
742 our_pred
->args
.gid
= gid
;
748 parse_help (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
755 Usage: %s [path...] [expression]\n"), program_name
);
757 default path is the current directory; default expression is -print\n\
758 expression may consist of: operators, options, tests, and actions:\n"));
760 operators (decreasing precedence; -and is implicit where no others are given):\n\
761 ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n\
762 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n"));
764 positional options (always true): -daystart -follow\n\
765 normal options (always true, specified before other expressions):\n\
766 -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf\n\
767 --version -xdev -ignore_readdir_race -noignore_readdir_race\n"));
769 tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
770 -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\
771 -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN\n\
772 -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE"));
774 -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
775 -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
776 -used N -user NAME -xtype [bcdpfls]\n"));
778 actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
779 -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
780 -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;\n\
781 -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;\n\
783 puts (_("Report (and track progress on fixing) bugs via the findutils bug-reporting\n\
784 page at http://savannah.gnu.org/ or, if you have no web access, by sending\n\
785 email to <bug-findutils@gnu.org>."));
790 parse_ilname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
792 struct predicate
*our_pred
;
794 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
796 our_pred
= insert_primary (entry
);
797 our_pred
->args
.str
= argv
[*arg_ptr
];
803 /* sanity check the fnmatch() function to make sure
804 * it really is the GNU version.
807 fnmatch_sanitycheck(void)
809 /* fprintf(stderr, "Performing find sanity check..."); */
810 if (0 != fnmatch("foo", "foo", 0)
811 || 0 == fnmatch("Foo", "foo", 0)
812 || 0 != fnmatch("Foo", "foo", FNM_CASEFOLD
))
814 error (1, 0, _("sanity check of the fnmatch() library function failed."));
815 /* fprintf(stderr, "FAILED\n"); */
819 /* fprintf(stderr, "OK\n"); */
825 check_name_arg(const char *pred
, const char *arg
)
827 if (strchr(arg
, '/'))
829 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'."),
832 return true; /* allow it anyway */
838 parse_iname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
840 struct predicate
*our_pred
;
842 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
844 if (!check_name_arg("-iname", argv
[*arg_ptr
]))
847 fnmatch_sanitycheck();
849 our_pred
= insert_primary (entry
);
850 our_pred
->need_stat
= our_pred
->need_type
= false;
851 our_pred
->args
.str
= argv
[*arg_ptr
];
857 parse_inum (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
859 return insert_num (argv
, arg_ptr
, entry
);
862 /* -ipath is deprecated (at RMS's request) in favour of
863 * -iwholename. See the node "GNU Manuals" in standards.texi
864 * for the rationale for this (basically, GNU prefers the use
865 * of the phrase "file name" to "path name"
868 parse_ipath (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
871 _("warning: the predicate -ipath is deprecated; please use -iwholename instead."));
873 return parse_iwholename(entry
, argv
, arg_ptr
);
877 parse_iwholename (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
879 struct predicate
*our_pred
;
881 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
884 fnmatch_sanitycheck();
886 our_pred
= insert_primary_withpred (entry
, pred_ipath
);
887 our_pred
->need_stat
= our_pred
->need_type
= false;
888 our_pred
->args
.str
= argv
[*arg_ptr
];
894 parse_iregex (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
896 return insert_regex (argv
, arg_ptr
, entry
, RE_ICASE
|options
.regex_options
);
900 parse_links (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
902 return insert_num (argv
, arg_ptr
, entry
);
906 parse_lname (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
908 struct predicate
*our_pred
;
913 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
916 fnmatch_sanitycheck();
918 our_pred
= insert_primary (entry
);
919 our_pred
->args
.str
= argv
[*arg_ptr
];
925 parse_ls (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
927 struct predicate
*our_pred
;
932 our_pred
= insert_primary (entry
);
933 our_pred
->side_effects
= our_pred
->no_default_print
= true;
938 parse_maxdepth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
943 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
945 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
946 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
948 options
.maxdepth
= atoi (argv
[*arg_ptr
]);
949 if (options
.maxdepth
< 0)
956 parse_mindepth (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
961 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
963 depth_len
= strspn (argv
[*arg_ptr
], "0123456789");
964 if ((depth_len
== 0) || (argv
[*arg_ptr
][depth_len
] != '\0'))
966 options
.mindepth
= atoi (argv
[*arg_ptr
]);
967 if (options
.mindepth
< 0)
974 parse_mmin (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
976 struct predicate
*our_pred
;
978 enum comparison_type c_type
;
981 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
983 if (!get_num_days (argv
[*arg_ptr
], &num
, &c_type
))
985 t
= options
.cur_day_start
+ DAYSECS
- num
* 60;
986 our_pred
= insert_primary (entry
);
987 our_pred
->args
.info
.kind
= c_type
;
988 our_pred
->args
.info
.negative
= t
< 0;
989 our_pred
->args
.info
.l_val
= t
;
995 parse_mtime (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
997 return insert_time (argv
, arg_ptr
, entry
, pred_mtime
);
1001 parse_name (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1003 struct predicate
*our_pred
;
1008 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1010 if (!check_name_arg("-name", argv
[*arg_ptr
]))
1012 fnmatch_sanitycheck();
1014 our_pred
= insert_primary (entry
);
1015 our_pred
->need_stat
= our_pred
->need_type
= false;
1016 our_pred
->args
.str
= argv
[*arg_ptr
];
1022 parse_negate (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1024 struct predicate
*our_pred
;
1029 our_pred
= get_new_pred_chk_op (entry
);
1030 our_pred
->pred_func
= pred_negate
;
1032 our_pred
->p_name
= find_pred_name (pred_negate
);
1034 our_pred
->p_type
= UNI_OP
;
1035 our_pred
->p_prec
= NEGATE_PREC
;
1036 our_pred
->need_stat
= our_pred
->need_type
= false;
1041 parse_newer (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1043 struct predicate
*our_pred
;
1044 struct stat stat_newer
;
1049 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1051 if ((*options
.xstat
) (argv
[*arg_ptr
], &stat_newer
))
1052 error (1, errno
, "%s", argv
[*arg_ptr
]);
1053 our_pred
= insert_primary (entry
);
1054 our_pred
->args
.time
= stat_newer
.st_mtime
;
1060 parse_noleaf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1066 options
.no_leaf_check
= true;
1071 /* Arbitrary amount by which to increase size
1072 of `uid_unused' and `gid_unused'. */
1073 #define ALLOC_STEP 2048
1075 /* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */
1076 char *uid_unused
= NULL
;
1078 /* Number of elements in `uid_unused'. */
1079 unsigned uid_allocated
;
1081 /* Similar for GIDs and group entries. */
1082 char *gid_unused
= NULL
;
1083 unsigned gid_allocated
;
1087 parse_nogroup (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1089 struct predicate
*our_pred
;
1094 our_pred
= insert_primary (entry
);
1096 if (gid_unused
== NULL
)
1100 gid_allocated
= ALLOC_STEP
;
1101 gid_unused
= xmalloc (gid_allocated
);
1102 memset (gid_unused
, 1, gid_allocated
);
1104 while ((gr
= getgrent ()) != NULL
)
1106 if ((unsigned) gr
->gr_gid
>= gid_allocated
)
1108 unsigned new_allocated
= (unsigned) gr
->gr_gid
+ ALLOC_STEP
;
1109 gid_unused
= xrealloc (gid_unused
, new_allocated
);
1110 memset (gid_unused
+ gid_allocated
, 1,
1111 new_allocated
- gid_allocated
);
1112 gid_allocated
= new_allocated
;
1114 gid_unused
[(unsigned) gr
->gr_gid
] = 0;
1123 parse_nouser (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1125 struct predicate
*our_pred
;
1130 our_pred
= insert_primary (entry
);
1132 if (uid_unused
== NULL
)
1136 uid_allocated
= ALLOC_STEP
;
1137 uid_unused
= xmalloc (uid_allocated
);
1138 memset (uid_unused
, 1, uid_allocated
);
1140 while ((pw
= getpwent ()) != NULL
)
1142 if ((unsigned) pw
->pw_uid
>= uid_allocated
)
1144 unsigned new_allocated
= (unsigned) pw
->pw_uid
+ ALLOC_STEP
;
1145 uid_unused
= xrealloc (uid_unused
, new_allocated
);
1146 memset (uid_unused
+ uid_allocated
, 1,
1147 new_allocated
- uid_allocated
);
1148 uid_allocated
= new_allocated
;
1150 uid_unused
[(unsigned) pw
->pw_uid
] = 0;
1159 parse_nowarn (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1165 options
.warnings
= false;
1170 parse_ok (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1172 return insert_exec_ok ("-ok", entry
, argv
, arg_ptr
);
1176 parse_okdir (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1178 return insert_exec_ok ("-okdir", entry
, argv
, arg_ptr
);
1182 parse_open (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1184 struct predicate
*our_pred
;
1189 our_pred
= get_new_pred_chk_op (entry
);
1190 our_pred
->pred_func
= pred_open
;
1192 our_pred
->p_name
= find_pred_name (pred_open
);
1194 our_pred
->p_type
= OPEN_PAREN
;
1195 our_pred
->p_prec
= NO_PREC
;
1196 our_pred
->need_stat
= our_pred
->need_type
= false;
1201 parse_or (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1203 struct predicate
*our_pred
;
1208 our_pred
= get_new_pred (entry
);
1209 our_pred
->pred_func
= pred_or
;
1211 our_pred
->p_name
= find_pred_name (pred_or
);
1213 our_pred
->p_type
= BI_OP
;
1214 our_pred
->p_prec
= OR_PREC
;
1215 our_pred
->need_stat
= our_pred
->need_type
= false;
1219 /* -path is deprecated (at RMS's request) in favour of
1220 * -iwholename. See the node "GNU Manuals" in standards.texi
1221 * for the rationale for this (basically, GNU prefers the use
1222 * of the phrase "file name" to "path name".
1224 * We do not issue a warning that this usage is deprecated
1225 * since HPUX find supports this predicate also.
1228 parse_path (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1230 return parse_wholename(entry
, argv
, arg_ptr
);
1234 parse_wholename (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1236 struct predicate
*our_pred
;
1238 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1240 our_pred
= insert_primary_withpred (entry
, pred_path
);
1241 our_pred
->need_stat
= our_pred
->need_type
= false;
1242 our_pred
->args
.str
= argv
[*arg_ptr
];
1248 parse_perm (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1252 enum permissions_type kind
= PERM_EXACT
;
1253 struct mode_change
*change
= NULL
;
1254 struct predicate
*our_pred
;
1256 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1259 switch (argv
[*arg_ptr
][0])
1263 kind
= PERM_AT_LEAST
;
1267 change
= mode_compile (argv
[*arg_ptr
]);
1270 /* Most likely the caller is an old script that is still
1271 * using the obsolete GNU syntax '-perm +MODE'. This old
1272 * syntax was withdrawn in favor of '-perm /MODE' because
1273 * it is incompatible with POSIX in some cases, but we
1274 * still support uses of it that are not incompatible with
1282 /* This is a POSIX-compatible usage */
1288 case '/': /* GNU extension */
1294 /* For example, '-perm 0644', which is valid and matches
1295 * only files whose mode is exactly 0644.
1297 * We do nothing here, because mode_start and kind are already
1305 change
= mode_compile (argv
[*arg_ptr
] + mode_start
);
1307 error (1, 0, _("invalid mode `%s'"), argv
[*arg_ptr
]);
1309 perm_val
= mode_adjust (0, change
, 0);
1312 our_pred
= insert_primary (entry
);
1314 switch (argv
[*arg_ptr
][0])
1317 our_pred
->args
.perm
.kind
= PERM_AT_LEAST
;
1320 our_pred
->args
.perm
.kind
= PERM_ANY
;
1323 our_pred
->args
.perm
.kind
= PERM_EXACT
;
1326 our_pred
->args
.perm
.val
= perm_val
& MODE_ALL
;
1332 parse_print (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1334 struct predicate
*our_pred
;
1339 our_pred
= insert_primary (entry
);
1340 /* -print has the side effect of printing. This prevents us
1341 from doing undesired multiple printing when the user has
1342 already specified -print. */
1343 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1344 our_pred
->need_stat
= our_pred
->need_type
= false;
1345 our_pred
->args
.printf_vec
.segment
= NULL
;
1346 our_pred
->args
.printf_vec
.stream
= stdout
;
1347 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(stdout
);
1348 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
1354 parse_print0 (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1356 struct predicate
*our_pred
;
1361 our_pred
= insert_primary (entry
);
1362 /* -print0 has the side effect of printing. This prevents us
1363 from doing undesired multiple printing when the user has
1364 already specified -print0. */
1365 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1366 our_pred
->need_stat
= our_pred
->need_type
= false;
1371 parse_printf (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1373 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1375 return insert_fprintf (stdout
, entry
, pred_fprintf
, argv
, arg_ptr
);
1379 parse_prune (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1381 struct predicate
*our_pred
;
1386 our_pred
= insert_primary (entry
);
1387 our_pred
->need_stat
= our_pred
->need_type
= false;
1388 /* -prune has a side effect that it does not descend into
1389 the current directory. */
1390 our_pred
->side_effects
= true;
1391 our_pred
->no_default_print
= false;
1396 parse_quit (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1398 struct predicate
*our_pred
= insert_primary (entry
);
1401 our_pred
->need_stat
= our_pred
->need_type
= false;
1402 our_pred
->side_effects
= true; /* Exiting is a side effect... */
1403 our_pred
->no_default_print
= false; /* Don't inhibit the default print, though. */
1409 parse_regex (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1411 return insert_regex (argv
, arg_ptr
, entry
, options
.regex_options
);
1415 insert_regex (char **argv
, int *arg_ptr
, const struct parser_table
*entry
, int regex_options
)
1417 struct predicate
*our_pred
;
1418 struct re_pattern_buffer
*re
;
1419 const char *error_message
;
1421 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1423 our_pred
= insert_primary_withpred (entry
, pred_regex
);
1424 our_pred
->need_stat
= our_pred
->need_type
= false;
1425 re
= (struct re_pattern_buffer
*)
1426 xmalloc (sizeof (struct re_pattern_buffer
));
1427 our_pred
->args
.regex
= re
;
1428 re
->allocated
= 100;
1429 re
->buffer
= (unsigned char *) xmalloc (re
->allocated
);
1432 re_set_syntax(regex_options
);
1433 re
->syntax
= regex_options
;
1434 re
->translate
= NULL
;
1436 error_message
= re_compile_pattern (argv
[*arg_ptr
], strlen (argv
[*arg_ptr
]),
1439 error (1, 0, "%s", error_message
);
1445 parse_size (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1447 struct predicate
*our_pred
;
1449 enum comparison_type c_type
;
1453 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1455 len
= strlen (argv
[*arg_ptr
]);
1457 error (1, 0, _("invalid null argument to -size"));
1458 switch (argv
[*arg_ptr
][len
- 1])
1462 argv
[*arg_ptr
][len
- 1] = '\0';
1467 argv
[*arg_ptr
][len
- 1] = '\0';
1472 argv
[*arg_ptr
][len
- 1] = '\0';
1475 case 'M': /* Megabytes */
1476 blksize
= 1024*1024;
1477 argv
[*arg_ptr
][len
- 1] = '\0';
1480 case 'G': /* Gigabytes */
1481 blksize
= 1024*1024*1024;
1482 argv
[*arg_ptr
][len
- 1] = '\0';
1487 argv
[*arg_ptr
][len
- 1] = '\0';
1503 error (1, 0, _("invalid -size type `%c'"), argv
[*arg_ptr
][len
- 1]);
1505 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
1507 our_pred
= insert_primary (entry
);
1508 our_pred
->args
.size
.kind
= c_type
;
1509 our_pred
->args
.size
.blocksize
= blksize
;
1510 our_pred
->args
.size
.size
= num
;
1517 parse_samefile (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1519 struct predicate
*our_pred
;
1522 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1524 if ((*options
.xstat
) (argv
[*arg_ptr
], &st
))
1525 error (1, errno
, "%s", argv
[*arg_ptr
]);
1527 our_pred
= insert_primary (entry
);
1528 our_pred
->args
.fileid
.ino
= st
.st_ino
;
1529 our_pred
->args
.fileid
.dev
= st
.st_dev
;
1530 our_pred
->need_type
= false;
1531 our_pred
->need_stat
= true;
1538 parse_true (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1540 struct predicate
*our_pred
;
1545 our_pred
= insert_primary (entry
);
1546 our_pred
->need_stat
= our_pred
->need_type
= false;
1551 parse_type (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1553 return insert_type (argv
, arg_ptr
, entry
, pred_type
);
1557 parse_uid (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1559 return insert_num (argv
, arg_ptr
, entry
);
1563 parse_used (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1565 struct predicate
*our_pred
;
1567 enum comparison_type c_type
;
1570 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1572 if (!get_num (argv
[*arg_ptr
], &num_days
, &c_type
))
1574 t
= num_days
* DAYSECS
;
1575 our_pred
= insert_primary (entry
);
1576 our_pred
->args
.info
.kind
= c_type
;
1577 our_pred
->args
.info
.negative
= t
< 0;
1578 our_pred
->args
.info
.l_val
= t
;
1584 parse_user (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1586 struct passwd
*cur_pwd
;
1587 struct predicate
*our_pred
;
1591 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
1593 cur_pwd
= getpwnam (argv
[*arg_ptr
]);
1595 if (cur_pwd
!= NULL
)
1596 uid
= cur_pwd
->pw_uid
;
1599 uid_len
= strspn (argv
[*arg_ptr
], "0123456789");
1600 if ((uid_len
== 0) || (argv
[*arg_ptr
][uid_len
] != '\0'))
1602 uid
= atoi (argv
[*arg_ptr
]);
1604 our_pred
= insert_primary (entry
);
1605 our_pred
->args
.uid
= uid
;
1611 parse_version (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1613 extern char *version_string
;
1621 printf (_("GNU find version %s\n"), version_string
);
1622 printf (_("Features enabled: "));
1625 printf("CACHE_IDS ");
1633 printf("DEBUG_STAT ");
1636 #if defined(USE_STRUCT_DIRENT_D_TYPE) && defined(HAVE_STRUCT_DIRENT_D_TYPE)
1640 #if defined(O_NOFOLLOW)
1641 printf("O_NOFOLLOW(%s) ",
1642 (options
.open_nofollow_available
? "enabled" : "disabled"));
1648 /* For the moment, leave this as English in case someone wants
1649 to parse these strings. */
1658 parse_xdev (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1663 options
.stay_on_filesystem
= true;
1668 parse_ignore_race (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1673 options
.ignore_readdir_race
= true;
1678 parse_noignore_race (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1683 options
.ignore_readdir_race
= false;
1688 parse_warn (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1693 options
.warnings
= true;
1698 parse_xtype (const struct parser_table
* entry
, char **argv
, int *arg_ptr
)
1702 return insert_type (argv
, arg_ptr
, entry
, pred_xtype
);
1706 insert_type (char **argv
, int *arg_ptr
, const struct parser_table
*entry
, PRED_FUNC which_pred
)
1709 struct predicate
*our_pred
;
1711 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
)
1712 || (strlen (argv
[*arg_ptr
]) != 1))
1714 switch (argv
[*arg_ptr
][0])
1716 case 'b': /* block special */
1717 type_cell
= S_IFBLK
;
1719 case 'c': /* character special */
1720 type_cell
= S_IFCHR
;
1722 case 'd': /* directory */
1723 type_cell
= S_IFDIR
;
1725 case 'f': /* regular file */
1726 type_cell
= S_IFREG
;
1729 case 'l': /* symbolic link */
1730 type_cell
= S_IFLNK
;
1734 case 'p': /* pipe */
1735 type_cell
= S_IFIFO
;
1739 case 's': /* socket */
1740 type_cell
= S_IFSOCK
;
1744 case 'D': /* Solaris door */
1745 type_cell
= S_IFDOOR
;
1748 default: /* None of the above ... nuke 'em. */
1751 our_pred
= insert_primary_withpred (entry
, which_pred
);
1753 /* Figure out if we will need to stat the file, because if we don't
1754 * need to follow symlinks, we can avoid a stat call by using
1755 * struct dirent.d_type.
1757 if (which_pred
== pred_xtype
)
1759 our_pred
->need_stat
= true;
1760 our_pred
->need_type
= false;
1764 our_pred
->need_stat
= false; /* struct dirent is enough */
1765 our_pred
->need_type
= true;
1767 our_pred
->args
.type
= type_cell
;
1768 (*arg_ptr
)++; /* Move on to next argument. */
1773 /* Return true if the file accessed via FP is a terminal.
1776 stream_is_tty(FILE *fp
)
1778 int fd
= fileno(fp
);
1781 return false; /* not a valid stream */
1785 return isatty(fd
) ? true : false;
1792 /* If true, we've determined that the current fprintf predicate
1793 uses stat information. */
1794 static boolean fprintf_stat_needed
;
1796 /* XXX: do we need to pass FUNC to this function? */
1798 insert_fprintf (FILE *fp
, const struct parser_table
*entry
, PRED_FUNC func
, char **argv
, int *arg_ptr
)
1800 char *format
; /* Beginning of unprocessed format string. */
1801 register char *scan
; /* Current address in scanning `format'. */
1802 register char *scan2
; /* Address inside of element being scanned. */
1803 struct segment
**segmentp
; /* Address of current segment. */
1804 struct predicate
*our_pred
;
1806 format
= argv
[(*arg_ptr
)++];
1808 fprintf_stat_needed
= false; /* Might be overridden later. */
1809 our_pred
= insert_primary_withpred (entry
, func
);
1810 our_pred
->side_effects
= our_pred
->no_default_print
= true;
1811 our_pred
->args
.printf_vec
.stream
= fp
;
1812 our_pred
->args
.printf_vec
.dest_is_tty
= stream_is_tty(fp
);
1813 our_pred
->args
.printf_vec
.quote_opts
= clone_quoting_options (NULL
);
1814 segmentp
= &our_pred
->args
.printf_vec
.segment
;
1817 for (scan
= format
; *scan
; scan
++)
1822 if (*scan2
>= '0' && *scan2
<= '7')
1826 for (i
= n
= 0; i
< 3 && (*scan2
>= '0' && *scan2
<= '7');
1828 n
= 8 * n
+ *scan2
- '0';
1843 make_segment (segmentp
, format
, scan
- format
, KIND_STOP
);
1844 our_pred
->need_stat
= fprintf_stat_needed
;
1862 /* *scan = '\\'; * it already is */
1866 _("warning: unrecognized escape `\\%c'"), *scan2
);
1871 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1873 format
= scan2
+ 1; /* Move past the escape. */
1874 scan
= scan2
; /* Incremented immediately by `for'. */
1876 else if (*scan
== '%')
1880 segmentp
= make_segment (segmentp
, format
, scan
- format
+ 1,
1886 /* Scan past flags, width and precision, to verify kind. */
1887 for (scan2
= scan
; *++scan2
&& strchr ("-+ #", *scan2
);)
1889 while (ISDIGIT (*scan2
))
1892 for (scan2
++; ISDIGIT (*scan2
); scan2
++)
1894 if (strchr ("abcdDfFgGhHiklmMnpPstuUyY", *scan2
))
1896 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1901 else if (strchr ("ACT", *scan2
) && scan2
[1])
1903 segmentp
= make_segment (segmentp
, format
, scan2
- format
,
1904 *scan2
| (scan2
[1] << 8));
1911 /* An unrecognized % escape. Print the char after the %. */
1912 error (0, 0, _("warning: unrecognized format directive `%%%c'"),
1914 segmentp
= make_segment (segmentp
, format
, scan
- format
,
1923 make_segment (segmentp
, format
, scan
- format
, KIND_PLAIN
);
1924 our_pred
->need_type
= false;
1925 our_pred
->need_stat
= fprintf_stat_needed
;
1929 /* Create a new fprintf segment in *SEGMENT, with type KIND,
1930 from the text in FORMAT, which has length LEN.
1931 Return the address of the `next' pointer of the new segment. */
1933 static struct segment
**
1934 make_segment (struct segment
**segment
, char *format
, int len
, int kind
)
1938 *segment
= (struct segment
*) xmalloc (sizeof (struct segment
));
1940 (*segment
)->kind
= kind
;
1941 (*segment
)->next
= NULL
;
1942 (*segment
)->text_len
= len
;
1944 fmt
= (*segment
)->text
= xmalloc (len
+ sizeof "d");
1945 strncpy (fmt
, format
, len
);
1948 switch (kind
& 0xff)
1950 case KIND_PLAIN
: /* Plain text string, no % conversion. */
1951 case KIND_STOP
: /* Terminate argument, no newline. */
1954 case 'a': /* atime in `ctime' format */
1955 case 'A': /* atime in user-specified strftime format */
1956 case 'c': /* ctime in `ctime' format */
1957 case 'C': /* ctime in user-specified strftime format */
1958 case 'F': /* filesystem type */
1959 case 'g': /* group name */
1960 case 'i': /* inode number */
1961 case 'l': /* object of symlink */
1962 case 'M': /* mode in `ls -l' format (eg., "drwxr-xr-x") */
1963 case 's': /* size in bytes */
1964 case 't': /* mtime in `ctime' format */
1965 case 'T': /* mtime in user-specified strftime format */
1966 case 'u': /* user name */
1967 case 'y': /* file type */
1968 case 'Y': /* symlink pointed file type */
1969 fprintf_stat_needed
= true;
1971 case 'f': /* basename of path */
1972 case 'h': /* leading directories part of path */
1973 case 'H': /* ARGV element file was found under */
1974 case 'p': /* pathname */
1975 case 'P': /* pathname with ARGV element stripped */
1979 /* Numeric items that one might expect to honour
1980 * #, 0, + flags but which do not.
1982 case 'G': /* GID number */
1983 case 'U': /* UID number */
1984 case 'b': /* size in 512-byte blocks */
1985 case 'D': /* Filesystem device on which the file exits */
1986 case 'k': /* size in 1K blocks */
1987 case 'n': /* number of links */
1988 fprintf_stat_needed
= true;
1992 /* Numeric items that DO honour #, 0, + flags.
1994 case 'd': /* depth in search tree (0 = ARGV element) */
1998 case 'm': /* mode as octal number (perms only) */
2000 fprintf_stat_needed
= true;
2005 return &(*segment
)->next
;
2009 check_path_safety(const char *action
)
2011 const char *path
= getenv("PATH");
2013 s
= next_element(path
, 1);
2014 while ((s
= next_element ((char *) NULL
, 1)) != NULL
)
2016 if (0 == strcmp(s
, "."))
2018 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)"),
2025 /* handles both exec and ok predicate */
2026 #if defined(NEW_EXEC)
2027 /* handles both exec and ok predicate */
2029 new_insert_exec_ok (const char *action
,
2030 const struct parser_table
*entry
,
2034 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2035 int i
; /* Index into cmd args */
2036 int saw_braces
; /* True if previous arg was '{}'. */
2037 boolean allow_plus
; /* True if + is a valid terminator */
2038 int brace_count
; /* Number of instances of {}. */
2039 PRED_FUNC func
= entry
->pred_func
;
2041 struct predicate
*our_pred
;
2042 struct exec_val
*execp
; /* Pointer for efficiency. */
2044 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2047 our_pred
= insert_primary_withpred (entry
, func
);
2048 our_pred
->side_effects
= our_pred
->no_default_print
= true;
2049 execp
= &our_pred
->args
.exec_vec
;
2051 if ((func
!= pred_okdir
) && (func
!= pred_ok
))
2056 if ((func
== pred_execdir
) || (func
== pred_okdir
))
2058 options
.ignore_readdir_race
= false;
2059 check_path_safety(action
);
2060 execp
->use_current_dir
= true;
2064 execp
->use_current_dir
= false;
2067 our_pred
->args
.exec_vec
.multiple
= 0;
2069 /* Count the number of args with path replacements, up until the ';'.
2070 * Also figure out if the command is terminated by ";" or by "+".
2073 for (end
= start
, saw_braces
=0, brace_count
=0;
2075 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2078 /* For -exec and -execdir, "{} +" can terminate the command. */
2080 && argv
[end
][0] == '+' && argv
[end
][1] == 0
2083 our_pred
->args
.exec_vec
.multiple
= 1;
2088 if (strstr (argv
[end
], "{}"))
2093 if (0 == end
&& (func
== pred_execdir
|| func
== pred_okdir
))
2095 /* The POSIX standard says that {} replacement should
2096 * occur even in the utility name. This is insecure
2097 * since it means we will be executing a command whose
2098 * name is chosen according to whatever find finds in
2099 * the filesystem. That can be influenced by an
2100 * attacker. Hence for -execdir and -okdir this is not
2101 * allowed. We can specify this as those options are
2102 * not defined by POSIX.
2104 error(1, 0, _("You may not use {} within the utility name for -execdir and -okdir, because this is a potential security problem."));
2109 /* Fail if no command given or no semicolon found. */
2110 if ((end
== start
) || (argv
[end
] == NULL
))
2117 if (our_pred
->args
.exec_vec
.multiple
&& brace_count
> 1)
2121 if (func
== pred_execdir
)
2127 _("Only one instance of {} is supported with -exec%s ... +"),
2131 /* execp->ctl = xmalloc(sizeof struct buildcmd_control); */
2132 bc_init_controlinfo(&execp
->ctl
);
2133 execp
->ctl
.exec_callback
= launch
;
2135 if (our_pred
->args
.exec_vec
.multiple
)
2137 /* "+" terminator, so we can just append our arguments after the
2138 * command and initial arguments.
2140 execp
->replace_vec
= NULL
;
2141 execp
->ctl
.replace_pat
= NULL
;
2142 execp
->ctl
.rplen
= 0;
2143 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2144 execp
->ctl
.args_per_exec
= 0; /* no limit */
2146 /* remember how many arguments there are */
2147 execp
->ctl
.initial_argc
= (end
-start
) - 1;
2149 /* execp->state = xmalloc(sizeof struct buildcmd_state); */
2150 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2152 /* Gather the initial arguments. Skip the {}. */
2153 for (i
=start
; i
<end
-1; ++i
)
2155 bc_push_arg(&execp
->ctl
, &execp
->state
,
2156 argv
[i
], strlen(argv
[i
])+1,
2163 /* Semicolon terminator - more than one {} is supported, so we
2164 * have to do brace-replacement.
2166 execp
->num_args
= end
- start
;
2168 execp
->ctl
.replace_pat
= "{}";
2169 execp
->ctl
.rplen
= strlen(execp
->ctl
.replace_pat
);
2170 execp
->ctl
.lines_per_exec
= 0; /* no limit */
2171 execp
->ctl
.args_per_exec
= 0; /* no limit */
2172 execp
->replace_vec
= xmalloc(sizeof(char*)*execp
->num_args
);
2175 /* execp->state = xmalloc(sizeof(*(execp->state))); */
2176 bc_init_state(&execp
->ctl
, &execp
->state
, execp
);
2178 /* Remember the (pre-replacement) arguments for later. */
2179 for (i
=0; i
<execp
->num_args
; ++i
)
2181 execp
->replace_vec
[i
] = argv
[i
+start
];
2185 if (argv
[end
] == NULL
)
2193 /* handles both exec and ok predicate */
2195 old_insert_exec_ok (boolean (*func
) (/* ??? */), char **argv
, int *arg_ptr
)
2197 int start
, end
; /* Indexes in ARGV of start & end of cmd. */
2198 int num_paths
; /* Number of args with path replacements. */
2199 int path_pos
; /* Index in array of path replacements. */
2200 int vec_pos
; /* Index in array of args. */
2201 struct predicate
*our_pred
;
2202 struct exec_val
*execp
; /* Pointer for efficiency. */
2204 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2207 /* Count the number of args with path replacements, up until the ';'. */
2209 for (end
= start
, num_paths
= 0;
2211 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2213 if (strstr (argv
[end
], "{}"))
2215 /* Fail if no command given or no semicolon found. */
2216 if ((end
== start
) || (argv
[end
] == NULL
))
2222 our_pred
= insert_primary (func
);
2223 our_pred
->side_effects
= our_pred
->no_default_print
= true;
2224 execp
= &our_pred
->args
.exec_vec
;
2225 execp
->usercontext
= our_pred
;
2226 execp
->use_current_dir
= false;
2228 (struct path_arg
*) xmalloc (sizeof (struct path_arg
) * (num_paths
+ 1));
2229 execp
->vec
= (char **) xmalloc (sizeof (char *) * (end
- start
+ 1));
2230 /* Record the positions of all args, and the args with path replacements. */
2231 for (end
= start
, path_pos
= vec_pos
= 0;
2233 && ((argv
[end
][0] != ';') || (argv
[end
][1] != '\0'));
2238 execp
->paths
[path_pos
].count
= 0;
2239 for (p
= argv
[end
]; *p
; ++p
)
2240 if (p
[0] == '{' && p
[1] == '}')
2242 execp
->paths
[path_pos
].count
++;
2245 if (execp
->paths
[path_pos
].count
)
2247 execp
->paths
[path_pos
].offset
= vec_pos
;
2248 execp
->paths
[path_pos
].origarg
= argv
[end
];
2251 execp
->vec
[vec_pos
++] = argv
[end
];
2253 execp
->paths
[path_pos
].offset
= -1;
2254 execp
->vec
[vec_pos
] = NULL
;
2256 if (argv
[end
] == NULL
)
2267 insert_exec_ok (const char *action
, const struct parser_table
*entry
, char **argv
, int *arg_ptr
)
2269 #if defined(NEW_EXEC)
2270 return new_insert_exec_ok(action
, entry
, argv
, arg_ptr
);
2272 return old_insert_exec_ok(func
, argv
, arg_ptr
);
2278 /* Get a number of days and comparison type.
2279 STR is the ASCII representation.
2280 Set *NUM_DAYS to the number of days, taken as being from
2281 the current moment (or possibly midnight). Thus the sense of the
2282 comparison type appears to be reversed.
2283 Set *COMP_TYPE to the kind of comparison that is requested.
2285 Return true if all okay, false if input error.
2287 Used by -atime, -ctime and -mtime (parsers) to
2288 get the appropriate information for a time predicate processor. */
2291 get_num_days (char *str
, uintmax_t *num_days
, enum comparison_type
*comp_type
)
2293 boolean r
= get_num (str
, num_days
, comp_type
);
2297 case COMP_LT
: *comp_type
= COMP_GT
; break;
2298 case COMP_GT
: *comp_type
= COMP_LT
; break;
2304 /* Insert a time predicate PRED.
2305 ARGV is a pointer to the argument array.
2306 ARG_PTR is a pointer to an index into the array, incremented if
2309 Return true if input is valid, false if not.
2311 A new predicate node is assigned, along with an argument node
2312 obtained with malloc.
2314 Used by -atime, -ctime, and -mtime parsers. */
2317 insert_time (char **argv
, int *arg_ptr
, const struct parser_table
* entry
, PRED_FUNC pred
)
2319 struct predicate
*our_pred
;
2321 enum comparison_type c_type
;
2324 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2326 if (!get_num_days (argv
[*arg_ptr
], &num_days
, &c_type
))
2329 /* Figure out the timestamp value we are looking for. */
2330 t
= ( options
.cur_day_start
- num_days
* DAYSECS
2331 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2335 /* We introduce a scope in which 'val' can be declared, for the
2336 * benefit of compilers that are really C89 compilers
2337 * which support intmax_t because config.h #defines it
2339 intmax_t val
= ( (intmax_t)options
.cur_day_start
- num_days
* DAYSECS
2340 + ((c_type
== COMP_GT
) ? DAYSECS
- 1 : 0));
2343 /* Check for possibility of an overflow */
2344 if ( (intmax_t)t
!= val
)
2346 error (1, 0, "arithmetic overflow while converting %s days to a number of seconds", argv
[*arg_ptr
]);
2350 our_pred
= insert_primary_withpred (entry
, pred
);
2351 our_pred
->args
.info
.kind
= c_type
;
2352 our_pred
->args
.info
.negative
= t
< 0;
2353 our_pred
->args
.info
.l_val
= t
;
2356 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2357 fprintf (stderr
, " type: %s %s ",
2358 (c_type
== COMP_GT
) ? "gt" :
2359 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2360 (c_type
== COMP_GT
) ? " >" :
2361 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? ">=" : " ?")));
2362 t
= our_pred
->args
.info
.l_val
;
2363 fprintf (stderr
, "%ju %s", (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2364 if (c_type
== COMP_EQ
)
2366 t
= our_pred
->args
.info
.l_val
+= DAYSECS
;
2367 fprintf (stderr
, " < %ju %s",
2368 (uintmax_t) our_pred
->args
.info
.l_val
, ctime (&t
));
2369 our_pred
->args
.info
.l_val
-= DAYSECS
;
2375 /* Get a number with comparison information.
2376 The sense of the comparison information is 'normal'; that is,
2377 '+' looks for a count > than the number and '-' less than.
2379 STR is the ASCII representation of the number.
2380 Set *NUM to the number.
2381 Set *COMP_TYPE to the kind of comparison that is requested.
2383 Return true if all okay, false if input error. */
2386 get_num (char *str
, uintmax_t *num
, enum comparison_type
*comp_type
)
2393 *comp_type
= COMP_GT
;
2397 *comp_type
= COMP_LT
;
2401 *comp_type
= COMP_EQ
;
2405 return xstrtoumax (str
, NULL
, 10, num
, "") == LONGINT_OK
;
2408 /* Insert a number predicate.
2409 ARGV is a pointer to the argument array.
2410 *ARG_PTR is an index into ARGV, incremented if all went well.
2411 *PRED is the predicate processor to insert.
2413 Return true if input is valid, false if error.
2415 A new predicate node is assigned, along with an argument node
2416 obtained with malloc.
2418 Used by -inum and -links parsers. */
2421 insert_num (char **argv
, int *arg_ptr
, const struct parser_table
*entry
)
2423 struct predicate
*our_pred
;
2425 enum comparison_type c_type
;
2427 if ((argv
== NULL
) || (argv
[*arg_ptr
] == NULL
))
2429 if (!get_num (argv
[*arg_ptr
], &num
, &c_type
))
2431 our_pred
= insert_primary (entry
);
2432 our_pred
->args
.info
.kind
= c_type
;
2433 our_pred
->args
.info
.l_val
= num
;
2436 fprintf (stderr
, "inserting %s\n", our_pred
->p_name
);
2437 fprintf (stderr
, " type: %s %s ",
2438 (c_type
== COMP_GT
) ? "gt" :
2439 ((c_type
== COMP_LT
) ? "lt" : ((c_type
== COMP_EQ
) ? "eq" : "?")),
2440 (c_type
== COMP_GT
) ? " >" :
2441 ((c_type
== COMP_LT
) ? " <" : ((c_type
== COMP_EQ
) ? " =" : " ?")));
2442 fprintf (stderr
, "%ju\n", our_pred
->args
.info
.l_val
);
2448 open_output_file (char *path
)
2452 if (!strcmp (path
, "/dev/stderr"))
2454 else if (!strcmp (path
, "/dev/stdout"))
2456 f
= fopen_safer (path
, "w");
2458 error (1, errno
, "%s", path
);